• 写着玩的聊天程序,拿出来给没有接触过的玩


    主要就是多线程与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;
      }

     }
    }

  • 相关阅读:
    php pcntl 多进程学习
    php socket 学习
    linux 常用alias
    php 设置一个函数的最大运行时间
    QTableView 一列添加两个按钮
    翻译qmake文档 目录
    翻译qmake文档(四) Building Common Project Types
    算法时间复杂度
    翻译qmake文档(三) Creating Project Files
    Caliburn.Micro学习笔记目录
  • 原文地址:https://www.cnblogs.com/tianyamoon/p/699414.html
Copyright © 2020-2023  润新知