基于socket实现的简单的聊天程序
记得八年前第一次使用socket做的一个五子棋程序,需要序列化棋子对象,传递到对方的电脑上。 一个偶然的机会,第二次使用socket做点事情。先看聊天服务器端的实现:
服务器端要实现以下功能:
1、启动服务,开启监听
2、持续不断地接收消息
3、发送消息
启动服务,创建监听socket,绑定Ip和端口:
1 /// <summary> 2 /// 启动服务 3 /// </summary> 4 private void Start() 5 { 6 socketwatch = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); 7 8 IPAddress address = IPAddress.Parse("10.0.0.31"); 9 10 IPEndPoint point = new IPEndPoint(address, 8025); 11 12 socketwatch.Bind(point); 13 14 //将套接字的监听队列长度限制为20 15 socketwatch.Listen(20); 16 17 //创建一个监听线程 18 threadwatch = new Thread(watchconnecting); 19 20 threadwatch.IsBackground = true; 21 22 threadwatch.Start(); 23 }
创建接收数据的Socket:
1 //监听客户端发来的请求 2 private void watchconnecting() 3 { 4 Socket connection = null; 5 while (true) //持续不断监听客户端发来的请求 6 { 7 try 8 { 9 connection = socketwatch.Accept(); 10 } 11 catch (Exception ex) 12 { 13 MessageBox.Show(ex.Message); //提示套接字监听异常 14 break; 15 } 16 //获取客户端的IP和端口号 17 IPAddress clientIP = (connection.RemoteEndPoint as IPEndPoint).Address; 18 int clientPort = (connection.RemoteEndPoint as IPEndPoint).Port; 19 20 //让客户显示"连接成功的"的信息 21 string sendmsg = "连接服务端成功!\r\n" + "本地IP:" + clientIP + ",本地端口" + clientPort.ToString(); 22 byte[] arrSendMsg = Encoding.UTF8.GetBytes(sendmsg); 23 connection.Send(arrSendMsg); 24 25 RemoteEndPoint = connection.RemoteEndPoint.ToString(); //客户端网络结点号 26 dic.Add(RemoteEndPoint, connection); //添加客户端信息 27 28 IPEndPoint netpoint = connection.RemoteEndPoint as IPEndPoint; 29 30 //创建一个通信线程 31 ParameterizedThreadStart pts = new ParameterizedThreadStart(recv); 32 Thread thread = new Thread(pts); 33 thread.IsBackground = true;//设置为后台线程,随着主线程退出而退出 34 //启动线程 35 thread.Start(connection); 36 } 37 }
socketwatch.Accept() 会创建一个新的socket,它用来负责和某个客户端的通讯。
接收数据:
1 private void recv(object socketclientpara) 2 { 3 4 Socket socketServer = socketclientpara as Socket; 5 while (true) 6 { 7 byte[] arrServerRecMsg = new byte[1024 * 1024]; 8 try 9 { 10 int length = socketServer.Receive(arrServerRecMsg); 11 12 string strSRecMsg = Encoding.UTF8.GetString(arrServerRecMsg, 0, length); 13 14 this.Dispatcher.BeginInvoke(DispatcherPriority.Normal, 15 (ThreadStart)delegate() 16 { 17 MessagePool.Insert(MessagePool.Count, new Message() { MessageInfo = strSRecMsg, IdentityName = socketServer.RemoteEndPoint.ToString() }); 18 }); 19 } 20 catch (Exception ex) 21 { 22 MessageBox.Show(ex.Message); 23 socketServer.Close(); 24 break; 25 } 26 } 27 }
把从客户端接收过来的字节流变为字符串,然后添加到页面上。
发送消息:
1 private static object lockPool = new object(); 2 public static ObservableCollection<Message> MessagePool = new ObservableCollection<Message>(); 3 private void send_Click(object sender, RoutedEventArgs e) 4 { 5 string msg = this.message.Text; 6 7 this.message.Clear(); 8 9 lock (lockPool) 10 { 11 MessagePool.Insert(MessagePool.Count, new Message() { MessageInfo = msg, IdentityName = "wbq" }); 12 } 13 14 string sendMsg = msg; 15 byte[] bytes = System.Text.Encoding.UTF8.GetBytes(sendMsg); 16 17 if (dic.Count > 0) 18 dic.First().Value.Send(bytes); 19 }
dic.First().Value.Send,从字典中获取到之前创建好的通讯socket,然后调用send方法。
客户端需要实现以下功能:
1、和服务器建立连接
2、接收数据
3、发送数据
建立连接:
1 private void Start() 2 { 3 4 //定义一个套接字监听 5 socketclient = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); 6 7 8 var servers= GetServers(); 9 10 11 if (servers.Count > 0) 12 { 13 14 //获取文本框中的IP地址 15 IPAddress address = IPAddress.Parse(servers.First().IPToPing); 16 17 //将获取的IP地址和端口号绑定在网络节点上 18 IPEndPoint point = new IPEndPoint(address, int.Parse(servers.First().Port)); 19 20 try 21 { 22 //客户端套接字连接到网络节点上,用的是Connect 23 socketclient.Connect(point); 24 } 25 catch (Exception) 26 { 27 //MessageBox. 28 MessageBox.Show("连接失败\r\n"); 29 return; 30 } 31 32 threadclient = new Thread(recv); 33 34 threadclient.IsBackground = true; 35 36 threadclient.Start(); 37 } 38 }
接收消息:
1 // 接收服务端发来信息的方法 2 private void recv()// 3 { 4 while (true)//持续监听服务端发来的消息 5 { 6 try 7 { 8 //定义一个1M的内存缓冲区,用于临时性存储接收到的消息 9 byte[] arrRecvmsg = new byte[1024 * 1024]; 10 11 //将客户端套接字接收到的数据存入内存缓冲区,并获取长度 12 int length = socketclient.Receive(arrRecvmsg); 13 14 //将套接字获取到的字符数组转换为人可以看懂的字符串 15 string strRevMsg = Encoding.UTF8.GetString(arrRecvmsg, 0, length); 16 17 this.Dispatcher.BeginInvoke(DispatcherPriority.Normal, 18 (ThreadStart)delegate() 19 { 20 MessagePool.Insert(MessagePool.Count, new Message() { MessageInfo = strRevMsg, IdentityName = socketclient.RemoteEndPoint.ToString() }); 21 }); 22 } 23 catch (Exception ex) 24 { 25 MessageBox.Show("远程服务器已经中断连接" + ex.Message); 26 break; 27 } 28 } 29 }
发送消息:
1 private void send_Click(object sender, RoutedEventArgs e) 2 { 3 string strRevMsg = this.message.Text; 4 this.message.Clear(); 5 6 lock (lockPool) 7 { 8 MessagePool.Insert(MessagePool.Count, new Message() { MessageInfo = strRevMsg, IdentityName = "我(" + socketclient.LocalEndPoint.ToString() + ")" }); 9 } 10 11 //将输入的内容字符串转换为机器可以识别的字节数组 12 byte[] arrClientSendMsg = Encoding.UTF8.GetBytes(strRevMsg); 13 //调用客户端套接字发送字节数组 14 socketclient.Send(arrClientSendMsg); 15 }
经过和同事的测试,可以实现简单的文字聊天,此代码仅为测试代码,若要用于实际的项目中,需要很大改进,比如socket异步建立,消息接收和发送等等。