基于c# TCP协议(一)
2021/12/5 11:16:45
本文主要是介绍基于c# TCP协议(一),对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
TCP整体的操作流程分为3步:1.建立连接;2.传输数据;3.断开连接;
一 建立连接:
1.1 连接前的校验---三次握手
1.1.1什么是三次握手
三次握手实在TCP/IP网络中用于服务器和客户端之间建立连接的过程。这是一个三步过程,要求客户端和服务器在真正的数据通信过程开始之前交 换同步和确认包。
三次握手过程的设计方式是,两端可以帮助您同时发起,协商和分离TCP套接字连接。它允许您同时在两个方向上传输多个TCP套接字连接。
1.1.2 TCP 握手时消息类型
消息类型 |
描述 |
Syn |
用于发起和建立连接,帮助您在设备之间同步序列号 |
ACK |
有助于向对方确认它已经收到SYN |
SYN-ACK |
来自本地设备的SYN消息和较早数据包的ACK |
FIN |
用于终止连接 |
1.1.3 三次握手的过程
TCP 已三次握手开始。在这个握手过程中,客户端需要通过请求与服务器的通信会话来发起会话:
1. 客户端与服务器建立连接。它发送一个带有SYN的数据段,并通知服务器,客户端开始通信,以及它的序列号是什么
2. 在此步骤中,服务器使用 SYN-ACK 信号集响应客户端请求。 ACK 帮助您表示收到的段的响应,SYN 表示它应该能够以段开头的序列号。
3. 在此步骤中,客户端确认服务器的响应,并且它们都创建稳定的连接将开始实际的数据传输过程。
1.1.4 使用示例
上面是一个简单的三次握手过程示例,它由三个步骤组成:
- 主机 X 通过向其服务器发送 SYN数据包来开始连接。数据包包含一个随机序列号(例如,4321),它指示主机 X 应传输的数据的序列号的开头。
- 之后,服务器将收到数据包,并以它的序列号响应。它的响应还包括确认号,即主机 X 的序列号加 1(这里是 4322)。
- 主机 X 通过发送确认号来响应服务器,该确认号主要是服务器的序列号,递增 1。
1.1.5 代码示例
(1)创建Socket实例
Socket serverSocket(/clientSocket) = new Socket(AddressFamily.InterNetWork,SocketType.Stream,ProtocolType.Tcp);
(2)Bind() 绑定和Connect()连接
服务端:使用Bind()方法来绑定IPEndPoint对象(表示一个特定的IP地址和端口的组合的IPEndPoint实例)
1.定义侦听端口:
IPEndPoint ipEnd = new IPEndPoint(IPAdress.Parse(ip),port);
ip:为服务器本机ip
Port:为服务器自定义端口号
2. 将套接字与IPEndPoint绑定:
serverSocket.Bind(ipEnd);
客户端:使用Connect()方法通过服务端的IP和端口号发送连接请求
String serverIp=”196.168.0.1.1”;
Int serverPort=5580;
IpEndPoint serveripEndPoint=new IpEndPoint(IPAdress.Parse(serverIp), serverPort);
clientSocket.Connect(serveripEndPoint);
serverIp:为服务器本机ip
serverPort:为服务器自定义端口号
(3)Listen()
服务端:使用Listen开始监听所有发送到服务端的连接请求,服务端使用,客户端不需要
int backlog=100;
serverSocket.Listen(backlog);
注:backlog:指定可排队等待接受的传入连接数量,即挂起的连接队列的最大长度
二 传输数据
2.1 服务端
前置代码:
Socket serverConnect= serverSocket.Accept();à获取服务端与客户端通讯的连接句柄
2.1.1服务端Receive 数据
创建一个内存缓冲区,其大小为1024*1024字节,即1M
Byte[] arrServerRecMsg=new byte[1024*1024];
将接受的数据存入到内存缓冲区,并返回其字节数组的长度
Int Length= serverConnect.Receive(arrServerRecMsg);
将机器接受的的字节数组转换为人可以读懂的的字符串
String strSRecMsg=Encoding.UTF8.GetString(arrServerRecMsg,0,Length);
2.1.2服务端 Send 数据
String msg=””;
serverConnect.Send(Encoding.UTF8.GetBytes(msg));
2.2 客户端
2.2.1客户端Receive 数据
定义一个1M的内存缓冲区,用于临时性存储接收到的消息
Byte[] arrRecvmsg=new byte[1024*1024];
将客户端接收到的消息存入内存缓冲区
Int Length= clientSocket.Receive(arrRecvmsg);
将客户端接收到的数据转换为UTF-8协议的字符串
Stirng strRevMsg=Encoding.UTF8.GetString(arrRecvmsg,0, Length);
2.2.2客户端Send 数据
将输入字符串转换为字节数组(byte[])
String ClientSendMsg=””;
byte[] arrClientSenMsg=Encoding.UTF8.GetBytes(ClientSendMsg);
客户端
客户端发送字节数组
clientSocket.Send(arrClientSenMsg);
三 断开连接
3.1 断开连接前的操作---四次握手
四 不同情景下 TCP通讯的问题
一. 多个不同客户端给服务端发送连接请求,服务端怎样识别不同客户端的连接呢?
回答1:连接由 4 元组标识,该 4 元组由源和目标端口以及源和目标 IP 地址组成。因此,如果两台不同的机器连接到第三台机器上的同一个端口,就会有两个不同的连接,因为源 IP 不同。如果同一台机器(或 NAT 后面的两台机器或以其他方式共享相同 IP 地址的机器)两次连接到单个远程端,则连接由源端口(通常是随机的高编号端口)区分。
简单地说,如果我从我的客户端连接到同一个 Web 服务器两次,从我的角度来看,这两个连接将具有不同的源端口和来自 Web 服务器的目标端口。因此没有歧义,即使两个连接具有相同的源 IP 地址和目标 IP 地址。
端口是一种多路复用IP 地址的方式,以便不同的应用程序可以侦听相同的 IP 地址/协议对。除非应用程序定义自己的更高级别的协议,否则无法复用端口。如果使用相同协议的两个连接同时具有相同的源和目标 IP 以及相同的源和目标端口,则它们必须是相同的连接。
回答2:
首先记住以下两条规则:
- 套接字的主键:套接字由{SRC-IP, SRC-PORT, DEST-IP, DEST-PORT, PROTOCOL}not标识{SRC-IP, SRC-PORT, DEST-IP, DEST-PORT}- 协议是套接字定义的重要部分。
- 操作系统进程和套接字映射:一个进程可以与(可以打开/可以侦听)多个套接字相关联,这对许多读者来说可能是显而易见的。
示例 1:连接到同一服务器端口的两个客户端意味着:socket1 {SRC-A, 100, DEST-X,80, TCP}和socket2{SRC-B, 100, DEST-X,80, TCP}。这意味着主机 A 连接到服务器 X 的端口 80,另一台主机 B 也连接到同一个服务器 X 到同一个端口 80。 现在,服务器如何处理这两个套接字取决于服务器是单线程还是多线程(我会稍后解释)。重要的是一台服务器可以同时侦听多个套接字。
要回答原始问题:
无论是有状态协议还是无状态协议,两个客户端都可以连接到同一个服务器端口,因为我们可以为每个客户端分配不同的套接字(因为客户端 IP 肯定会不同)。同一个客户端也可以有两个套接字连接到同一个服务器端口 - 因为这些套接字有SRC-PORT.公平地说,“Borealid”基本上提到了相同的正确答案,但对无状态/完整的引用有点不必要/令人困惑。
回答关于服务器如何知道要回答哪个套接字的问题的第二部分。首先要了解,对于侦听同一端口的单个服务器进程,可能有多个套接字(可能来自同一客户端或来自不同客户端)。现在只要服务器知道哪个请求与哪个套接字相关联,它就可以始终使用相同的套接字响应适当的客户端。因此,除了客户端最初尝试连接的原始端口之外,服务器永远不需要在其自己的节点中打开另一个端口。如果任何服务器在绑定套接字后分配不同的服务器端口,那么在我看来,服务器正在浪费它的资源,它必须需要客户端再次连接到分配的新端口。
为完整性多一点:
示例 2:这是一个非常有趣的问题:“服务器上的两个不同进程是否可以侦听同一个端口”。如果您不将协议视为定义套接字的参数之一,那么答案是否定的。这是因为我们可以说在这种情况下,尝试连接到服务器端口的单个客户端将没有任何机制来提及客户端打算连接到两个侦听进程中的哪一个。这与规则 (2) 断言的主题相同。然而,这是错误的答案,因为“协议”也是套接字定义的一部分。因此,只有在使用不同协议的情况下,同一节点中的两个进程才能侦听同一端口。例如,两个不相关的客户端(比如一个使用 TCP,另一个使用 UDP)可以连接并通信到同一个服务器节点和同一个端口,但它们必须由两个不同的服务器进程提供服务。
服务器类型 - 单个和多个:
当服务器进程侦听端口时,这意味着多个套接字可以同时连接同一个服务器进程并与之通信。如果服务器仅使用一个子进程来为所有套接字提供服务,则该服务器称为单进程/线程,如果服务器使用多个子进程为一个子进程提供每个套接字服务,则该服务器称为多进程进程/线程服务器。请注意,无论服务器的类型如何,服务器都可以/应该始终使用相同的初始套接字来响应(无需分配另一个服务器端口)。
2.TCP/HTTP 监听端口:如何让多个用户共享同一个端口
必须明白三件事:
1.) 在服务器上,一个进程正在监听一个端口。一旦它获得连接,它就会把它交给另一个线程。通信永远不会占用侦听端口。
2.) 连接由操作系统通过以下 5 元组唯一标识:(本地 IP、本地端口、远程 IP、远程端口、协议)。如果元组中的任何元素不同,那么这是一个完全独立的连接。
3.) 当客户端连接到服务器时,它会随机选择一个未使用的高阶源端口。通过这种方式,单个客户端最多可以有约 64k 连接到服务器的同一目标端口。
二.服务端与多个客户端通讯时,怎样指定客户端呢?
三.TCP通讯,怎样解决数据的粘包,拆包问题?
...
剩余问题会在后续博文上继续学习
这篇关于基于c# TCP协议(一)的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2022-03-01沐雪多租宝商城源码从.NetCore3.1升级到.Net6的步骤
- 2024-12-06使用Microsoft.Extensions.AI在.NET中生成嵌入向量
- 2024-11-18微软研究:RAG系统的四个层次提升理解与回答能力
- 2024-11-15C#中怎么从PEM格式的证书中提取公钥?-icode9专业技术文章分享
- 2024-11-14云架构设计——如何用diagrams.net绘制专业的AWS架构图?
- 2024-05-08首个适配Visual Studio平台的国产智能编程助手CodeGeeX正式上线!C#程序员必备效率神器!
- 2024-03-30C#设计模式之十六迭代器模式(Iterator Pattern)【行为型】
- 2024-03-29c# datetime tryparse
- 2024-02-21list find index c#
- 2024-01-24convert toint32 c#