c# TCP/IP协议利用Socket的简单通信
完全是基础,新手可以随意看看,大牛可以关闭浏览页了,哈哈。
TCP/IP协议
TCP/IP是一系列网络通信协议的统称,其中最核心的两个协议是TCP和IP。TCP称为传输控制协议,IP称为互联网络协议。
网络分层除了OSI模型分层,还有TCP/IP模型分层,将网络划分为四层,应用层、传输层、网际层、网络接口层。TCP/IP模型分层是OSI模型分层的浓缩版
OSI模型和TCP/IP网络分层的对应结构及TCP/IP部分协议族,
TCP三次握手
第一次握手:建立连接。客户端发送连接请求报文段,将SYN位置为1,Sequence Number为x;然后,客户端进入SYN_SEND状态,等待服务器的确认;
第二次握手:服务器收到SYN报文段。服务器收到客户端的SYN报文段,需要对这个SYN报文段进行确认,设置Acknowledgment Number为x+1(Sequence Number+1);同时,自己自己还要发送SYN请求信息,将SYN位置为1,Sequence Number为y;服务器端将上述所有信息放到一个报文段(即SYN+ACK报文段)中,一并发送给客户端,此时服务器进入SYN_RECV状态;
第三次握手:客户端收到服务器的SYN+ACK报文段。然后将Acknowledgment Number设置为y+1,向服务器发送ACK报文段,这个报文段发送完毕以后,客户端和服务器端都进入ESTABLISHED状态,完成TCP三次握手。
三次握手图解
在此基础上,socket连接过程:
服务器监听:服务器端socket并不定位具体的客户端socket,而是处于等待监听状态,实时监控网络状态。
客户端请求:客户端clientSocket发送连接请求,目标是服务器的serverSocket。为此,clientSocket必须知道serverSocket的地址和端口号,进行扫描发出连接请求。
连接确认:当服务器socket监听到或者是受到客户端socket的连接请求时,服务器就响应客户端的请求,建议一个新的socket,把服务器socket发送给客户端,一旦客户端确认连接,则连接建立。
注:在连接确认阶段:服务器socket即使在和一个客户端socket建立连接后,还在处于监听状态,仍然可以接收到其他客户端的连接请求,这也是一对多产生的原因。
socket连接原理知道了,此处编写最基本最简单的socket通信:
客户端代码:
/// <summary> /// 客户端发送与服务端的连接请求,创建与之通信用的Socket /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void btnLink_Click(object sender, EventArgs e) { try { //定义一个用于请求连接服务器的IP和端口号 IPEndPoint ip = new IPEndPoint(IPAddress.Parse(txtIp.Text), Convert.ToInt32(txtPort.Text));//txtIP:IP地址;txtPort:端口号 //定义一个套接字用于请求连接服务器,包含三个参数(IP4寻址协议,流式连接,Tcp协议) socket clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); //请求连接服务端 clientSocket.Connect(ip); //创建一个通信线程,避免阻塞主线程 Thread th = new Thread(CRecive); //设置为后台线程,随着主线程退出而退出 th.IsBackground = true; //启动线程 th.Start(clientSocket); //显示与服务端连接情况 ShowMsg($"{clientSocket.RemoteEndPoint.ToString()}:已连接"); } catch (Exception ex) { //提示套接字监听异常 ShowMsg($"网络错误"); } } /// <summary> /// 客户端不停的接收服务端发送过来的消息 /// </summary> /// <param name="o"></param> void CRecive(object o) { Socket socketSend = o as Socket; while (true) { try { //创建一个内存缓冲区,其大小为1024*1024字节 即1M byte[] buffer = new byte[1024 * 1024]; //将接收到的信息存入到内存缓冲区,并返回其字节数组的长度 int length = socketSend.Receive(buffer); if (length == 0) { ShowMsg($"{socketSend.RemoteEndPoint.ToString()}:已断开连接"); break; } //将机器接受到的字节数组转换为人可以读懂的字符串 string str = Encoding.UTF8.GetString(buffer, 0, length); //将发送的字符串信息附加到ShowMsg上 ShowMsg($"Ta({socketSend.RemoteEndPoint.ToString()}):{str}"); } catch (Exception ex) { } } }
客户端向服务端发送数据:
clientSocket.Send(Encoding.UTF8.GetBytes($"{txtWrite.Text}"));
服务端代码:
/// <summary> /// 开启监听,等待客户端连接 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void btnMonitor_Click(object sender, EventArgs e) { try { //定义一个套接字用于监听客户端发来的消息,包含三个参数(IP4寻址协议,流式连接,Tcp协议) socketWatch = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); //服务端发送信息需要一个IP地址和端口号 IPAddress ip = IPAddress.Parse(txtIp.Text);//IPAddress.Any; //将IP地址和端口号绑定到网络节点point上 IPEndPoint point = new IPEndPoint(ip, Convert.ToInt32(txtPort.Text)); //监听绑定的网络节点 socketWatch.Bind(point); ShowMsg("开始监听。。。"); //将套接字的监听队列长度限制为10 socketWatch.Listen(10); //负责监听客户端的线程:创建一个监听线程 Thread th = new Thread(SListen); //将窗体线程设置为与后台同步,随着主线程结束而结束 th.IsBackground = true; //启动线程 th.Start(socketWatch); } catch (Exception ex) { } } /// <summary> /// 等待客户端的连接 并且创建与之通信用的Socket /// </summary> /// <param name="o"></param> void SListen(object o) { Socket socketWatch = o as Socket; while (true) { try { //等待客户端的连接,并且创建与之通信用的Socket Socket serverSocket = socketWatch.Accept(); //获取客户端网络结点号 string socketIP = serverSocket.RemoteEndPoint.ToString(); ShowMsg($"{socketIP}:已连接"); //创建一个通信线程,避免阻塞主线程 Thread th = new Thread(SRecive); th.IsBackground = true; th.Start(serverSocket); } catch { } } } /// <summary> /// 服务端不停的接收客户端发送过来的消息 /// </summary> /// <param name="o"></param> void SRecive(object o) { Socket socketSend = o as Socket; while (true) { //将接收到的信息存入到内存缓冲区,并返回其字节数组的长度 try { //创建一个内存缓冲区,其大小为1024*1024字节 即1M byte[] buffer = new byte[1024 * 1024]; //接收客户端发送过来的消息 int length = socketSend.Receive(buffer); if (length == 0) { ShowMsg($"{socketSend.RemoteEndPoint.ToString()}:已断开连接"); break; } //将机器接受到的字节数组转换为人可以读懂的字符串 string str = Encoding.UTF8.GetString(buffer, 0, length); //将发送的字符串信息附加到ShowMsg上 ShowMsg($"Ta({socketSend.RemoteEndPoint.ToString()}):{str}"); } catch (Exception ex) { } } }
服务端向已连接的客户端发送数据:
serverSocket.Send(Encoding.UTF8.GetBytes($"{txtWrite.Text}"));
注:c#新手的随手笔记,方便以后学习和使用,如有不足和雷同,欢迎留言!谢谢!