Socket 打洞

Client:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Threading;
using System.Net;
using System.Net.Sockets;

namespace TestClient
{
    public partial class Chat : Form
    {
        //以下是客户端到服务器端的消息开头
        const string LOGININ  = "10"   ;//请求登陆的消息|||消息形式:10+自己的用户名
        const string LOGINOUT  = "11"  ;//请求登出的消息|||消息形式:11+自己的用户名
        const string GETULIST  = "12"  ;//请求获得在线用户列表|||消息形式:12+自己的用户名
        const string P2PCONN  = "13"   ;//请求P2P连接的消息|||消息形式:13+自己的用户名+对方的用户名
        const string HOLDLINE = "14";//保持连接.|||消息开式:14+自己的用户名
        const string LOGINADD = "15";//新用户登录
        const string LOGINREMOVE = "16";//用户登出
        //以下是服务器到客户端的消息开头
        const string HVUSER = "20";//用户名已存在
        const string GETUSER = "21";//在线用户列表|||消息格式:21+用户名+EP
        const string MAKHOLD = "22";//打洞命令|||消息格式:22+IP
        const string LOGINOK = "23";//登陆成功
        const string SERVCLS = "24";//服务器关闭
        const string MSGEND = "25";//'消息结束  
        const string CHATMSGALL = "26";//发送给所有人
        //以下是客户端到客户端的消息开头
        const string HOLDOK = "30"    ;//打洞成功
        const string CHATMSG = "31"   ;//聊天消息
        const string CHTMSGEND = "32" ;//聊天消息发送成功

        private string userName;
        public string UserName
        {
            private get { return userName; }
            set { userName = value; }
        }
        private IPEndPoint serverEP;
        public IPEndPoint ServerEP
        {
            set
            {
                serverEP = value;
            }
            private get
            {
                return serverEP;
            }
        }

        Socket clientSocket;
        public Socket ClientSocket
        {
            private get { return clientSocket; }
            set { clientSocket = value; }
        }

        int getUrecCount;


        //和服务器保持连接连接时用到的byte数组
        byte[] holdBytes;
        List<string> OLUserName;
        List<IPEndPoint> OLUserEP;
        //为保持在线状态弄得
        TimerCallback timerDelegate;
        bool msgSendEnd = false;//消息是否发送成功,若发送成功,则会返回结束消息
        private ManualResetEvent getUDone;//用来阻塞请求好友名单的线程,等待接收好友名单
        private ManualResetEvent chatDone;//用来阻塞发送聊天消息时的线程        
        private ManualResetEvent holdDone;//用来阻塞打洞时的线程        
        private ManualResetEvent sendDone ;//用来阴塞发送消息的线程.等待收到回送的确认消息

        //监听的线程
        Thread thListen;        
        delegate void myMethodDelegate(ref byte[] myInData);//登陆时用的事件
        delegate void TextChangedMelegate(string text);//给textbox1赋值用到的委托
        delegate void ComboboxBindDelegate();//用户绑定combobox的委托

        static bool testHold = false;
        static bool testChat = false;
        //用户列表的数据源
        DataTable dt;
        //上一条消息
        string lastMsg = "";

        public Chat()
        {
            InitializeComponent();
        }

        private void Chat_Load(object sender, EventArgs e)
        {
            Login login = new Login();
            login.StartPosition = FormStartPosition.CenterParent;
            login.Tag = this;
            login.ShowDialog();   
            thListen = new Thread(new ThreadStart(this.Listen));
            thListen.IsBackground = true;
            thListen.Start();
        }

        public void LoadInit()
        {            
            //timerDelegate = new TimerCallback(Holdonline);
            //holdBytes = Encoding.Unicode.GetBytes(HOLDLINE + UserName);
            ////登陆成功后.用一个timer,每隔50秒向服务器发送消息,保持在线状态跟在主机注册的端口
            //System.Threading.Timer timer = new System.Threading.Timer(timerDelegate, null, 10000, 10000);
            //登陆成功后.用一个timer,每隔50秒向服务器发送消息,保持在线状态跟在主机注册的端口
            System.Windows.Forms.Timer timer = new System.Windows.Forms.Timer();
            timer.Tick += new EventHandler(Holdonline);
            timer.Interval = 50000;
            timer.Start();

            //请求在线名单
            this.Text = "正在获取在线名单,请稍后....";
            bool getUbool = false;
            //while (getUbool == false)
            //{
            //    //getUbool = GetU();
            //    //if (getUbool == false)
            //    //{
            //    //    if (MessageBox.Show("是否重试?", "是否重试", MessageBoxButtons.YesNo) == DialogResult.Yes)
            //    //    {
            //    //        getUbool = false;
            //    //    }
            //    //    else
            //    //    {
            //    //        getUbool = true;
            //    //    }
            //    //}
            //}
        }

        private bool GetU()
        {
            getUDone = new ManualResetEvent(false);
            byte[] getUbytes = Encoding.Unicode.GetBytes(GETULIST);
            ClientSocket.SendTo(getUbytes, ServerEP);
            byte[] data = new byte[4056];
            string comStr = Encoding.Unicode.GetString(data, 0, 4);
            myMethodDelegate GUrecv = new myMethodDelegate(RecvGetU);
            GUrecv.BeginInvoke(ref data, null, null);
            getUDone.WaitOne(30000, true);
            string recvStr = Encoding.Unicode.GetString(data, 0, 4);
            if (recvStr == comStr)
            {
                MessageBox.Show("服务器超时.或取好友名单失败!!");
                return false;
            }
            string test = Encoding.Unicode.GetString(data);
            if (Encoding.Unicode.GetString(data, 0, 4) == GETUSER)
            {
                GetUserList(data, getUrecCount);
                this.comboBox1.Invoke(new ComboboxBindDelegate(ShowUserList));
                //ShowUserList();
                return true;
            }
            else
            {
                MessageBox.Show("服务器未知错误,获取在线名单失败!!");
                return false;
            }
        }

        /// <summary>
        /// 显示在线用户
        /// </summary>
        private void ShowUserList()
        {
            lock (OLUserName)
            {
                dt = new DataTable();
                dt.Columns.Add("Name");
                dt.Columns.Add("IP");
                DataRow dr1 = dt.NewRow();
                dr1["Name"] = "所有人";
                dt.Rows.Add(dr1);
                this.comboBox1.DataSource = dt;
                this.comboBox1.ValueMember = "IP";
                this.comboBox1.DisplayMember = "Name";
                for (int i = 0; i < OLUserName.Count; i++)
                {
                    if (OLUserName[i] != "")
                    {
                        DataRow dr = dt.NewRow();
                        dr["Name"] = OLUserName[i];
                        dr["IP"] = OLUserEP[i];
                        dt.Rows.Add(dr);
                    }
                }
            }
        }

        /// <summary>
        /// 处理收到的在线用户信息
        /// </summary>
        /// <param name="userInfobytes"></param>
        /// <param name="reccount"></param>
        private void GetUserList(byte[] userInfobytes, int reccount)
        {
            string ustr = Encoding.Unicode.GetString(userInfobytes, 4, reccount - 4);

            string[] splitStr = null;
            splitStr = ustr.Split('|');
            string[] IPEPSplit = null;
            OLUserName = new List<string>();
            OLUserEP = new List<IPEndPoint>();
            int i = 0;
            for(int k=0;k<splitStr.Length -2;k = k+2)
            {
                OLUserName.Add(splitStr[k]);
                IPEPSplit = splitStr[k + 1].Split(':');
                OLUserEP.Add(new IPEndPoint(IPAddress.Parse(IPEPSplit[0]), Convert.ToInt32(IPEPSplit[1])));
                IPEPSplit = null;
                i++;
            }
        }

        //用保持在线状态的函数
        private void Holdonline(object state,EventArgs e)
        {
            holdBytes = Encoding.Unicode.GetBytes(HOLDLINE + UserName);
            ClientSocket.SendTo(holdBytes, ServerEP);
        }

        //登陆时用来异步的接收服务器发送的消息
        void RecvGetU(ref byte[] inData)
        {
            getUrecCount = ClientSocket.Receive(inData);
            getUDone.Set();
        }

        /// <summary>
        /// 发送
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void buttonSend_Click(object sender, EventArgs e)
        {
            if (this.textBox2.Text.Trim() == "")
                return;
            else
            {
                SendChatMsg(this.comboBox1.Text, this.textBox2.Text);
            }
        }

        /// <summary>
        /// 发送聊天消息
        /// </summary>
        /// <param name="remoteUser"></param>
        /// <param name="chatMsgStr"></param>
        private void SendChatMsg(string remoteUser, string chatMsgStr)
        {
            if (lastMsg.Trim() == chatMsgStr.Trim())
            {
                MessageBox.Show("看你傻样,还想刷屏!");
                return;
            }
            else
                lastMsg = chatMsgStr;
            IPEndPoint remoteUEP = null;
            if (remoteUser == "所有人")
            {
                for(int i=0;i<OLUserName.Count;i++)
                {
                    if (OLUserName[i].ToUpper().Trim() == this.UserName.ToUpper().Trim())
                        continue;
                    byte[] msgbytes = Encoding.Unicode.GetBytes(CHATMSGALL + UserName + "|" + chatMsgStr);                    
                    string test = OLUserEP[i].Address.ToString();
                    string port = OLUserEP[i].Port.ToString();
                    ClientSocket.SendTo(msgbytes, OLUserEP[i]);                    
                }
                this.textbox.AppendText(this.UserName + " to " + this.comboBox1.Text + ":" + chatMsgStr + "\n");
                this.textbox.Focus();
                Application.DoEvents();
                this.textBox2.Focus();
            }
            else
            {
                for (int i = 0; i < OLUserName.Count; i++)
                {
                    if (remoteUser == OLUserName[i])
                        remoteUEP = OLUserEP[i];
                }
                byte[] msgbytes = Encoding.Unicode.GetBytes(CHATMSG + userName + "|" + chatMsgStr);
                byte[] holdbytes = Encoding.Unicode.GetBytes(P2PCONN + userName + "|" + remoteUser);
                chatDone = new ManualResetEvent(false);
                string test = remoteUEP.Address.ToString();
                string port = remoteUEP.Port.ToString();
                ClientSocket.SendTo(msgbytes, remoteUEP);
                chatDone.WaitOne(1000, true);
                this.textbox.AppendText(this.UserName + " to " + this.comboBox1.Text + ":" + chatMsgStr + "\n");
                //this.textbox.Focus();
                Application.DoEvents();
                //this.textBox2.Focus();

                if (testChat)
                {
                    testChat = false;
                    return;
                }
                testHold = false;
                while (testHold == false)
                {
                    //开始打洞
                    holdDone = new ManualResetEvent(false);
                    ClientSocket.SendTo(holdbytes, remoteUEP);
                    ClientSocket.SendTo(holdbytes, ServerEP);
                    holdDone.WaitOne(1000, true);
                    if (testHold == false)
                    {
                        //打洞超时
                        testHold = true;
                    }
                    else
                    {
                        //打洞成功                    
                    }
                }
                while (testChat == false)
                {
                    //打洞成功准备发送
                    chatDone = new ManualResetEvent(false);

                    ClientSocket.SendTo(msgbytes, remoteUEP);
                    chatDone.WaitOne(1000, true);
                    if (testChat == false)
                    {
                        //发送超时
                        testChat = true;
                    }
                    else
                    {
                        //发送成功
                        testChat = true;
                    }
                }
            }
            this.textBox2.Text = "";
            testHold = false;
            testChat = false;
        }       

        /// <summary>
        /// 客户程序监听的函数
        /// </summary>
        private void Listen()
        {
            while (true)
            {
                try
                {
                    int recv = 0;//收到的字节数
                    byte[] data = new byte[1024];//缓冲区大小
                    IPEndPoint sender = new IPEndPoint(IPAddress.Any, 0);
                    EndPoint tempRemoteEP = (EndPoint)sender;
                    
                    recv = ClientSocket.ReceiveFrom(data, ref tempRemoteEP);
                    //获得消息头的内容
                    string msgHead = Encoding.Unicode.GetString(data, 0, 4);
                    switch (msgHead)
                    {                           
                        case MSGEND:
                            msgSendEnd = true;
                            sendDone.Set();
                            break;
                        case LOGININ:
                            AddOnLine(data, recv);
                            break;
                        case LOGINOUT:
                            RemoveOnLine(data, recv);
                            break;
                        case MAKHOLD:
                            MakeHold(data, recv);
                            break;
                        case CHATMSG:
                            ShowChatMsg(data, recv);
                            break;
                        case CHATMSGALL:
                            ShowChatMsgAll(data, recv);
                            break;
                        case HOLDOK:
                            testHold = true;
                            holdDone.Set();
                            break;
                        case CHTMSGEND:
                            testChat = true;
                            chatDone.Set();
                            break;
                        case LOGINADD:
                            this.GetU();
                            break;
                        case LOGINREMOVE:
                            this.GetU();
                            break;
                    }
                }
                catch
                { }
            }
        }

        /// <summary>
        /// 处理聊天消息
        /// </summary>
        /// <param name="data"></param>
        /// <param name="recv"></param>
        private void ShowChatMsgAll(byte[] data, int recv)
        {
            string msgStr = Encoding.Unicode.GetString(data, 4, recv - 4);
            string[] splitStr = msgStr.Split('|');
            string fromUname = splitStr[0];
            string msg = splitStr[1];
            this.textbox.Invoke(new TextChangedMelegate(SetTextBox), fromUname + " to 所有人:" + msg);            
        }

        /// <summary>
        /// 处理聊天消息
        /// </summary>
        /// <param name="data"></param>
        /// <param name="recv"></param>
        private void ShowChatMsg(byte[] data, int recv)
        {
            string msgStr = Encoding.Unicode.GetString(data, 4, recv-4);
            string[] splitStr = msgStr.Split('|');
            string fromUname = splitStr[0];
            string msg = splitStr[1];
            this.textbox.Invoke(new TextChangedMelegate(SetTextBox), fromUname + " to " + userName + ":" + msg);
            //this.textBox1.Text += fromUname + " to " + userName + ":" + msg + "\n";
            byte[] tempbytes = Encoding.Unicode.GetBytes(CHTMSGEND);
            for (int i = 0; i < OLUserName.Count; i++)
            {
                if (OLUserName[i].ToUpper() == fromUname.ToUpper())
                {
                    ClientSocket.SendTo(tempbytes, OLUserEP[i]);
                }
            }
        }

        private void SetTextBox(string text)
        {
            this.textbox.SelectionColor = Color.Blue;
            this.textbox.AppendText(text + "\n");
            //this.textbox.Focus();
            Application.DoEvents();
            //this.textBox2.Focus();
        }

        /// <summary>
        /// 处理打洞函数
        /// </summary>
        /// <param name="data"></param>
        /// <param name="recv"></param>
        private void MakeHold(byte[] indata, int recvcount)
        {
            string makholdstr = Encoding.Unicode.GetString(indata, 4, recvcount);
            string[] ipepstr = makholdstr.Split(':');
            IPEndPoint holdEP = new IPEndPoint(IPAddress.Parse(ipepstr[0]), Convert.ToInt32(ipepstr[1]));
            byte[] holdbytes = Encoding.Unicode.GetBytes(HOLDOK + UserName);            
            //回送打洞消息
            ClientSocket.SendTo(holdbytes, holdEP);
        }

        /// <summary>
        /// 处理用户上线的函数
        /// </summary>
        private void AddOnLine(byte[] inData, int recvCount)
        {
            string inStr = Encoding.Unicode.GetString(inData, 4, recvCount - 4);
            string[] userinfo = inStr.Split('|');
            string[] strUserEP = userinfo[1].Split(':');
            int count = OLUserName.Count - 1;
            //for (int i = 0; i < count; i++)
            //{
            //    if (OLUserName[i] == "")
            //    {
            OLUserName.Add(userinfo[0]);
            IPEndPoint ip = new IPEndPoint(IPAddress.Parse(strUserEP[0]), Convert.ToInt32(strUserEP[1]));
            OLUserEP.Add(ip);
            lock (dt)
            {
                DataRow dr = this.dt.NewRow();
                dr["Name"] = userinfo[0];
                dr["IP"] = ip;
                dt.Rows.Add(dr);
            }
            this.textbox.Invoke(new TextChangedMelegate(SetTextBox), userinfo[0] + "上线了!");
            
            //this.textBox1.Text += userinfo[0] + "上线了!\n";
            //break;
            //    }
            //}
        }

        /// <summary>
        /// 处理用户下线的函数
        /// </summary>
        private void RemoveOnLine(byte[] inData,int recvCount)
        {
            string offUname = Encoding.Unicode.GetString(inData,4,recvCount-4);
            for(int i = 0; i< OLUserName.Count-1;i++)
            {
                if(OLUserName[i] == offUname)
                {
                    OLUserName.Remove(offUname);
                    OLUserEP.RemoveAt(i);
                    lock (dt)
                    {
                        foreach (DataRow dr in dt.Rows)
                        {
                            if (dr["name"].ToString().Trim() == offUname.Trim())
                            {
                                dt.Rows.Remove(dr);
                                break;
                            }
                        }
                    }
                    this.textbox.Invoke(new TextChangedMelegate(SetTextBox), offUname + "下线了!");
                    //this.textBox1.Text += offUname + "下线了!\n";
                    break;
                }
            }
        }

        private void textBox2_KeyPress(object sender, KeyPressEventArgs e)
        {
            if (e.KeyChar == '\r')
                this.buttonSend_Click(null, null);
        }

        private void textbox_TextChanged(object sender, EventArgs e)
        {
            ////让文本框获取焦点   
            //this.textbox.Focus();
            ////设置光标的位置到文本尾   
            //this.textbox.Select(this.richTextBoxInfo.TextLength, 0);
            ////滚动到控件光标处   
            //this.textbox.ScrollToCaret();
        }

        private void Chat_FormClosed(object sender, FormClosedEventArgs e)
        {
            this.thListen.Abort();
            string loginOutStr = LOGINOUT + UserName;
            byte[] sendBytes = Encoding.Unicode.GetBytes(loginOutStr);
            ClientSocket.SendTo(sendBytes,ServerEP);
        }
    }
}



namespace TestClient
{
    partial class Chat
    {
        /// <summary>
        /// 必需的设计器变量。
        /// </summary>
        private System.ComponentModel.IContainer components = null;

        /// <summary>
        /// 清理所有正在使用的资源。
        /// </summary>
        /// <param name="disposing">如果应释放托管资源,为 true;否则为 false。</param>
        protected override void Dispose(bool disposing)
        {
            if (disposing && (components != null))
            {
                components.Dispose();
            }
            base.Dispose(disposing);
        }

        #region Windows 窗体设计器生成的代码

        /// <summary>
        /// 设计器支持所需的方法 - 不要
        /// 使用代码编辑器修改此方法的内容。
        /// </summary>
        private void InitializeComponent()
        {
            this.panel1 = new System.Windows.Forms.Panel();
            this.buttonSend = new System.Windows.Forms.Button();
            this.textBox2 = new System.Windows.Forms.TextBox();
            this.label1 = new System.Windows.Forms.Label();
            this.comboBox1 = new System.Windows.Forms.ComboBox();
            this.textbox = new System.Windows.Forms.RichTextBox();
            this.panel1.SuspendLayout();
            this.SuspendLayout();
            // 
            // panel1
            // 
            this.panel1.Controls.Add(this.buttonSend);
            this.panel1.Controls.Add(this.textBox2);
            this.panel1.Controls.Add(this.label1);
            this.panel1.Controls.Add(this.comboBox1);
            this.panel1.Dock = System.Windows.Forms.DockStyle.Bottom;
            this.panel1.Location = new System.Drawing.Point(0, 382);
            this.panel1.Name = "panel1";
            this.panel1.Size = new System.Drawing.Size(557, 59);
            this.panel1.TabIndex = 0;
            // 
            // buttonSend
            // 
            this.buttonSend.Location = new System.Drawing.Point(470, 29);
            this.buttonSend.Name = "buttonSend";
            this.buttonSend.Size = new System.Drawing.Size(75, 23);
            this.buttonSend.TabIndex = 1;
            this.buttonSend.Text = "发送";
            this.buttonSend.UseVisualStyleBackColor = true;
            this.buttonSend.Click += new System.EventHandler(this.buttonSend_Click);
            // 
            // textBox2
            // 
            this.textBox2.Location = new System.Drawing.Point(3, 29);
            this.textBox2.Name = "textBox2";
            this.textBox2.Size = new System.Drawing.Size(443, 21);
            this.textBox2.TabIndex = 0;
            this.textBox2.KeyPress += new System.Windows.Forms.KeyPressEventHandler(this.textBox2_KeyPress);
            // 
            // label1
            // 
            this.label1.AutoSize = true;
            this.label1.Location = new System.Drawing.Point(3, 6);
            this.label1.Name = "label1";
            this.label1.Size = new System.Drawing.Size(53, 12);
            this.label1.TabIndex = 2;
            this.label1.Text = "发送给:";
            // 
            // comboBox1
            // 
            this.comboBox1.FormattingEnabled = true;
            this.comboBox1.Location = new System.Drawing.Point(62, 3);
            this.comboBox1.Name = "comboBox1";
            this.comboBox1.Size = new System.Drawing.Size(121, 20);
            this.comboBox1.TabIndex = 3;
            // 
            // textbox
            // 
            this.textbox.BackColor = System.Drawing.SystemColors.ActiveCaptionText;
            this.textbox.Dock = System.Windows.Forms.DockStyle.Fill;
            this.textbox.Location = new System.Drawing.Point(0, 0);
            this.textbox.Name = "textbox";
            this.textbox.ReadOnly = true;
            this.textbox.Size = new System.Drawing.Size(557, 382);
            this.textbox.TabIndex = 1;
            this.textbox.Text = "";
            this.textbox.TextChanged += new System.EventHandler(this.textbox_TextChanged);
            // 
            // Chat
            // 
            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F);
            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
            this.ClientSize = new System.Drawing.Size(557, 441);
            this.Controls.Add(this.textbox);
            this.Controls.Add(this.panel1);
            this.Name = "Chat";
            this.Text = "Chat";
            this.FormClosed += new System.Windows.Forms.FormClosedEventHandler(this.Chat_FormClosed);
            this.Load += new System.EventHandler(this.Chat_Load);
            this.panel1.ResumeLayout(false);
            this.panel1.PerformLayout();
            this.ResumeLayout(false);

        }

        #endregion

        private System.Windows.Forms.Panel panel1;
        private System.Windows.Forms.TextBox textBox2;
        private System.Windows.Forms.Label label1;
        private System.Windows.Forms.ComboBox comboBox1;
        private System.Windows.Forms.Button buttonSend;
        private System.Windows.Forms.RichTextBox textbox;
    }
}

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Net;
using System.Net.Sockets;
using System.Threading;

namespace TestClient
{
    public partial class Login : Form
    {
        //以下是客户端到服务器端的消息开头
        const string LOGININ = "10";//请求登陆的消息|||消息形式:10+自己的用户名
        const string LOGINOK = "23";//登陆成功
        //以下是服务器到客户端的消息开头
        const string HVUSER  = "20";//用户名已存在

        Socket clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);  //客户端套节字的定义
        IPEndPoint serverEP;//服务器的IPEP

        delegate void myMethodDelegate(ref byte[] myInData);//登陆时用的事件

        private ManualResetEvent receiveDone;//在登陆时用来阻塞线程,等待收到数据

        bool inPutOk = false;
        public Login()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            try
            {
                serverEP = new IPEndPoint(IPAddress.Parse(this.textBoxServerIP.Text), 11000);
                inPutOk = true;
            }
            catch
            {
                MessageBox.Show("你输入的服务器IP不正确,请重新输入.");
                inPutOk = false;
            }

            
            //判断用户是否登陆成功
            bool ok = false;
            while (ok == false)
            {
                bool loginOk = LogOn(this.textBoxUserName.Text);
                if (loginOk)
                    ok = true;
                else
                {
                    DialogResult res = MessageBox.Show("是否重试?", "是否重试", MessageBoxButtons.YesNo);
                    if (res == DialogResult.No)
                        ok = true;
                    else
                        ok = false;
                }
            }
            Chat c = (Chat)this.Tag;
            c.UserName = this.textBoxUserName.Text;
            c.ServerEP = serverEP;
            c.ClientSocket = clientSocket;
            c.LoadInit();
            this.Close();          
        }


        //登陆函数
        private bool LogOn(string username)
        {
            receiveDone = new ManualResetEvent(false);
            byte[] userBytes = null;
            bool userOk = false;
            //判断用户名是否符合格式
            while(userOk != true)
            {
            username = username.ToUpper();
            userBytes = Encoding.Unicode.GetBytes(LOGININ + username);

            if(userBytes.Length > 24 || userBytes.Length < 10)
            {
                MessageBox.Show("用户名不得小于6个字节,且不得大于20个字节.");
                return false;
            }
            else
                userOk = true;
            }

            //向服务器发送客户消息
            clientSocket.SendTo(userBytes, serverEP);
            byte[] data = new Byte[1024];
            string comStr = Encoding.Unicode.GetString(data,0,4);       

            //异面的接收服务器回送的消息
            myMethodDelegate DGrecv = new myMethodDelegate(RecvLogin);
            DGrecv.BeginInvoke(ref data, null, null);

            //等待服务器回送消息的时长为10秒,否则为服务器超时
            receiveDone.WaitOne(30000, true);

            string recvStr = Encoding.Unicode.GetString(data,0,4);
            if(recvStr == comStr)
            {
                MessageBox.Show("服务器超时.登陆失败!!");
                return false;
            }
            else
            {
                if(Encoding.Unicode.GetString(data,0,4) == LOGINOK)
                {
                    MessageBox.Show("登陆成功!!");
                }
                else
                {
                    if (Encoding.Unicode.GetString(data, 0, 4) == HVUSER)
                    {
                        MessageBox.Show("用户名重复.登陆失败!!");
                        return false;
                    }
                    else
                    {
                        if (Encoding.Unicode.GetString(data, 0, 4) == LOGINOK)
                        {
                            MessageBox.Show("服务器未知错误,登陆失败!!!!");
                            return false;
                        }                       
                    }
                }
            }
            return true;
        }

        //登陆时用来异步的接收服务器发送的消息
        void RecvLogin(ref byte[] inData)
        {
            clientSocket.Receive(inData);
            receiveDone.Set();
        }
    }
}


namespace TestClient
{
    partial class Login
    {
        /// <summary>
        /// 必需的设计器变量。
        /// </summary>
        private System.ComponentModel.IContainer components = null;

        /// <summary>
        /// 清理所有正在使用的资源。
        /// </summary>
        /// <param name="disposing">如果应释放托管资源,为 true;否则为 false。</param>
        protected override void Dispose(bool disposing)
        {
            if (disposing && (components != null))
            {
                components.Dispose();
            }
            base.Dispose(disposing);
        }

        #region Windows 窗体设计器生成的代码

        /// <summary>
        /// 设计器支持所需的方法 - 不要
        /// 使用代码编辑器修改此方法的内容。
        /// </summary>
        private void InitializeComponent()
        {
            this.label2 = new System.Windows.Forms.Label();
            this.textBoxServerIP = new System.Windows.Forms.TextBox();
            this.label1 = new System.Windows.Forms.Label();
            this.textBoxUserName = new System.Windows.Forms.TextBox();
            this.button1 = new System.Windows.Forms.Button();
            this.SuspendLayout();
            // 
            // label2
            // 
            this.label2.AutoSize = true;
            this.label2.Location = new System.Drawing.Point(56, 27);
            this.label2.Name = "label2";
            this.label2.Size = new System.Drawing.Size(77, 12);
            this.label2.TabIndex = 7;
            this.label2.Text = "服务器IP:";
            // 
            // textBoxServerIP
            // 
            this.textBoxServerIP.Location = new System.Drawing.Point(139, 24);
            this.textBoxServerIP.Name = "textBoxServerIP";
            this.textBoxServerIP.Size = new System.Drawing.Size(100, 21);
            this.textBoxServerIP.TabIndex = 6;
            this.textBoxServerIP.Text = "10.24.11.231";
            // 
            // label1
            // 
            this.label1.AutoSize = true;
            this.label1.Location = new System.Drawing.Point(56, 68);
            this.label1.Name = "label1";
            this.label1.Size = new System.Drawing.Size(53, 12);
            this.label1.TabIndex = 9;
            this.label1.Text = "用户名:";
            // 
            // textBoxUserName
            // 
            this.textBoxUserName.Location = new System.Drawing.Point(139, 65);
            this.textBoxUserName.Name = "textBoxUserName";
            this.textBoxUserName.Size = new System.Drawing.Size(100, 21);
            this.textBoxUserName.TabIndex = 8;
            // 
            // button1
            // 
            this.button1.Location = new System.Drawing.Point(100, 106);
            this.button1.Name = "button1";
            this.button1.Size = new System.Drawing.Size(75, 23);
            this.button1.TabIndex = 10;
            this.button1.Text = "登 陆";
            this.button1.UseVisualStyleBackColor = true;
            this.button1.Click += new System.EventHandler(this.button1_Click);
            // 
            // Login
            // 
            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F);
            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
            this.ClientSize = new System.Drawing.Size(292, 144);
            this.Controls.Add(this.button1);
            this.Controls.Add(this.label1);
            this.Controls.Add(this.textBoxUserName);
            this.Controls.Add(this.label2);
            this.Controls.Add(this.textBoxServerIP);
            this.Name = "Login";
            this.Text = "Login";
            this.ResumeLayout(false);
            this.PerformLayout();

        }

        #endregion

        private System.Windows.Forms.Label label2;
        private System.Windows.Forms.TextBox textBoxServerIP;
        private System.Windows.Forms.Label label1;
        private System.Windows.Forms.TextBox textBoxUserName;
        private System.Windows.Forms.Button button1;
    }
}


Server:
using System;
using System.Drawing;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;
using System.Net.Sockets;
using System.Threading;
using System.Net;
using System.Text;

namespace ChatServer
{
 /// <summary>
 /// Form1 的摘要说明。
 /// </summary>
 public partial class Form1 : System.Windows.Forms.Form
 {
  /// <summary>
  /// 必需的设计器变量。
  /// </summary>
  private System.ComponentModel.Container components = null;
  //全局变量
  Socket ServerSocket = null;
  IPEndPoint ipep = null;
  Hashtable htUserList = null;
  string[] userName = new string[1000];
  IPEndPoint[] userIPEP = new IPEndPoint[1000];
  int[] userTime = new int[1000];
  TimerCallback timerDelegate = null;
  WriteLog writeLog = null;
  Thread listenTH = null;
  bool flag = true;

  //常量
  //以下是客户端到服务器端的消息开头
  const string LOGININ = "10"; //请求登陆的消息|||消息形式:10+自己的用户名
  const string LOGINOUT = "11"; //请求登出的消息|||消息形式:11+自己的用户名
  const string GETULIST = "12"; //请求获得在线用户列表|||消息形式:12
  const string P2PCONN = "13"; //请求P2P连接的消息|||消息形式:13+自己的用户名+|+对方的用户名
  const string HOLDLINE = "14"; //保持连接.|||消息开式:14+自己的用户名
        const string LOGINADD = "15";//新用户登录
        const string LOGINREMOVE = "16";//用户登出

  //以下是服务器到客户端的消息开头
  const string HVUSER = "20"; //用户名已存在
  const string GETUSER = "21"; //在线用户列表|||消息格式:21+用户名+EP
  const string MAKHOLD = "22"; //打洞命令|||消息格式:22+IP
  const string LOGINOK = "23"; //登陆成功
  const string SERVCLS = "24"; //服务器关闭
  const string MSGEND = "25"; //消息结束

  //以下是服务器端的命名
  const string EXITPRO = "EXIT"; //退出命令
  const string SHOWULIST = "SHOWUSER";
  private System.Windows.Forms.StatusBar statusBar1;
  private System.Windows.Forms.StatusBarPanel statusBarPanel1;
  private System.Windows.Forms.TextBox textBox1; //显示在线用户
  const string HELP = "HELP"; //显示帮助
        private List<IPEndPoint> onlineIPList;

  public Form1()
  {
   //
   // Windows 窗体设计器支持所必需的
   //
   InitializeComponent();

   //初始化变量
   ServerSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); 
   ipep = new IPEndPoint(IPAddress.Any, 11000);
   htUserList = new Hashtable();
   writeLog = new WriteLog();
   timerDelegate = new TimerCallback(OnLineTimeOut);
  }

  /// <summary>
  /// 清理所有正在使用的资源。
  /// </summary>
  protected override void Dispose( bool disposing )
  {
   if( disposing )
   {
    if (components != null) 
    {
     components.Dispose();
    }
   }
   base.Dispose( disposing );
  }

  #region Windows 窗体设计器生成的代码
  /// <summary>
  /// 设计器支持所需的方法 - 不要使用代码编辑器修改
  /// 此方法的内容。
  /// </summary>
  private void InitializeComponent()
  {
   this.statusBar1 = new System.Windows.Forms.StatusBar();
   this.statusBarPanel1 = new System.Windows.Forms.StatusBarPanel();
   this.textBox1 = new System.Windows.Forms.TextBox();
   ((System.ComponentModel.ISupportInitialize)(this.statusBarPanel1)).BeginInit();
   this.SuspendLayout();
   // 
   // statusBar1
   // 
   this.statusBar1.Location = new System.Drawing.Point(0, 319);
   this.statusBar1.Name = "statusBar1";
   this.statusBar1.Panels.AddRange(new System.Windows.Forms.StatusBarPanel[] {
                        this.statusBarPanel1});
   this.statusBar1.ShowPanels = true;
   this.statusBar1.Size = new System.Drawing.Size(466, 22);
   this.statusBar1.TabIndex = 0;
   // 
   // statusBarPanel1
   // 
   this.statusBarPanel1.AutoSize = System.Windows.Forms.StatusBarPanelAutoSize.Spring;
   this.statusBarPanel1.Text = "statusBarPanel1";
   this.statusBarPanel1.Width = 450;
   // 
   // textBox1
   // 
   this.textBox1.Dock = System.Windows.Forms.DockStyle.Left;
   this.textBox1.Location = new System.Drawing.Point(0, 0);
   this.textBox1.Multiline = true;
   this.textBox1.Name = "textBox1";
   this.textBox1.ScrollBars = System.Windows.Forms.ScrollBars.Vertical;
   this.textBox1.Size = new System.Drawing.Size(286, 319);
   this.textBox1.TabIndex = 1;
   this.textBox1.Text = "";
   // 
   // Form1
   // 
   this.AutoScaleBaseSize = new System.Drawing.Size(6, 14);
   this.ClientSize = new System.Drawing.Size(466, 341);
   this.Controls.Add(this.textBox1);
   this.Controls.Add(this.statusBar1);
   this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
   this.MaximizeBox = false;
   this.Name = "Form1";
   this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
   this.Text = "服务程序";
   this.Load += new System.EventHandler(this.Form1_Load);
   this.Closed += new System.EventHandler(this.Form1_Closed);
   ((System.ComponentModel.ISupportInitialize)(this.statusBarPanel1)).EndInit();
   this.ResumeLayout(false);

  }
  #endregion

  /// <summary>
  /// 启动服务器
  /// </summary>
  /// <param name="sender"></param>
  /// <param name="e"></param>
  private void Form1_Load(object sender, System.EventArgs e)
  {
   IPAddress[] addressList = Dns.GetHostByName(Dns.GetHostName()).AddressList;
   IPAddress ServerIP = addressList[0];
   ServerSocket.Bind(ipep);
   SetStatusText("服务器正在启动......");
   SetStatusText("服务器IP:" + ServerIP.ToString() + "  正在监听" + ipep.Port.ToString() + "端口");
   listenTH = new Thread(new ThreadStart(Listen)); //监听线程
   listenTH.Start(); //启动
   SetStatusText("服务器启动成功.....");
   System.Windows.Forms.Timer timer = new System.Windows.Forms.Timer();
   timer.Tick += new EventHandler(timer_Tick);
   timer.Interval = 5000;
   timer.Start();
  }

  /// <summary>
  /// 服务器监听函数
  /// </summary>
  private void Listen()
  {
   try
   {
    while (flag)
    {
     int recv = 0;
     byte[] data = new byte[1024];
     IPEndPoint sender = new IPEndPoint(IPAddress.Any, 0);
     EndPoint tempRemoteEP = sender as EndPoint;
     recv = ServerSocket.ReceiveFrom(data, ref tempRemoteEP);
     string msgHead = Encoding.Unicode.GetString(data, 0, 4);
     switch (msgHead)
     {
      case LOGININ: //用户登录
       string LoginThing = UserLogin(data, (IPEndPoint)tempRemoteEP, recv);
       if (LoginThing == HVUSER) //用户存在
       {
        SendMsg(HVUSER, (IPEndPoint)tempRemoteEP);
       }
       else
       {
           SendMsg(LOGINOK, (IPEndPoint)tempRemoteEP);
                                if (onlineIPList == null)
                                {
                                    onlineIPList = new List<IPEndPoint>();
                                }
                                onlineIPList.Add((IPEndPoint)tempRemoteEP);
                                foreach (IPEndPoint ip in onlineIPList)
                                {
                                    SendMsg(LOGINADD, ip);
                                }
       }
       break;
      case LOGINOUT: //用户退出
       Userloginout(data, recv);
                            onlineIPList.Remove((IPEndPoint)tempRemoteEP);
                            foreach (IPEndPoint ip in onlineIPList)
                            {
                                SendMsg(LOGINREMOVE, ip);
                            }
       break;
      case GETULIST: // 获取用户列表
       string userInfor = GetUserList();
       SendMsg(GETUSER + userInfor, (IPEndPoint)tempRemoteEP);
       break;
      case P2PCONN: //打洞
       QuestP2PConn(data, recv);
       break;
      case HOLDLINE:
       HoldOnLine(data, recv);
       break;
     }
    }
   }
   catch (System.Exception errr)
   {
    writeLog.AppendErrorLog(errr.ToString());
   }
  }

  /// <summary>
  /// 保持用户在线
  /// </summary>
  /// <param name="data"></param>
  /// <param name="recvCount"></param>
  private void HoldOnLine(byte[] data, int recvCount)
  {
   string Uname = Encoding.Unicode.GetString(data, 4, recvCount - 4);
   int i = 0;
   for (i=0;i<userName.Length;i++)
   {
    if (Uname == userName[i])
    {
     userTime[i] = 60;
     break;
    }
   }
  }

  /// <summary>
  /// 用户超时退出
  /// </summary>
  private void OnLineTimeOut(object state)
  {
   int i = 0, j= 0;
   for (i=0;i<userName.Length;i++)
   {
    if (userTime[i] > 0)
    {
     userTime[i] -= 5;
     if (userTime[i] < 0)
     {
      string loginoutmsg = LOGINOUT + userName[i];
      userName[i] = "";
      userIPEP[i] = null;
      
      byte[] ULoginOutbytes = Encoding.Unicode.GetBytes(loginoutmsg);
      for (i=1;j<userName.Length;j++)
      {
       if (userName[j] != "")
       {
        if (userIPEP[j] != null)
        {
         ServerSocket.SendTo(ULoginOutbytes, userIPEP[j]);
        }
       }
      }
     }
    }
   }
  }

  /// <summary>
  /// 转发P2P连接请求
  /// </summary>
  private void QuestP2PConn(byte[] data, int recv)
  {
   string recvStr = Encoding.Unicode.GetString(data, 4, recv - 4);
   string[] split = recvStr.Split(new char[]{'|'});
   IPEndPoint fromEP = null, toEP = null;
   int i;
   for (i=0;i<userName.Length;i++)
   {
    if (userName[i] == split[0])
    {
     fromEP = userIPEP[i];
    }
    if (userName[i] == split[1])
    {
     toEP = userIPEP[i];
    }
   }
   byte[] holdbyte = Encoding.Unicode.GetBytes(MAKHOLD + fromEP.ToString());
   ServerSocket.SendTo(holdbyte, toEP);
  }

  /// <summary>
  /// 用户登录,直接返回登陆是否成功的值
  /// </summary>
  /// <param name="data"></param>
  /// <param name="userEP"></param>
  /// <param name="recvCount"></param>
  /// <returns></returns>
  private string UserLogin(byte[] data, IPEndPoint userEP, int recvCount)
  {
   string Uname = Encoding.Unicode.GetString(data, 4, recvCount - 4);
   byte[] Uinfobytes = null;
   int i, j;
   for (i=0;i<userName.Length;i++)
   {
    if (userName[i] != "" && userName[i] != null)
    {
     if (userName[i].Equals(Uname))
      return HVUSER;
    }
   }
   for (i=0;i<userName.Length;i++)
   {
    if (userName[i] == "" || userName[i] == null)
    {
     userName[i] = Uname;
     userIPEP[i] = userEP;
     userTime[i] = 60;
     Uinfobytes = Encoding.Unicode.GetBytes(LOGININ + userName[i] + "|" + userIPEP[i].ToString());
     for (j=1;j<userName.Length;j++)
     {
      if (userName[j] != "" && userName[j] != Uname && userName[j] != null && userIPEP[j] != null)
      {
       ServerSocket.SendTo(Uinfobytes, userIPEP[j]);
      }
     }
     break;
    }
   }
   return LOGINOK;
  }

  /// <summary>
  /// 用户退出
  /// </summary>
  /// <param name="data"></param>
  /// <param name="recvCount"></param>
  private void Userloginout(byte[] data, int recvCount)
  {
   int i = 0, j = 0;
   string Uname = Encoding.Unicode.GetString(data, 4, recvCount - 4);
   for (i=0;i<userName.Length;i++)
   {
    if (Uname.ToUpper() == userName[i].ToUpper())
    {
     string loginOutMsg = LOGINOUT + Uname;
     userName[i] = "";
     userIPEP[i] = null;
     userTime[i] = 0;
     for (j=1;j<userName.Length;j++)
     {
      if (userName[j]!= null && userName[j] != "")
      {
       SendMsg(loginOutMsg, userIPEP[j]);
      }
     }
     break;
    }
   }
  }

  /// <summary>
  /// 获取用户列表
  /// </summary>
  /// <returns></returns>
  private string GetUserList()
  {
   string userInfor = "";
   int i = 0;
   for (i=0;i<userName.Length;i++)
   {
    if (userName[i] != "" && userName[i] != null && userIPEP[i] != null)
    {
     userInfor += userName[i] + "|" + userIPEP[i].ToString() + "|";
    }
   }
   return userInfor;
  }

  /// <summary>
  /// 发送消息
  /// </summary>
  /// <param name="msg"></param>
  /// <param name="remoteEP"></param>
  private void SendMsg(string msg, IPEndPoint remoteEP)
  {
   byte[] sendBytes = Encoding.Unicode.GetBytes(msg);
   try
   {
    ServerSocket.SendTo(sendBytes, remoteEP);
   }
   catch (System.Exception errr) {
    writeLog.AppendErrorLog(errr.ToString());
   }
  }

  /// <summary>
  /// 提示信息
  /// </summary>
  /// <param name="text"></param>
  /// <returns></returns>
  private void SetStatusText(string text)
  {
   this.statusBarPanel1.Text = text;
  }

  /// <summary>
  /// 显示在线用户 
  /// </summary>
  private StringBuilder ShowUser()
  {
   StringBuilder strB = new StringBuilder();
   if (userName != null && userName.Length > 0)
   {
    int i;
    for (i=0;i<userName.Length;i++)
    {
     if (userName[i] != "" && userIPEP[i] != null)
     {
      strB.Append(Environment.NewLine + "用户名:" + userName[i] + "  地址:" + userIPEP[i].ToString());
     }
    }
   }
   return strB;
  }

  /// <summary>
  /// 退出
  /// </summary>
  /// <param name="sender"></param>
  /// <param name="e"></param>
  private void Form1_Closed(object sender, System.EventArgs e)
  {
   flag = false;
   if (listenTH != null)
   {
    if (listenTH.ThreadState == ThreadState.Running)
    {
     listenTH.Abort();
    }
    listenTH = null;
   }
   timerDelegate = null;
   ServerSocket.Close();
  }

  private void timer_Tick(object sender, EventArgs e)
  {
   OnLineTimeOut(null);
   StringBuilder strB = ShowUser();
   this.textBox1.Text = strB.ToString();
  }

 }
}


using System;
using System.Text;
using System.IO;
using System.Windows.Forms;

namespace ChatServer
{
 /// <summary>
 /// WriteLog 的摘要说明。
 /// </summary>
 public class WriteLog
 {
  public WriteLog()
  {
   //
   // TODO: 在此处添加构造函数逻辑
   //
  }
  /// <summary>
  /// 追加日志文件
  /// </summary>
  /// <param name="errorMsg"></param>
  public void AppendErrorLog(string errorMsg)
  {
   try
   {
    StringBuilder strB = new StringBuilder();
    strB.Append(DateTime.Now.ToString());
    strB.Append("   ");
    strB.Append(Environment.MachineName.ToString());
    strB.Append("   ");
    strB.Append(Environment.UserDomainName.ToString() + "\\" + Environment.UserName);
    strB.Append("   ");
    strB.Append(Environment.OSVersion.Platform.ToString() + "\\" + Environment.OSVersion.Version.ToString());
    strB.Append(Environment.NewLine + Environment.NewLine);
    strB.Append(errorMsg);
    strB.Append(Environment.NewLine + Environment.NewLine);
    AppendErrorLog(strB);
   }
   catch{}
  }

  /// <summary>
  /// 追加日志文件
  /// </summary>
  /// <param name="strB"></param>
  public void AppendErrorLog(StringBuilder strB)
  {
   try
   {
    string filePath = "";
    filePath = CreateErrorLogFile();
    FileStream fileStream = File.Open(filePath, FileMode.Append, FileAccess.Write);
    StreamWriter streamWriter = new StreamWriter(fileStream);
    
    streamWriter.Write(strB.ToString());
    streamWriter.Flush();
    streamWriter.Close();
    fileStream.Close();
    fileStream = null;
   }
   catch{}
  }

  /// <summary>
  /// 创建日志文件
  /// </summary>
  private string CreateErrorLogFile()
  {
   string directoryPath = Application.StartupPath + "\\ErrorLog";
   string filePath = string.Empty;
   //判断日志文件夹是否存在
   if (!Directory.Exists(directoryPath))
   {
    Directory.CreateDirectory(directoryPath);
   }
   //根据日期创建日志文件
   filePath = directoryPath + "\\Error" + string.Format("{0:D2}", DateTime.Today.Year) + string.Format("{0:D2}", DateTime.Today.Month) + string.Format("{0:D2}", DateTime.Today.Day) + ".log";
   if (!File.Exists(filePath))
   {
    FileStream fileStream = File.Create(filePath);
    fileStream.Close();
   }
   return filePath;
  }

 }
}

地址:http://www.cnblogs.com/gjahead/archive/2007/04/04/699842.html

posted @ 2014-11-28 17:23  yiyan21  阅读(904)  评论(0编辑  收藏  举报