传输层的主要功能
- 进程与进程的通信
- 端口的概念
端口
◆ 使用端口(Port)来标记不同的网络进程
◆ 端口(Port)使用16比特位表示(0~65535)
UDP协议
- UDP(User Datagram Protocol: 用户数据报协议)
- UDP是
无连接
协议 - UDP
不能保证可靠
的交付数据(“想发就发”,“无法保证数据在网络中是否丢失”) - UDP是
面向报文传输
的(UDP将应用层传输过来的数据不做任何处理,完整的封装到UDP报文中去) - UDP
没有拥塞控制
- UDP的
首部开销很小
TCP协议
- TCP(Transmission Control Protocol: 传输控制协议)
- TCP是
面向连接
的协议 - TCP的一个连接有两端(点对点通信)
- TCP提供
可靠
的传输服务(可靠传输的基本原理:停止等待协议、连续ARQ协议) - TCP协议提供
全双工
的通信 - TCP是
基于字节流
的协议
TCP首部由固定的20个字节,由前面5行所组成。(UDP首部有8个字节,IP首部也是位20字节的长度)
源端口:
◆ 16位,源机器使用该网络的进程
目的端口:
◆ 16位,目的机器使用该网络的进程
序号:
◆占据 4 个字节, 0~2^32-1位,
◆一个字节一个序号(TCP协议是面向字节流的,所以对于TCP协议来说,每个字节都有一个唯一的序号来标记传输的字节)
◆ 表示数据首字节序号(该序号代表的是TCP报文所传输的数据(图三 中 TCP数据报的数据)的第一个字节)
确认号:
◆ 占据 4 个字节,0~2^32-1位
◆ 一个字节一个序号
◆ 表示期望收到数据的首字节序号(确认号为N:则表示N-1序号的数据都已经收到)
数据偏移:
◆ 占4位:0~15,单位为:32位字
◆ 数据偏离首部的距离(因为有TCP选项
字段长度不确定有多少,所以以数据偏移
来记录真实的数据偏移头部的位置)
◆ TCP首部长度位20~60字节(TCP首部固定字节20位,选项
最大的数字是15,每一个偏移都可以表示4个字节,即 60字节(15*4),由此也可得选项
最多能有40个字节)
TCP标记:
◆ 占6位,每位各有不同意义
窗口:
◆ 占16位:0~2^16-1
◆ 窗口指明允许对方发送的数据量
◆ 窗口
与 确认号
:设确认号为501,窗口值为1000,那么501~1500字节的数据都是可以接收的
校验和:
◆ 检查数据在传输中是否发生错误
紧急指针:
◆ 紧急数据(URG=1)
◆ 指定紧急数据在报文的位置
选项:
◆ 最多40字节
◆ 支持未来的拓展
TCP协议的可靠传输
滑动窗口
累计确认
选择重传
可靠传输的基本原理
1)停止等待协议(超时重传计时器)
2)连续ARQ协议(滑动窗口、累计确认、选择重传)
停止等待协议
停止等待协议特征:
1)最简单的可靠传输。停止等待协议是最简单的可靠传输协议
2)信道利用率不高。停止等待协议对信道的利用效率不高
3)超时定时器。每发送一个消息,都需要设置一个定时器
停止等待协议出差错的三种情况:
1)发送的消息在路上丢失了
2)确认的消息在路上丢失了
3)确认的消息很久才到
停止等待协议出差错的解决方法:超时重传
ARQ协议(Automatic Repeat reQuest 自动重传请求)
TCP协议的可靠传输:
1)滑动窗口(以字节为单位)
2)累计确认(提高了对信道的利用效率)
3)选择重传/回退N(指定重传边界)
可用窗口
的首位字节序号为对方期待收到的下一个字节
,即接收方TCP首部的 确认号
。
假如在某个时刻,23 - 26 这4个字节都已经发送出去了,但是没有收到确认,所以窗口还不能向前滑动,此时的可用窗口只有3个字节大小。
当23、24字节已经收到确认了,此时窗口可用往前滑动2个字节,可用窗口大小为5字节。
假如窗口为7的大小的所有字节都已经发送出去了,但是都没有收到确认,此时窗口大小为0。
选择重传/回退N
TCP报文一次传输是传输多个字节的,那么报文丢失时丢失的是整个TCP报文,该报文中的整段字节流都是丢失的,所以选择重传是指定需要重传的边界而不是某个字节。
我们知道,每一个字节都有唯一的32位序号,选择重传指定的重传字节序号存储在TCP报文首部的选项
字段中。
累计确认 与 回退N:
假如窗口为7的大小的所有数据都已经发送出去了,在某个时刻25、27字节的确认号已经收到了。但是23、24字节并没有收到确认消息,窗口并不能往前滑动。
假如此时超时时间到了,需要重新传输,即便25、27字节已经收到了,但是因为没有按顺序收到确认消息,还是会从23字节开始重新传输。
累计确认 与 选择重传:
连续ARQ协议中,滑动窗口滑动的条件是前面的数据都已经收到确认。从功能上来看,会感觉选择重传和连续ARQ协议是不能共存的,因为选择重传意味着前面的数据没有正确收到确认,需要重传,也即是窗口此时不能向前滑动。
实际上两者是可以共存的,累计确认和选择重传不冲突。
举个例子,假设发送窗口有11200,并已经全部发送,1400已收到确认,401800未收到确认,8011200已收到确认。按照累计确认和选择重传,可以确认1400,窗口向前移动400,然后选择重传401800,然后收到确认后在向前移动800。
但这个例子如果是回退N的话,那么801~1200也是需要重传的,也即是回退N策略。
所以回退N和选择重传是冲突的,但累计确认和选择重传不冲突。
TCP协议的流量控制
窗口
坚持定时器
名词解析
ACK:指那6个比特位的标记。
ack:指确认号
seq:的序号是以传输的字节为单位的(所以传输100字节以后,seq应当为1+100=101)
rwnd:值可用窗口大小
窗口
◆ 流量控制指让发送方发送速率不要太快
◆ 流量控制是使用滑动窗口来实现的
接受方可用调整窗口大小,以告诉对方我当前还可用接收多少数据,通过窗口大小的控制来实现流量的控制。
ACK=1,ack=201,rwnd=300 解析:
ACK=1:TCP中用ACK=1来表示收到确认
ack=201:确认号为201,即期待收到的下一个字节的帧序号
rwnd=300:窗口为300,即发送方还可用发送300字节的数据,若rwnd=0,表示发送方不可再发送数据了
坚持定时器
◆ 当接收到窗口为0的消息,则启动坚持定时器
◆ 坚持定时器每隔一段时间发送一个窗口探测报文
接着上面例子,接受方窗口大小0时,在进行数据处理并交给应用层后,接受方又可以接收消息了,那么接收方就会重新调整窗口大小( rwnd=1000)并告知接收方,当前我的窗口可以接收1000个数据。
如果接收方的 rwnd=1000 消息在网络传输过程中发生了丢失,会导致发送方和接收方都一直的在等待状态,形成了死锁局面。
发送方:发送方以为接收方的窗口为0,便会一直在等待,等待接收方什么时候有可用窗口才会继续进行数据的发送
接收方:接收方已发送了有可用窗口的消息,便会一直等待,等待发送方发送新的数据来
TCP不是可靠传输吗,rwnd=1000 报文为什么会丢失?
TCP的可靠传输主是从数据的角度来说的,如 seq、ack 都是对数据字节的确认。但是对于特殊的消息,如窗口大小rwnd的消息,是没有超时重传的机制的,那么就会有这个消息丢失导致发送方和接收方死锁的情况。
解决方法:坚持定时器
当发送方接收到窗口为0的消息时,就会启动坚持定时器,该定时器每隔一段时间就会向接受方发送一个窗口探测报文,来询问对方窗口是否增大,以解决死锁的局面。
TCP协议的拥塞控制
慢启动算法
拥塞避免算法
◆ 一条数据链路经过非常多的设备,数据链路中各个部分都有可能成为网路传输的瓶颈,形成拥塞
◆ 报文超时则可以
认为是拥塞(也可能是路线故障等其他因素导致的报文超时)
◆ 流量控制考虑点对点的通信量的控制(以端到端
的角度去考虑)
◆ 拥塞控制考虑整个网络,是全局性的考虑(以自身与全局
的角度去考虑)
慢启动算法
◆ 由小到大逐渐增加发送数据量,直到增长到慢启动阈值(ssthresh)
◆ 每收到一个报文确认,就加一倍
◆ 指数增长
拥塞避免算法
◆ 维护一个拥塞窗口的变量,只要网络不拥塞,就试探着拥塞窗口调大
◆ 每收到一个报文确认,就加一
◆ 线性增长
以上可以看出,TCP的拥塞控制更多是从自身的角度去控制的。如果网络不发生拥塞,就发送更多的数据到网络中去,如果拥塞了,就减少自己所发送的数据量,以降低网络设备的压力。
TCP连接的建立
三次握手
TCP标记
TCP标记占6个比特位,每位都用不同的含义:
三次握手过程
第一次握手:
SYN=1,表示连接请求的报文;seq=x 并且同步自己的序列号为x
第二次握手:
SYN=1,表示连接请求的报文;ACK=1表示对序列号的确认;ACK标志需要和确认号ack结合来看,确认号ack=x+1 表示希望收到x+1序列号的值;seq=y:同时该报文会携带自己的序列号为y
第三次:
ACK=1握手:该报文的确认号是有用的,确认号 ack = y+1:我希望收到序号为y+1的数据;seq=x+1:同步一个自己的序列号为x+1,表示当前我发送的数据的序列号为x+1
三次握手关键信息
第一次、第二次:都是由SYN标记,SYN=1,表示连接请求;
第二次、第三次:都有ACK标记,ACK是先对连接双方的序列号进行同步。通过两次的ACK,发送方就能知道接收方的序列号,接收方也知道了发送方的ACK。通过这三次握手,它们不仅建立了TCP连接,并且同步了各自的序列号。
序列号seq(Sequence Number):
1)这个数字不是0或1,而是随机生成的
2)该字节代表着包含该序列号的报文段的数据中的第一个字节
仔细观察三次握手报文的关键信息,三次握手最主要的目的就是建立连接,其中就包括确定序列号,可以看到的是,序列号是由主动连接的一方随机生成的,而被动连接方在收到序列号后,就会继续沿用这个序列号,此后的通信都是在这个序列号之上做的偏移,所以你的理解是正确的。
为什么发送方要发出第三个确认报文?
结论:
已经失效的连接请求报文
传送到对方,引起错误
。
解说:
TCP连接请求的双方:发送方、接收方
发送方发送了一个请求报文(请求1),即第一次握手。
假设该报文(请求1)在网络中传输了很久才到达了接收方,发送方很久都没有收到确认消息,认为该请求已经超时了,所以又发送了一次同样的报文(请求2)。
这次的报文(请求2)很快的就达到了接收方,接收方进行确认回应,并建立起它们之间的连接。因为第二次请求建立连接了,那么第一次的(请求1)就是失效的报文(因为请求1的功能已经被请求2所完成,它在网络中已经没有作用了,我们认为是失效的)。
两次握手建立连接的结果:
如果两次握手就可以建立连接,即只要请求到了接收方就可以建立起连接。
对于上面的例子,不管请求2还是(我们认为已经失效的)请求1,在到达接收方后就可以建立起连接。即 同样的一个请求,发送了两次,就会建立两个TCP连接的情况的错误。
三次握手建立连接的结果:
接着上面的例子,请求2先到达接收方(第一次握手)后,接收方发送确认报文给发送方(第二次握手),发送方会再发送一个报文(第三次握手)表示确认,此时才建立起连接。
那么请求1呢?第一次握手后,接收方也会发送一个确认报文到发送方(第二次握手),但是发送发已经进行请求2的第三次握手了,因此发送方对于该第二次的确认消息会忽略掉,并不会去进行第三次的握手。这样就避免了两次握手建立连接引发的错误。
TCP连接的释放
四次释放
2MSL
等待计时器
四次释放
第一次挥手:主动释放方发出报文,FIN=1表示释放连接报文,并发送序列号seq为u;
第二次挥手:接收方接收到消息,并发送确认消息给释放方,ACK=1表示请求已经收到,并且已对消息进行了确认;
第三次挥手:被动释放的接收方,在数据处理完成后,再次发送报文,FIN=1表示可以进行连接释放,ACK=1表示重复的对第一个报文(seq=u)的确认;
第四次: 主动释放方在接收到第二次报文后,就会发送一个确认报文,表示被动释放方的连接释放消息已经收到了,可以释放连接了。主动释放连接的这方,在进行第四次挥手之后,会进入一个特殊的状态,TIME-WAIT
时间等待状态,等待SMSL时间后,确保没有出现任何的问题,然后才会进入关闭
状态,而对于接收方,只要在最后确认
状态之后,就会进入关闭
状态
TCP的连接释放,四次挥手:
1,A发送一个FIN,代表数据发送完毕,其序列号为u,它等于前面已传送数据
最后一个字节的序列号+1
2,B收到A的释放报文段后发出确认,确认号ACK=u+1,确认报文段的序列号为v。
A到B这个方向上的连接就释放了。
3,B发送释放报文段FIN,B还需要重复上次发送的ACK,B放的报文段序列号为w,
等待A的确认。
4,A在收到B的释放报文段后,需要确认。确认号为w+1
为什么是四次挥手?
为什么不是三次或者五次?
因为TCP的通信是全双工通信。
要实现可靠的连接关闭,A发送结束报文FIN,收到B的确认后。A知道自己没有
数据要发送,B知道A不再发送数据。但是B还可以发送数据,A还可以接收数据。
只有当B发送结束报文FIN,收到A的确认后,才算是真正的断开连接。
2MSL:
MSL(Max Segment Lifetime): 最长报文段寿命
一般MSL通常建议设置为2分钟,2MSL即4分钟
我们知道,TCP连接会占用一个端口。如果我们释放一个链接后想立马复用该端口,是不行的。因为主动释放连接的一方会进入TIME-WAIT
状态,连接还没有被释放,只有TIME-WAIT
状态结束后才会释放端口。
为什么需要等待2MSL(等待计时器的作用)?
1)作用一:确保发送方发送的第四个挥手报文可以正确的到达对方,以正确的接收这次连接
◆ 最后一个报文没有确认
◆ 确保发送方的ACK可以到达接收方
◆ 2MSL时间内没有收到,则接收方会重发
2MSL时间内没有收到,则接收方会重复进行第三次挥手步骤,发送方在接收到该报文之后,就知道原来我发送的确认释放连接报文对方并没有收到,所以对方重新发送了连接释放的请求,因此发送方会再次进行第四次挥手,这就是等待计时器的作用。确保发送方发送的第四个挥手报文可以正确的到达对方,如果没有到达的话,接收方就会重新发送第三次挥手的报文,以正确的接收这次连接
2)作用二:确保当前连接的所有报文都已经过期
最后的一个报文都等待了2倍的MSL,那么其他报文肯定是超过2MSL的时间的,MSL表示最长报文段寿命,即其他报文都是过期了的,所以就可以确保当前连接的所有报文都已经过期了。
套接字与套接字编程
套接字
服务端编程
客户端编程
套接字
套接字:IP和端口的组合 {𝐼𝑃: 𝑃𝑜𝑟𝑡} ,表示指定网络中的某一台主机的某进程
(一个IP可以有多个套接字,因为IP和不同的端口组合就形成不同的套接字)套接字(Socket)是抽象概念,表示TCP连接的一端
(它并不是一个新的东西,它只是IP和Port组合的一个名字,表示TCP连接的一端)通过套接字可以进行数据发送或接收
(很多时候我们在对网络进行编程,实际上也就是来对套接字来进行编程,通过套接字来进行数据的发送和接收)TCP连接由两个套接字组成
TCP是端到端的通信,所以TCP连接由两个套接字组成,即2个端口2个IP我们就可以确定一个TCP的连接:
TCP
= { 𝑆𝑜𝑐𝑘𝑒𝑡1: 𝑆𝑜𝑐𝑘𝑒𝑡2}
= { { 𝐼𝑃: 𝑃𝑜𝑟𝑡 𝐼𝑃: 𝑃𝑜𝑟𝑡 }}
套接字编程
如果需要对套接字进行编程的话,很多时候都是把它们看出C/S架构。客户端和服务端通过TCP连接起来,然后不管是客户端还是服务端都会通过Socket来进行数据的发送和接收。
TCP协议的四个定时器
超时定时器
超时定时器也称为超时重传定时器。
超时定时器主要应用在TCP的可靠传输协议里面,是为了控制可能发生丢失的报文而设计的定时器,当TCP协议发送端发送一个报文时,就会为该报文设置一个超时定时器。
如果在超时定时器结束之前收到了来自接收端对该报文段的确认,则撤销这个定时器。
如果在超时定时器结束时仍然没有收到来自接收端对该报文段的确认(超时),则认为这个报文可能已经丢弃,发送端重新发送该报文,并重新设置一个超时定时器。
需要注意的是,发送端在超时定时器撤销之前,必须继续缓存已发送未确认的报文,直到发送端收到了来自接收端的确认。
坚持定时器
坚持定时器是使用滑动窗口进行流量控制的时候而设置的。
要理解坚持定时器,需要理解“零窗口通知”的情况。
我们知道接收端通过调整接收窗口的大小可以控制发送端的发送速度,当接收端把接收窗口调小时,那么发送端就会调小发送的流量。
这就可能产生一种情况,就是接收端的缓存区已经满了,这个时候接收端会给发送端发送一个“零窗口”的消息,表示说“当前我已经没有余力处理更多的数据了”,这就是“零窗口通知”的情况。
当出现这种情况的时候,双方都会陷入等待的状态,发送端等待接收端的窗口调大,接收端等待发送端发送的数据。当接收端窗口可以调大的时候,接收端会发送窗口调大的信息给发送端,但是这个消息是不可靠的,也即是这个消息可能会在传输中丢失,并且不会被感知到丢失和重传。
如果这个消息在发送过程中丢失的话,那么发送端和接收端就会进入死锁状态,因为接收端认为“我已经把窗口调大的消息发送出去了,发送端理应发送新的消息给我才对”,所以接收端会一直等待发送端的消息;而发送端因为没有收到窗口调大的消息,则认为“接收端还没有调大窗口,因此我不能发送”,发送端也会一直等待。
因此为了解决这个问题,当发送端收到窗口为零的消息之后,会启动一个坚持定时器来周期性主动的向接收方查询,以便发现窗口是否增大,这个就是坚持定时器的作用。
时间等待计时器
时间等待定时器是在TCP连接的第四次挥手中使用到的。
时间等待计时器(Time-Wait)是由主动关闭TCP连接的一方设置的,当主动关闭TCP连接的一方收到来自对方的FIN报文的时候(第三次挥手),则认为对方也可以关闭TCP连接,这个时候主动关闭TCP连接的一方发送一个消息确认的报文(第四次挥手),并启动这个时间等待计时器,这个计时器会等待2倍MSL的时间。
(以下为方便讨论,我们把主动关闭的一方称为A,被动关闭的一方称为B。)
这个定时器主要是为了正确关闭一个TCP连接而考虑的,这主要是为了保证A在对最后一个FIN报文(第三次挥手)发送确认的报文可以到达B。
当A发出这个报文之后,就会启动2MSL计时器,注意,这个报文是有可能在网络传输过程中丢失的,如果B收不到这个确认,那么B会重新发送一次FIN报文,A会重新收到这个报文并重传一次最后的确认,并重新启动2MSL计时器,直到双方正常结束TCP连接。2MSL时间可以保证当B没有收到确认时,B可以再次发出FIN报文,并且A可以再次收到并重新发送确认,所以2MSL的时间可以保证连接正常结束。
保活计时器
保活计时器,顾名思义,他是为了保活TCP连接而设计的。保活定时器可以防止TCP连接的两端出现长时期的空闲(如当一方出现状态变化或故障时),另一方没有察觉的情况。
设想连接双方在建立连接后,只传输了一些数据,然后就都保持静默了,双方也都没有关闭连接(这种情况经常存在),如果这个时候其中一方已经故障,那么这个连接将会永远被打开,如果被连接的一方是服务端的话,那将浪费很多服务端的资源。
因此为了解决这个问题,服务端一般都会设置一个保活定时器,每次收到对方的数据则重置这个定时器,如果定时器超时,服务端则发送探测报文段,探测客户端是否还在线,如果没有收到响应的话,那么则认为客户端已经断开连接了,因此服务端也会终止这个连接。
保活定时器其实有很广泛的应用,在今天,很多的分布式系统,都会使用保活定时器来检测其他节点是否在线还是已经故障,或者其他节点也会每隔一段时间向主节点上报心跳信息以证明在线,这些都是保活定时器在更多领域的广泛应用。
传输层相关问答
1. 传输层主要工作在什么地方?
传输层主要工作在终端设备(手机、计算机、笔记本、平板电脑等)上。
2. 传输层最重要的两个协议是什么?
TCP 协议和 UDP 协议。
3. 端口是什么,端口有什么作用?端口在协议中一般占用几个字节?
端口是为了标记不同的使用网络的进程,端口使用 16 个比特为表示。
4. UDP 协议的中文全称是什么?UDP 协议有什么特点?请分点说明。
{UDP 全称是用户数据报协议 (User Datagram Protocol);UDP 是一个非常简单的传输层协议,他不能保证可靠的交付数据。
5. UDP 的报文和 IP 报文一样,也是由头部和数据两个部分组成,请绘制 UDP 头部的结构图。
略
6. TCP 协议的中文全称是什么?TCP 协议有什么特点?请分点说明。
TCP 全称是传输控制协议 (Transmission Control Protocol),TCp 是面向连接的协议,TCP 协议可以提供可靠的数据传输服务。
7. TCP 的报文和 UDP、IP 报文一样,也是由头部和数据两个部分组成,请绘制 TCP 头部的结构图。
略
8. TCP 报文头部的序号和确认号都占据 4 个字节,序号有什么用?确认号呢?
序号用于传输数据的编号,在 TCP 报文中一个字节一个序号。确认号表示期待收到的数据的首字节序号。
9. TCP 报文头部的数据偏移字段有什么用?他和 TCP 报文的结构有什么关系?
数据偏移字段表示传输数据偏移 TCP 首部的距离,如果 TCP 报文中包含可选数据,则数据偏移字段起作用。
10. TCP 报文头部的标记占用 6 个比特位,每个比特位的值可以为 1 或 0,他们分别有什么意义?
略
11. TCP 报文头部的窗口占用 2 个字节,窗口字段有什么作用?
窗口指明允许对方发送的数据量,用于控制 TCP 连接中数据传输的速率。
12. TCP 协议一个重要的特点是可靠传输,可靠传输是怎么实现的?请简要描述可靠传输的基本原理。
可靠传输依赖于超时重传,即接收方需要发送确认消息给发送方,如果一定时间内发送方没有收到确认消息,则认为消息没有到达接收方,重新发送数据。
13. TCP 协议的流量控制和 TCP 报文头部的什么字段有关系?流量控制具体是怎么工作的?请简要描述流量控制的主要过程。
TCP 的流量控制和 TCP 头部的窗口字段有关系,流量控制根据 TCP 报文中窗口的大小来进行数据的传输,当窗口较大时,增加数据发送速度,当窗口较小时,降低数据发送速度。
14. TCP 协议具备拥塞控制的功能,拥塞控制和流量控制有什么区别?拥塞控制是怎么实现的?
流量控制强调 TCP 连接双方的发送 - 接收速率,拥塞控制强调网络环境的状态。
TCP 连接的发送方可以感知网络环境的状态并做出反应。发送端使用慢启动算法、拥塞避免算法
15. TCP 协议的三次握手是非常重要的知识点,TCP 协议的三次握手和 TCP 报文头部的哪些字段有关系?
序号、确认号、TCP 标记 ACK、SYN、FIN
16.请简述 TCP 协议三次握手的过程。
略
17. TCP 协议的三次握手是 TCP 建立可靠通信的基础,请问为什么 TCP 连接建立的过程叫做三次握手,请陈述三次握手的必要性,两次握手为什么不行?
两次握手不能保证连接正确建立,TCP 被连接的一端有可能响应失效的请求报文而无法确认。
18. TCP 协议的四次挥手是非常重要的知识点,TCP 协议的四次挥手和 TCP 报文头部的哪些字段有关系?
序号、确认号、TCP 标记 ACK、SYN、FIN。
19. 请简述 TCP 协议四次挥手的过程,并说明清楚每一次挥手后,双方进入的状态。
略
20. TCP 连接释放的过程中有一个重要概念叫做 2MSL,什么是 2MSL,它有什么作用?
MSL 指的是最长报文段寿命 (Max Segment Lifetime),2MSL 保证 ACK 报文可以正确到达对方。
21. TCP 协议有四个重要的定时器,分别是什么定时器?请简述这些定时器的作用。
超时定时器、坚持定时器、时间等待计时器、保活计时器。
22. 什么是套接字?你了解套接字的编程吗?请使用套接字实现一个简单的 C/S 通信工具。
套接字是 TCP 连接双方的 IP: 端口对