udp实现聊天程序

其实udp不存在客户端和服务端之分。我这里主要区分两个程序。因为有个程序需要指定ip.所以我就就把它叫客户端。客户端会先指定ip。并且异步方法接收消息。一旦受到消息,会交给回调函数处理。

服务端会保存客户端发来的ip和端口。用于回发。服务端也实现异步接收消息的方法。

并且消息显示方面。我用了ManualReSetevent来同步控制窗体的显示。注释写的很清楚。本人还是学生,可能注释理解有错,希望各位指点。

我觉得程序最大的特点就是界面不会卡顿,而且一旦设置好ip,客户端和服务端就能随意发送和收取消息。

我画了个图:左右两边的四个小箭头我主要想说明循环调用;

客户端代码:

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

namespace CHEINGDDDDDDD
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }
        public static Socket udpReceive;//接收对象
        public static Socket udpSend;//发送对象
        public const int udpR_PROT = 9095;
        public const int udpS_PORT = 9096;
        public static byte[] udpReDataBuf;//接收字节数组
        public static byte[] udpSeDataBuf;//发送字节数组
        public static bool setIP = false;//用于判断ip是否正确
        public static EndPoint remoteEP;
        public static IPEndPoint remoteIPEP;
        public static IPAddress remoteIpadress;
        public static int sendLen;
        public static string messageC;//收到消息的字符窜
        public static string messageS;//发送消息的字符窜
        public static int receiveLen;//收到消息的的长度
        public static IntPtr Main_wnd_handle;//窗体句柄
        public static ManualResetEvent close;//同步控制线程结束
        public static ManualResetEvent come;//同步控制线程消息收
        public static ManualResetEvent post;//同步控制线程消息发
        public static ManualResetEvent[] list;
        public const int MESSAGE_SEND = 0x521;//消息常量 发送
        public const int MESSAGE_CON = 0x520;//消息常量 接受
        [DllImport("User32.dll", EntryPoint = "SendMessage")]
        private static extern int SendMessage(
            IntPtr hWnd,
            int Msg,
            int wParam,
            int lParam
            );
        public static void Setdate()//用于数据初始化
        {
            come = new ManualResetEvent(false);
            close = new ManualResetEvent(false);
            post = new ManualResetEvent(false);
            list = new ManualResetEvent[3];
            list[0] = close;//0 线程结束
            list[1] = come;//1 消息到达
            list[2] = post;//2 消息发送
            //初始消息为1k
            udpReDataBuf = new byte[1024];
            udpSeDataBuf = new byte[1024];
            //初始发送的ip地址和端口的封装,会在后面的发送按钮明确ip和端口
            remoteIPEP = new IPEndPoint(IPAddress.Any, 0);
            //用于接收的Socket的绑定,确定的端口
            IPEndPoint iep = new IPEndPoint(IPAddress.Any, udpR_PROT);
            //new Socket收
            udpReceive = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
            //绑定
            udpReceive.Bind(iep);
            //注意remoteEP是EndPoint型,是抽象类,所以不能new
            remoteEP = (EndPoint)remoteIPEP;
            //BeginReceiveFrom是异步的方法接收消息,注意ref remote ,当有消息来会写入ip地址
            udpReceive.BeginReceiveFrom(udpReDataBuf, 0, 1024, SocketFlags.None, ref remoteEP, ReceiveServerCallback, new object());
            //new Socket 发
            udpSend = new Socket(AddressFamily.InterNetwork,
              SocketType.Dgram, ProtocolType.Udp);
            //工作线程启动
            ThreadStart workStart = new ThreadStart(doing);
            Thread gogo = new Thread(workStart);
            gogo.Start();



        }

        private void Form1_Load(object sender, EventArgs e)
        {
            //句柄赋值,初始化函数的运行和线程的启动
            Main_wnd_handle = this.Handle;
            Setdate();
        }
        //窗体消息回调函数。用于同步显示发送消息。
        //消息来就添加到texbox2;消息发送就清空texbox3,同时显示在texbox2
        protected override void DefWndProc(ref Message m)
        {
            switch (m.Msg)
            {
                case MESSAGE_CON:
                    textBox2.AppendText("服务器:" + messageC+"\n");
                    break;
                case MESSAGE_SEND:
                    textBox3.Clear();
                    textBox2.AppendText("客户机:" + messageS + "\n");
                    break;

                default:

                    base.DefWndProc(ref m);
                    break;
            }
        }
        //消息发送按钮 

        private void button1_Click(object sender, EventArgs e)
        {
            remoteIPEP.Address = remoteIpadress;//远程ip
            remoteIPEP.Port = udpS_PORT;//远程端口
            messageS = textBox3.Text;//发送的消息字符串
            udpSeDataBuf = Encoding.UTF8.GetBytes(messageS);//字节窜封装成字节数组
            udpSend.SendTo(udpSeDataBuf, remoteIPEP);//发送
            post.Set();//同步控制窗体显示
            
        }
        //接收消息的回调函数
        public static void ReceiveServerCallback(IAsyncResult ar)
        {
            try
            {
                //远程ip的写入,字节流转换成字符窜
                EndPoint tmpremoteEP = (EndPoint)remoteIPEP;
                receiveLen = udpReceive.EndReceiveFrom(ar, ref tmpremoteEP);
                messageC = Encoding.UTF8.GetString(udpReDataBuf, 0, receiveLen);
                int len = receiveLen;
                //有消息来同步控制窗体显示
                if (len != 0)
                {
                    come.Set();
                }
                //再次调用异步方法接收
                udpReceive.BeginReceiveFrom(udpReDataBuf, 0, 1024, SocketFlags.None, ref remoteEP, ReceiveServerCallback, new object());

            }
            catch (SocketException se)
            {
                MessageBox.Show(se.Message);
            }

        }
        //工作线程控制窗体显示
        public static void doing()
        {
            int select;
            bool working = true;
            while (working)
            {
                select = WaitHandle.WaitAny(list, 1000);
                switch (select)
                {
                    case 0:
                        working = false;
                        break;
                    case 1:
                        come.Reset();
                        SendMessage(Main_wnd_handle, MESSAGE_CON, 0, 0);
                        //消息到达
                        break;
                    case 2:
                        post.Reset();
                        SendMessage(Main_wnd_handle, MESSAGE_SEND, 0, 0);

                        //消息发送
                        break;
                    case WaitHandle.WaitTimeout:
                        //超时处理
                        break;
                }

            }

        }
        //判断写入的ip是否正确
        private void button2_Click(object sender, EventArgs e)
        {
            setIP = IPAddress.TryParse(textBox1.Text, out remoteIpadress);
            if (!setIP)
            {
                label1.Text = "输入IP不正确";
                textBox1.Focus();

            }
            else
            {
                label1.Text = "IP地址解析正确";

            }
        }
        //结束工作线程

        private void button3_Click(object sender, EventArgs e)
        {
            close.Set();
        }
    }
}

 

服务端代码:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace WindowsFormsApplication1
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            Main_wnd_handle = this.Handle;
            Setdate();
        }
        //设置变量
        public const int MESSAGE_CON = 0x520;//消息常亮 收
        public const int MESSAGE_SEND = 0x521;//消息常量 发
        public static IntPtr Main_wnd_handle;//窗体句柄
        public static Socket udpReceive;//socket 收
        public static Socket udpSend;//socket 发
        public const int udpR_PORT = 9096;
        public const int udpS_PORT = 9095;
        public static IPEndPoint remoteIPEP;
        public static EndPoint remoteEP;
        public static byte[] udpSeDataBuf;
        public static byte[] udpReDataBuf;
        public static int receiveLen;
        public static string messageC;
        public static string messageS;
        public static ManualResetEvent post;
        public static ManualResetEvent close;
        public static ManualResetEvent come;
        public static ManualResetEvent[] list;




        [DllImport("User32.dll", EntryPoint = "SendMessage")]
        private static extern int SendMessage(
            IntPtr hWnd,
            int Msg,
            int wParam,
            int lParam
            );
        //窗体回调处理
        protected override void DefWndProc(ref Message m)
        {
            switch (m.Msg)
            {
                case MESSAGE_CON:
                    textBox1.AppendText("客户机:" + messageC+"\n");
                    break;

                case MESSAGE_SEND:
                    textBox2.Clear();
                    textBox1.AppendText("服务器:" + messageS + "\n");
                    break;
                default:


                    base.DefWndProc(ref m);
                    break;
            }
        }
        public static void Setdate()
        {
            come = new ManualResetEvent(false);
            post = new ManualResetEvent(false);
            close = new ManualResetEvent(false);
            list = new ManualResetEvent[3];
            list[0] = close;
            list[1] = come;
            list[2] = post;
            udpReDataBuf = new byte[1024];
            udpSeDataBuf = new byte[1024];
            remoteIPEP = new IPEndPoint(IPAddress.Any, 0);
            IPEndPoint iep = new IPEndPoint(IPAddress.Any, udpR_PORT);
            udpReceive = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
            udpReceive.Bind(iep);
            udpSend = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
            remoteEP = (EndPoint)remoteIPEP;
            udpReceive.BeginReceiveFrom(udpReDataBuf, 0, 1024, SocketFlags.None, ref remoteEP, ReceiveServerCallback, new object());

            ThreadStart workStart = new ThreadStart(doing);
            Thread gogo = new Thread(workStart);
            gogo.Start();





        }
        public static void doing()
        {
            bool linlin = true;
            int select;

            while (linlin)
            {
                select = WaitHandle.WaitAny(list, 1000);
                switch (select)
                {
                    case 0:


                        linlin = false;
                        break;
                    case 1:
                        come.Reset();

                        SendMessage(Main_wnd_handle, MESSAGE_CON, 0, 0);

                        break;
                    case 2:
                        post.Reset();
                        SendMessage(Main_wnd_handle, MESSAGE_SEND, 0, 0);
                        break;
                    case WaitHandle.WaitTimeout:

                        break;


                }



            }

        }
        public static void ReceiveServerCallback(IAsyncResult ar)
        {
            try
            {
                //实际写入远程ip的地方,所以需要个临时tmpremoteEP写入。再装换成IPEndPoint类型给remoteIPEP,用于确定会发的ip地址
                EndPoint tmpremoteEP = (EndPoint)remoteIPEP;
                receiveLen = udpReceive.EndReceiveFrom(ar, ref tmpremoteEP);
                remoteIPEP = (IPEndPoint)tmpremoteEP;
                messageC = Encoding.UTF8.GetString(udpReDataBuf, 0, receiveLen);
                if (receiveLen != 0)
                {
                    come.Set();
                }
                udpReceive.BeginReceiveFrom(udpReDataBuf, 0, 1024, SocketFlags.None, ref remoteEP, ReceiveServerCallback, new object());

            }
            catch (SocketException se)
            {
                MessageBox.Show(se.Message);
            }

        }

        private void button1_Click(object sender, EventArgs e)
        {
            messageS = textBox2.Text;
            udpSeDataBuf = Encoding.UTF8.GetBytes(messageS);
            //注意远程端口的绑定
            remoteIPEP.Port = udpS_PORT;
            udpSend.SendTo(udpSeDataBuf, remoteIPEP);
            post.Set();
            

        }
     
    }
}

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

我画了个图:左右两边的四个小箭头我主要想说明循环调用;

posted @ 2015-06-28 22:10  林看看  阅读(521)  评论(0编辑  收藏  举报