1:什么是Socket
所谓套接字(Socket),就是对网络中不同主机上的应用进程之间进行双向通信的端点的抽象。
一个套接字就是网络上进程通信的一端,提供了应用层进程利用网络协议交换数据的机制。
从所处的地位来讲,套接字上联应用进程,下联网络协议栈,是应用程序通过网络协议进行通信的接口,是应用程序与网络协议根进行交互的接口。
2:客服端和服务端的通信简单流程
3:服务端Code:
1 using System; 2 using System.Collections.Generic; 3 using System.ComponentModel; 4 using System.Data; 5 using System.Drawing; 6 using System.Linq; 7 using System.Text; 8 using System.Threading.Tasks; 9 using System.Windows.Forms; 10 11 namespace ChartService 12 { 13 using System.Net; 14 using System.Net.Sockets; 15 using System.Threading; 16 using ChatCommoms; 17 using ChatModels; 18 19 public partial class ServiceForm : Form 20 { 21 Socket _socket; 22 private static List<ChatUserInfo> userinfo = new List<ChatUserInfo>(); 23 public ServiceForm() 24 { 25 InitializeComponent(); 26 27 } 28 29 private void btnServicStart_Click(object sender, EventArgs e) 30 { 31 try 32 { 33 string ip = textBox_ip.Text.Trim(); 34 string port = textBox_port.Text.Trim(); 35 if (string.IsNullOrWhiteSpace(ip) || string.IsNullOrWhiteSpace(port)) 36 { 37 MessageBox.Show("IP与端口不可以为空!"); 38 } 39 ServiceStartAccept(ip, int.Parse(port)); 40 } 41 catch (Exception) 42 { 43 MessageBox.Show("连接失败!或者ip,端口参数异常"); 44 } 45 } 46 public void ServiceStartAccept(string ip, int port) 47 { 48 Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); 49 IPEndPoint endport = new IPEndPoint(IPAddress.Parse(ip), port); 50 socket.Bind(endport); 51 socket.Listen(10); 52 Thread thread = new Thread(Recevice); 53 thread.IsBackground = true; 54 thread.Start(socket); 55 textboMsg.AppendText("服务开启ok..."); 56 } 57 58 /// <summary> 59 /// 开启接听服务 60 /// </summary> 61 /// <param name="obj"></param> 62 private void Recevice(object obj) 63 { 64 var socket = obj as Socket; 65 while (true) 66 { 67 string remoteEpInfo = string.Empty; 68 try 69 { 70 Socket txSocket = socket.Accept(); 71 _socket = txSocket; 72 if (txSocket.Connected) 73 { 74 remoteEpInfo = txSocket.RemoteEndPoint.ToString(); 75 textboMsg.AppendText($" {remoteEpInfo}:连接上线了..."); 76 var clientUser = new ChatUserInfo 77 { 78 UserID = Guid.NewGuid().ToString(), 79 ChatUid = remoteEpInfo, 80 ChatSocket = txSocket 81 }; 82 userinfo.Add(clientUser); 83 84 85 listBoxCoustomerList.Items.Add(new ChatUserInfoBase { UserID = clientUser.UserID, ChatUid = clientUser.ChatUid }); 86 listBoxCoustomerList.DisplayMember = "ChatUid"; 87 listBoxCoustomerList.ValueMember = "UserID"; 88 89 ReceseMsgGoing(txSocket, remoteEpInfo); 90 } 91 else 92 { 93 if (userinfo.Count > 0) 94 { 95 userinfo.Remove(userinfo.Where(c => c.ChatUid == remoteEpInfo).FirstOrDefault()); 96 //移除下拉框对于的socket或者叫用户 97 } 98 break; 99 } 100 } 101 catch (Exception) 102 { 103 if (userinfo.Count > 0) 104 { 105 userinfo.Remove(userinfo.Where(c => c.ChatUid == remoteEpInfo).FirstOrDefault()); 106 //移除下拉框对于的socket或者叫用户 107 } 108 } 109 } 110 111 } 112 113 /// <summary> 114 /// 接受来自客服端发来的消息 115 /// </summary> 116 /// <param name="txSocket"></param> 117 /// <param name="remoteEpInfo"></param> 118 private void ReceseMsgGoing(Socket txSocket, string remoteEpInfo) 119 { 120 121 //退到一个客服端的时候 int getlength = txSocket.Receive(recesiveByte); 有抛异常 122 Thread thread = new Thread(() => 123 { 124 while (true) 125 { 126 try 127 { 128 byte[] recesiveByte = new byte[1024 * 1024 * 4]; 129 int getlength = txSocket.Receive(recesiveByte); 130 if (getlength <= 0) { break; } 131 132 var getType = recesiveByte[0].ToString(); 133 string getmsg = Encoding.UTF8.GetString(recesiveByte, 1, getlength - 1); 134 ShowMsg(remoteEpInfo, getType, getmsg); 135 } 136 catch (Exception) 137 { 138 //string userid = userinfo.FirstOrDefault(c => c.ChatUid == remoteEpInfo)?.ChatUid; 139 listBoxCoustomerList.Items.Remove(remoteEpInfo); 140 userinfo.Remove(userinfo.FirstOrDefault(c => c.ChatUid == remoteEpInfo));//从集合中移除断开的socket 141 142 listBoxCoustomerList.DataSource = userinfo;//重新绑定下来的信息 143 listBoxCoustomerList.DisplayMember = "ChatUid"; 144 listBoxCoustomerList.ValueMember = "UserID"; 145 txSocket.Dispose(); 146 txSocket.Close(); 147 } 148 } 149 }); 150 thread.IsBackground = true; 151 thread.Start(); 152 153 } 154 155 private void ShowMsg(string remoteEpInfo, string getType, string getmsg) 156 { 157 textboMsg.AppendText($" {remoteEpInfo}:消息类型:{getType}:{getmsg}"); 158 } 159 private void Form1_Load(object sender, EventArgs e) 160 { 161 CheckForIllegalCrossThreadCalls = false; 162 this.textBox_ip.Text = "192.168.1.101";//初始值 163 this.textBox_port.Text = "50000"; 164 } 165 166 /// <summary> 167 /// 服务器发送消息,可以先选择要发送的一个用户 168 /// </summary> 169 /// <param name="sender"></param> 170 /// <param name="e"></param> 171 private void btnSendMsg_Click(object sender, EventArgs e) 172 { 173 var getmSg = textBoxSendMsg.Text.Trim(); 174 if (string.IsNullOrWhiteSpace(getmSg)) 175 { 176 MessageBox.Show("要发送的消息不可以为空", "注意"); return; 177 } 178 var obj = listBoxCoustomerList.SelectedItem; 179 int getindex = listBoxCoustomerList.SelectedIndex; 180 if (obj == null || getindex == -1) 181 { 182 MessageBox.Show("请先选择左侧用户的用户"); return; 183 } 184 var getChoseUser = obj as ChatUserInfoBase; 185 var sendMsg = ServiceSockertHelper.GetSendMsgByte(getmSg, ChatTypeInfoEnum.StringEnum); 186 userinfo.FirstOrDefault(c => c.ChatUid == getChoseUser.ChatUid)?.ChatSocket?.Send(sendMsg); 187 } 188 189 /// <summary> 190 /// 给所有登录的用户发送消息,群发了 191 /// </summary> 192 /// <param name="sender"></param> 193 /// <param name="e"></param> 194 private void button1_Click(object sender, EventArgs e) 195 { 196 var getmSg = textBoxSendMsg.Text.Trim(); 197 if (string.IsNullOrWhiteSpace(getmSg)) 198 { 199 MessageBox.Show("要发送的消息不可以为空", "注意"); return; 200 } 201 if (userinfo.Count <= 0) 202 { 203 MessageBox.Show("暂时没有客服端登录!"); return; 204 } 205 var sendMsg = ServiceSockertHelper.GetSendMsgByte(getmSg, ChatTypeInfoEnum.StringEnum); 206 foreach (var usersocket in userinfo) 207 { 208 usersocket.ChatSocket?.Send(sendMsg); 209 } 210 } 211 212 /// <summary> 213 /// 服务器给发送震动 214 /// </summary> 215 /// <param name="sender"></param> 216 /// <param name="e"></param> 217 private void btnSendSnak_Click(object sender, EventArgs e) 218 { 219 var obj = listBoxCoustomerList.SelectedItem; 220 int getindex = listBoxCoustomerList.SelectedIndex; 221 if (obj == null || getindex == -1) 222 { 223 MessageBox.Show("请先选择左侧用户的用户"); return; 224 } 225 var getChoseUser = obj as ChatUserInfoBase; 226 227 byte[] sendMsgByte = ServiceSockertHelper.GetSendMsgByte("", ChatTypeInfoEnum.Snake); 228 userinfo.FirstOrDefault(c => c.ChatUid == getChoseUser.ChatUid)?.ChatSocket.Send(sendMsgByte); 229 } 230 231 232 } 233 }
4:客服端Code:
1 using System; 2 using System.Collections.Generic; 3 using System.ComponentModel; 4 using System.Data; 5 using System.Drawing; 6 using System.Linq; 7 using System.Text; 8 using System.Threading.Tasks; 9 using System.Windows.Forms; 10 11 namespace ChatClient 12 { 13 using ChatCommoms; 14 using System.Net; 15 using System.Net.Sockets; 16 using System.Threading; 17 18 public partial class Form1 : Form 19 { 20 public Form1() 21 { 22 InitializeComponent(); 23 } 24 25 private void Form1_Load(object sender, EventArgs e) 26 { 27 CheckForIllegalCrossThreadCalls = false; 28 this.textBoxIp.Text = "192.168.1.101";//先初始化一个默认的ip等 29 this.textBoxPort.Text = "50000"; 30 } 31 32 Socket clientSocket; 33 /// <summary> 34 /// 客服端连接到服务器 35 /// </summary> 36 /// <param name="sender"></param> 37 /// <param name="e"></param> 38 private void btnServicStart_Click(object sender, EventArgs e) 39 { 40 try 41 { 42 var ipstr = textBoxIp.Text.Trim(); 43 var portstr = textBoxPort.Text.Trim(); 44 if (string.IsNullOrWhiteSpace(ipstr) || string.IsNullOrWhiteSpace(portstr)) 45 { 46 MessageBox.Show("要连接的服务器ip和端口都不可以为空!"); 47 return; 48 } 49 clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); 50 clientSocket.Connect(IPAddress.Parse(ipstr), int.Parse(portstr)); 51 labelStatus.Text = "连接到服务器成功...!"; 52 ReseviceMsg(clientSocket); 53 54 } 55 catch (Exception) 56 { 57 MessageBox.Show("请检查要连接的服务器的参数"); 58 } 59 } 60 private void ReseviceMsg(Socket clientSocket) 61 { 62 63 Thread thread = new Thread(() => 64 { 65 while (true) 66 { 67 try 68 { 69 Byte[] byteContainer = new Byte[1024 * 1024 * 4]; 70 int getlength = clientSocket.Receive(byteContainer); 71 if (getlength <= 0) 72 { 73 break; 74 } 75 var getType = byteContainer[0].ToString(); 76 string getmsg = Encoding.UTF8.GetString(byteContainer, 1, getlength - 1); 77 78 GetMsgFomServer(getType, getmsg); 79 } 80 catch (Exception ex) 81 { 82 } 83 } 84 }); 85 thread.IsBackground = true; 86 thread.Start(); 87 88 } 89 90 private void GetMsgFomServer(string strType, string msg) 91 { 92 this.textboMsg.AppendText($" 类型:{strType};{msg}"); 93 } 94 95 /// <summary> 96 /// 文字消息的发送 97 /// </summary> 98 /// <param name="sender"></param> 99 /// <param name="e"></param> 100 private void btnSendMsg_Click(object sender, EventArgs e) 101 { 102 var msg = textBoxSendMsg.Text.Trim(); 103 var sendMsg = ServiceSockertHelper.GetSendMsgByte(msg, ChatModels.ChatTypeInfoEnum.StringEnum); 104 int sendMsgLength = clientSocket.Send(sendMsg); 105 } 106 } 107 }
5:测试效果:
6:完整Code GitHUb下载路径 https://github.com/zrf518/WinformSocketChat.git
7:这个只是一个简单的聊天练习Demo,待进一步完善(实现部分功能,传递的消息byte[0]为消息的类型,用来判断是文字,还是图片等等),欢迎大家指教