Socket连接
Socket使用TCP协议,TCP是一种面向连接的、可靠的,基于字节流的传输层通信协议。为两台主机提供高可靠性的数据通信服务。它可以将源主机的数据无差错地传输到目标主机。当有数据要发送时,对应用进程送来的数据进行分片,以适合于在网络层中传输;当接收到网络层传来的分组时,它要对收到的分组进行确认,还要对丢失的分组设置超时重发等。为此TCP需要增加额外的许多开销,以便在数据传输过程中进行一些必要的控制,确保数据的可靠传输。因此,TCP传输的效率比较低。一旦通信双方建立了TCP连接,连接中的任何一方都能向对方发送数据和接收对方发来的数据。TCP协议负责把用户数据(字节流)按一定的格式和长度组成多个数据报进行发送,并在接收到数据报之后按分解顺序重新组装和恢复用户数据。利用TCP传输数据时,数据是以字节流的形式进行传输的。
TCP是面向连接的协议,TCP协议通过三个报文段完成类似电话呼叫的连接建立过程,这个过程称为三次握手
第一次握手:建立连接时,客户端发送SYN包(SEQ=x)到服务器,并进入SYN_SEND状态,等待服务器确认。
第二次握手:服务器收到SYN包,必须确认客户的SYN(ACK=x+1),同时自己也发送一个SYN包(SEQ=y),即SYN+ACK包,此时服务器进入 SYN_RECV状态。
第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ACK=y+1),此包发送完毕,客户端和服务器进入Established状态,完成三次握手。
建立一个连接需要三次握手,而终止一个连接要经过四次握手,这是由TCP的半关闭造成的。
TCP是以全双工方式运输,一对一通信。
Socket的基本通信:
服务器端:
1、创建用于监听的Socket对象,创建用于通信的Socket对象
2、使用监听的Socket对象的Bind方法与IP和端口进行绑定,再通过Socket对象的Listen方法进行监听
3、将通信的Socket对象使用Accept方法创建新连接
4、通过Socket对象的Send方法和Recieve方法进行通讯
5、通信结束后关闭Socket
客户端:
1、创建用于通信的Socket对象
2、Socket对象使用Connect方法创建一个和IP与端口的连接
3、通过该Socket对象的Send方法和Recieve方法进行通讯
4、通信结束后关闭Socket

1 using System; 2 using System.Collections.Generic; 3 using System.ComponentModel; 4 using System.Data; 5 using System.Drawing; 6 using System.IO; 7 using System.Linq; 8 using System.Net; 9 using System.Net.Sockets; 10 using System.Text; 11 using System.Threading; 12 using System.Threading.Tasks; 13 using System.Windows.Forms; 14 15 namespace Server 16 { 17 public partial class SocketServer : Form 18 { 19 public SocketServer() 20 { 21 InitializeComponent(); 22 } 23 //定义回调:解决跨线程访问问题 24 private delegate void SetTextValueCallBack(string strValue); 25 //定义接收客户端发送消息的回调 26 private delegate void ReceiveMsgCallBack(string strReceive); 27 //定义回调:给ComboBox控件添加元素 28 private delegate void SetCmbCallBack(string strItem); 29 //定义发送文件的回调 30 private delegate void SendFileCallBack(byte[] bf); 31 //声明回调 32 private SetTextValueCallBack setCallBack; 33 //声明 34 private ReceiveMsgCallBack receiveCallBack; 35 //声明 36 private SetCmbCallBack setCmbCallBack; 37 //声明 38 private SendFileCallBack sendCallBack; 39 40 //用于通信的Socket 41 Socket socketSend; 42 //用于监听的SOCKET 43 Socket socketWatch; 44 45 //将远程连接的客户端的IP地址和Socket存入集合中 46 Dictionary<string, Socket> dicSocket = new Dictionary<string, Socket>(); 47 48 //创建监听连接的线程 49 Thread AcceptSocketThread; 50 //接收客户端发送消息的线程 51 Thread threadReceive; 52 53 /// <summary> 54 /// 开始监听 55 /// </summary> 56 /// <param name="sender"></param> 57 /// <param name="e"></param> 58 private void btn_Start_Click(object sender, EventArgs e) 59 { 60 //当点击开始监听的时候 在服务器端创建一个负责监听IP地址和端口号的Socket 61 socketWatch = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); 62 //获取ip地址 63 IPAddress ip = IPAddress.Parse(this.txt_IP.Text.Trim()); 64 //创建端口号 65 IPEndPoint point = new IPEndPoint(ip, Convert.ToInt32(this.txt_Port.Text.Trim())); 66 //绑定IP地址和端口号 67 socketWatch.Bind(point); 68 this.txt_Log.AppendText("监听成功" + " \r \n"); 69 //开始监听:设置最大可以同时连接多少个请求 70 socketWatch.Listen(10); 71 72 //实例化回调 73 setCallBack = new SetTextValueCallBack(SetTextValue); 74 receiveCallBack = new ReceiveMsgCallBack(ReceiveMsg); 75 setCmbCallBack = new SetCmbCallBack(AddCmbItem); 76 sendCallBack = new SendFileCallBack(SendFile); 77 78 //创建线程 79 AcceptSocketThread = new Thread(new ParameterizedThreadStart(StartListen)); 80 AcceptSocketThread.IsBackground = true; 81 AcceptSocketThread.Start(socketWatch); 82 } 83 84 /// <summary> 85 /// 等待客户端的连接,并且创建与之通信用的Socket 86 /// </summary> 87 /// <param name="obj"></param> 88 private void StartListen(object obj) 89 { 90 Socket socketWatch = obj as Socket; 91 while (true) 92 { 93 //等待客户端的连接,并且创建一个用于通信的Socket 94 socketSend = socketWatch.Accept(); 95 //获取远程主机的ip地址和端口号 96 string strIp = socketSend.RemoteEndPoint.ToString(); 97 dicSocket.Add(strIp, socketSend); 98 this.cmb_Socket.Invoke(setCmbCallBack, strIp); 99 string strMsg = "远程主机:" + socketSend.RemoteEndPoint + "连接成功"; 100 //使用回调 101 txt_Log.Invoke(setCallBack, strMsg); 102 103 //定义接收客户端消息的线程 104 Thread threadReceive = new Thread(new ParameterizedThreadStart(Receive)); 105 threadReceive.IsBackground = true; 106 threadReceive.Start(socketSend); 107 108 } 109 } 110 111 112 113 /// <summary> 114 /// 服务器端不停的接收客户端发送的消息 115 /// </summary> 116 /// <param name="obj"></param> 117 private void Receive(object obj) 118 { 119 Socket socketSend = obj as Socket; 120 while (true) 121 { 122 //客户端连接成功后,服务器接收客户端发送的消息 123 byte[] buffer = new byte[2048]; 124 //实际接收到的有效字节数 125 int count = socketSend.Receive(buffer); 126 if (count == 0)//count 表示客户端关闭,要退出循环 127 { 128 break; 129 } 130 else 131 { 132 string str = Encoding.Default.GetString(buffer, 0, count); 133 string strReceiveMsg = "接收:" + socketSend.RemoteEndPoint + "发送的消息:" + str; 134 txt_Log.Invoke(receiveCallBack, strReceiveMsg); 135 } 136 } 137 } 138 139 /// <summary> 140 /// 回调委托需要执行的方法 141 /// </summary> 142 /// <param name="strValue"></param> 143 private void SetTextValue(string strValue) 144 { 145 this.txt_Log.AppendText(strValue + " \r \n"); 146 } 147 148 149 private void ReceiveMsg(string strMsg) 150 { 151 this.txt_Log.AppendText(strMsg + " \r \n"); 152 } 153 154 private void AddCmbItem(string strItem) 155 { 156 this.cmb_Socket.Items.Add(strItem); 157 } 158 159 private void SendFile(byte[] sendBuffer) 160 { 161 162 try 163 { 164 dicSocket[cmb_Socket.SelectedItem.ToString()].Send(sendBuffer, SocketFlags.None); 165 } 166 catch (Exception ex) 167 { 168 MessageBox.Show("发送文件出错:" + ex.Message); 169 } 170 } 171 172 /// <summary> 173 /// 服务器给客户端发送消息 174 /// </summary> 175 /// <param name="sender"></param> 176 /// <param name="e"></param> 177 private void btn_Send_Click(object sender, EventArgs e) 178 { 179 try 180 { 181 string strMsg = this.txt_Msg.Text.Trim(); 182 byte[] buffer = Encoding.Default.GetBytes(strMsg); 183 List<byte> list = new List<byte>(); 184 list.Add(0); 185 list.AddRange(buffer); 186 //将泛型集合转换为数组 187 byte[] newBuffer = list.ToArray(); 188 //获得用户选择的IP地址 189 string ip = this.cmb_Socket.SelectedItem.ToString(); 190 dicSocket[ip].Send(newBuffer); 191 } 192 catch (Exception ex) 193 { 194 MessageBox.Show("给客户端发送消息出错:" + ex.Message); 195 } 196 //socketSend.Send(buffer); 197 } 198 199 /// <summary> 200 /// 选择要发送的文件 201 /// </summary> 202 /// <param name="sender"></param> 203 /// <param name="e"></param> 204 private void btn_Select_Click(object sender, EventArgs e) 205 { 206 OpenFileDialog dia = new OpenFileDialog(); 207 //设置初始目录 208 dia.InitialDirectory = @""; 209 dia.Title = "请选择要发送的文件"; 210 //过滤文件类型 211 dia.Filter = "所有文件|*.*"; 212 dia.ShowDialog(); 213 //将选择的文件的全路径赋值给文本框 214 this.txt_FilePath.Text = dia.FileName; 215 } 216 217 /// <summary> 218 /// 发送文件 219 /// </summary> 220 /// <param name="sender"></param> 221 /// <param name="e"></param> 222 private void btn_SendFile_Click(object sender, EventArgs e) 223 { 224 List<byte> list = new List<byte>(); 225 //获取要发送的文件的路径 226 string strPath = this.txt_FilePath.Text.Trim(); 227 using (FileStream sw = new FileStream(strPath, FileMode.Open, FileAccess.Read)) 228 { 229 byte[] buffer = new byte[2048]; 230 int r = sw.Read(buffer, 0, buffer.Length); 231 list.Add(1); 232 list.AddRange(buffer); 233 234 byte[] newBuffer = list.ToArray(); 235 //发送 236 //dicSocket[cmb_Socket.SelectedItem.ToString()].Send(newBuffer, 0, r+1, SocketFlags.None); 237 btn_SendFile.Invoke(sendCallBack, newBuffer); 238 239 240 } 241 242 } 243 244 private void btn_Shock_Click(object sender, EventArgs e) 245 { 246 byte[] buffer = new byte[1] { 2 }; 247 dicSocket[cmb_Socket.SelectedItem.ToString()].Send(buffer); 248 } 249 250 /// <summary> 251 /// 停止监听 252 /// </summary> 253 /// <param name="sender"></param> 254 /// <param name="e"></param> 255 private void btn_StopListen_Click(object sender, EventArgs e) 256 { 257 socketWatch.Close(); 258 socketSend.Close(); 259 //终止线程 260 AcceptSocketThread.Abort(); 261 threadReceive.Abort(); 262 } 263 } 264 }

1 using System; 2 using System.Collections.Generic; 3 using System.ComponentModel; 4 using System.Data; 5 using System.Drawing; 6 using System.IO; 7 using System.Linq; 8 using System.Net; 9 using System.Net.Sockets; 10 using System.Text; 11 using System.Threading; 12 using System.Threading.Tasks; 13 using System.Windows.Forms; 14 15 namespace SocketTest 16 { 17 public partial class SocketClient : Form 18 { 19 public SocketClient() 20 { 21 InitializeComponent(); 22 } 23 //定义回调 24 private delegate void SetTextCallBack(string strValue); 25 //声明 26 private SetTextCallBack setCallBack; 27 28 //定义接收服务端发送消息的回调 29 private delegate void ReceiveMsgCallBack(string strMsg); 30 //声明 31 private ReceiveMsgCallBack receiveCallBack; 32 33 //创建连接的Socket 34 Socket socketSend; 35 //创建接收客户端发送消息的线程 36 Thread threadReceive; 37 38 /// <summary> 39 /// 连接 40 /// </summary> 41 /// <param name="sender"></param> 42 /// <param name="e"></param> 43 private void btn_Connect_Click(object sender, EventArgs e) 44 { 45 try 46 { 47 socketSend = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); 48 IPAddress ip = IPAddress.Parse(this.txt_IP.Text.Trim()); 49 socketSend.Connect(ip, Convert.ToInt32(this.txt_Port.Text.Trim())); 50 //实例化回调 51 setCallBack = new SetTextCallBack(SetValue); 52 receiveCallBack = new ReceiveMsgCallBack(SetValue); 53 this.txt_Log.Invoke(setCallBack, "连接成功"); 54 55 //开启一个新的线程不停的接收服务器发送消息的线程 56 threadReceive = new Thread(new ThreadStart(Receive)); 57 //设置为后台线程 58 threadReceive.IsBackground = true; 59 threadReceive.Start(); 60 } 61 catch (Exception ex) 62 { 63 MessageBox.Show("连接服务端出错:" + ex.ToString()); 64 } 65 } 66 67 /// <summary> 68 /// 接口服务器发送的消息 69 /// </summary> 70 private void Receive() 71 { 72 try 73 { 74 while (true) 75 { 76 byte[] buffer = new byte[2048]; 77 //实际接收到的字节数 78 int r = socketSend.Receive(buffer); 79 if (r == 0) 80 { 81 break; 82 } 83 else 84 { 85 //判断发送的数据的类型 86 if (buffer[0] == 0)//表示发送的是文字消息 87 { 88 string str = Encoding.Default.GetString(buffer, 1, r - 1); 89 this.txt_Log.Invoke(receiveCallBack, "接收远程服务器:" + socketSend.RemoteEndPoint + "发送的消息:" + str); 90 } 91 //表示发送的是文件 92 if (buffer[0] == 1) 93 { 94 SaveFileDialog sfd = new SaveFileDialog(); 95 sfd.InitialDirectory = @""; 96 sfd.Title = "请选择要保存的文件"; 97 sfd.Filter = "所有文件|*.*"; 98 sfd.ShowDialog(this); 99 100 string strPath = sfd.FileName; 101 using (FileStream fsWrite = new FileStream(strPath, FileMode.OpenOrCreate, FileAccess.Write)) 102 { 103 fsWrite.Write(buffer, 1, r - 1); 104 } 105 106 MessageBox.Show("保存文件成功"); 107 } 108 } 109 110 111 } 112 } 113 catch (Exception ex) 114 { 115 MessageBox.Show("接收服务端发送的消息出错:" + ex.ToString()); 116 } 117 } 118 119 120 private void SetValue(string strValue) 121 { 122 this.txt_Log.AppendText(strValue + "\r \n"); 123 } 124 125 /// <summary> 126 /// 客户端给服务器发送消息 127 /// </summary> 128 /// <param name="sender"></param> 129 /// <param name="e"></param> 130 private void btn_Send_Click(object sender, EventArgs e) 131 { 132 try 133 { 134 string strMsg = this.txt_Msg.Text.Trim(); 135 byte[] buffer = new byte[2048]; 136 buffer = Encoding.Default.GetBytes(strMsg); 137 int receive = socketSend.Send(buffer); 138 } 139 catch (Exception ex) 140 { 141 MessageBox.Show("发送消息出错:" + ex.Message); 142 } 143 } 144 145 private void SocketClient_Load(object sender, EventArgs e) 146 { 147 Control.CheckForIllegalCrossThreadCalls = false; 148 } 149 150 /// <summary> 151 /// 断开连接 152 /// </summary> 153 /// <param name="sender"></param> 154 /// <param name="e"></param> 155 private void btn_CloseConnect_Click(object sender, EventArgs e) 156 { 157 //关闭socket 158 socketSend.Close(); 159 //终止线程 160 threadReceive.Abort(); 161 } 162 } 163 }
服务器端示图
客户端示图
参考原文:点这里
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 快收藏!一个技巧从此不再搞混缓存穿透和缓存击穿
· Blazor Hybrid适配到HarmonyOS系统
· 支付宝 IoT 设备入门宝典(下)设备经营篇
· 万字调研——AI生成内容检测
· 解决跨域问题的这6种方案,真香!