C# Socket网络编程(三)

目录

Socket/TCP

TCP报文格式

TCP三次握手

四次挥手

三次握手和四次挥手面试问题

Socket编程

Socket编程方式

数据传输方式

服务器编写步骤

客户端编写步骤


Socket/TCP

TCP报文格式

TCP是一种协议

报文:报纸文字

TCP报文是发送网络消息需要按照这种报文的格式去包装数据

例如:

TCP规定的数据包格式:

亲爱的[xxx],你好,[XXXXX],保重勿念![xxxx]年[xx]月[xx]日。

那么按照TCP发送一句话:“I Love You”,则这句话必须按照上面的协议类型包装

亲爱的[小明],你好,[I Love You],保重勿念![2020]年[03]月[27]日。

C# Socket网络编程(三)

 

一般需要了解一下几个字段:

  1. 序号:Seq序号,占32位,用来表示从TCP源端向目的端发送的字节流,发起方发送数据时对此进行标识

  2. 确认序号:ACK序号,占32位,只有ACK标志位为1时,确认序号字段才有效,ACK=Seq+1

  3. 标志位共六个:URG、ACK、PSH、RST、SYN、FIN含义:

    1. URG:紧急指针

    2. ACK:确认序号有效

    3. PSH:接收方应该尽快将这个报文交给应用层

    4. RST:充值连接

    5. SYN:发起一个新连接

    6. FIN:释放一个连接

  4. 需要注意的是:

    (A)不要将确认序号Ack与标志位中的ACK搞混了。 (B)确认方Ack=发起方Req+1,两端配对

TCP三次握手

所谓三次握手(Three-Way Handshake)即建立TCP连接,就是指建立一个TCP连接时,需要客户端和服务端总共发送3个包以确认连接的建立。在socket编程中,这一过程由客户端执行connect来触发,整个流程如下图所示:

C# Socket网络编程(三)

第一次握手:Client将标志位SYN置为1,随机产生一个值seq=J,并将该数据包发送给Server,Client进入SYN_SEND状态,等待Server确认。

第二次握手:Server收到数据包后由标志位SYN=1知道Client请求建立连接,Server将标志位SYN和ACK都置为1,ACK=J+1,随机产生一个seq=K,并将该数据包发送给Client以确认连接请求,Server进入SYN_RECV状态。

第三次握手:Client收到确认后,检查ACK是否为J+1,ACK是否为1,如果正确则将标志位ACK置为1,ACK=K+1,并将数据包发送给Server,Server检查ACK是否为K+1,如果正确则连接建立成功,Client和Server进入ESTABLISHED状态,完成三次握手,随后Client与Server之间就可以开始传输数据了。

SYN攻击:在三次握手过程中,Server发送SYN-ACK后,收到Client的ACK之前的TCP连接称为半连接,此时Serve处于SYN_RECV状态,当收到ACK后,Server转入ESTABLISHED状态。SYN攻击就是Client在短时间内伪造大量不存在的IP地址,并向Server不断的发送SYN包,Server回复确认包,并等待Client的确认,由于源地址不存在,因此Server需要不断重发直至超时,这些伪造的SYN包将长时间占用未连接队列,导致正常的SYN请求因为队列满而被丢弃,从而引起网络阻塞甚至系统瘫痪。SYN攻击就是一种典型的DDOS攻击,检测SYN攻击方式也很简单,即当有大量半连接状态且源地址是随机的,则可以断定遭到SYN攻击了,使用如下命令让其无处可逃:netstat -nap|grep SYN_RECV

四次挥手

所谓四次挥手即终止TCP连接,就是指断开一个TCP连接时,需要客户端和服务端总发送三个包以确认连接的断开。在Socket编程中,这一过程由客户端或服务端任一方执行close来触发,流程如下:

C# Socket网络编程(三)

由于TCP连接是全双工的,因此每个方向都必须要单独进行关闭,这一原则是当一方完成数据发送任务后,发送一个FIN来终止这一方向的连接,收到一个FIN只是意味着这一方向上没有数据流动了,即不会再收到数据了,但是在这个TCP连接上仍然能够发送数据,直到这一方向也发送了FIN。首先进行关闭的一方将执行主动关闭,而另一方则执行被动关闭。

(1)第一次挥手:Client发送一个FIN,用来关闭Client到Server的数据传输,Client进入FIN_WAIT_1状态。

(2)第二次挥手:Server收到FIN后,发送一个ACK给Client,确认序号为收到序号+1(与SYN相同,一个FIN占用一个序号),Server进入CLOSE_WAIT状态

(3)第三次挥手:Server发送一个FIN,用来关闭Server到Client的数据传输,Server进入LAST_ACK状态

(4)第四次挥手:Client收到FIN后,Client进入TIME_WAIT状态,接着发送一个ACK给Server,确认序号为收到序号+1,Server进入CLOSED状态,完成四次挥手

三次握手和四次挥手面试问题

(1)为什么建立连接协议是三次握手,而关闭连接却是四次握手呢?

这是因为Server在LISTEN状态下,当收到建立连接请求的SYN报文后,把ACK和SYN放在一个报文里发送给客户端。而关闭连接时,当收到对方的FIN报文时,仅仅表示对方不在发送数据了但是还能接受数据,己方也未必全部数据都发送给对方,所以己方可以立即close,也可以发送一些数据给对方后,再发送FIN报文给对方表示同意现在关闭连接,因此己方ACK和FIN一般都会分开发送。

(2)为什么TIME_WAIT状态还需要等2MSL后才能返回到CLOSED状态?

这是因为虽然双方都同意关闭连接了,而且握手的四个报文也都协调和发送完毕,按理可以直接回到CLOSED状态(就好比从SYN_SEND状态到ESTABLISHED状态那样),但是因为我们必须要假想网络是不可靠的,你无法保证你最后发送的ACK报文会一定被对方收到,因此对方处于LAST_WAIT状态下的Socket可能会因为超时未收到ACK报文而重发FIN报文,所以这个TIME_WAIT状态的作用就是用来重发可能丢失的ACK报文。

Socket编程

Socket编程方式

Socket起源于Unix,而Unix/Linux基本哲学之一就是“一切皆文件”,都可以用“打开Open->读写write/read->关闭close”模式来操作文件。Socket就是该模式的一个实现,Socket即是一种特殊的文件,一些Socket函数就是对其进行操作(读写IO、打开、关闭)。因此Socket也提供了类似于连接Connect、关闭连接Close、发送、接收等方法的调用

数据传输方式

常用stream和dgram

  1. STREAM表示面向连接的数据传输方式,数据可以准确无误地到达另一台计算机,如果丢失或损坏,可以重新发送,但是相对效率低

  2. DGRAM表示无连接的数据传输方式,计算机只管数据传输,不做数据校验,DGRAM所做的校验工作少,所以效率比STREAM高

QQ视频聊天和语音聊天使用的就是DGRAM传输数据,因为首先需要保证通信的效率,尽量减少延迟,而数据的正确性是次要的,即使丢失很小的一部分数据视频和音频也可以正常解析,最多出现噪点或杂音,不会对通信质量有实质影响

服务器编写步骤

  1. 调用socket()函数创建一个用于通信的套接字

    买了个手机

  2. 给已经创建的套接字绑定一个端口号,一般通过设置网络套接口地址和调用bind()函数来实现

    办张手机卡,插上手机卡

  3. 调用listen()函数使套接字成为一个监听套接字

    等待来电

  4. 调用accept()函数来接受客户端的连接,这时就可以和客户端通信

    接听到了打来的电话

  5. 处理客户端的连接请求

    接通电话听、说沟通

  6. 终止连接

    挂断电话

客户端编写步骤

  1. 调用socket()函数创建一个用于通信的套接字

    买了个手机

  2. 通过设置套接字地址结构,说明客户端与之通信的服务器的IP地址和端口号

    输入对方手机号

  3. 调用Connect()函数来建立与服务器的连接

    拨号,并等接听

  4. 调用读写函数发送或接收数据

    说话、听话

  5. 终止连接

    挂断电话