n大多数网络应用系统可分为两个部分:客户端©和服务器端(S) 。
一个server经常同时和多个client建立连接
TCP 套接字通信工作流程
面向连接的套接字Socket通信工作流程
为了实现服务器与客户机的通信,服务器和客户机都必须建立套接字。服务器与客户机的工作原理可以用下面的过程来描述。
1.服务器先用 socket 函数来建立一个套接字,用这个套接字完成通信的监听 。
2.用 bind 函数来绑定一个端口号和 IP 地址。因为本地计算机可能有多个网址和 IP,每一个 IP 和端口有多个端口。需要指定一个 IP 和端口进行监听。
3.服务器调用 listen 函数,使服务器的这个端口和 IP 处于监听状态,等待客户机的连接。
4.客户机用 socket 函数建立一个套接字,设定远程 IP 和端口。
5.客户机调用 connect 函数连接远程计算机指定的端口。
6.服务器用 accept 函数来接受远程计算机的连接,建立起与客户机之间的通信。
7.建立连接以后,客户机用 write 函数向 socket 中写入数据。也可以用 read 函数读取服务器发送来的数据。
8.服务器用 read 函数读取客户机发送来的数据,也可以用 write 函数来发送数据。
9.完成通信以后,用 close 函数关闭 socket 连接。
基础概念
socket套接口
套接口是应用层到传输层的接口;类似于Unix管道,可以向/从“套接口”中写/读数据
sockfd套接字
套接口的描述字
报文格式
封装过程
TCP报文格式
其中比较重要的字段有:
注意区分两个ACK:ACK确认Ack是否有效(大写的单词表示标志位)
(1)序号(sequence number):Seq序号,占32位
TCP连接中传送的字节流中的每个字节都按顺序编号 ,第一个字节的编号由本地随机产生
seq其实就是这个报文段中的第一个字节的数据编号。
例如,一段报文的seq字段值是 200 ,而携带的数据共有100字节,显然下一个报文段(如果还有的话)的数据序号应该从300开始;
(2)确认号(acknowledgement number):Ack序号,占32位
只有ACK标志位为1时,确认序号字段才有效
Ack=Seq+1。期待收到对方下一个报文段的seq(第一个数据字节的序号);序列号表示报文段携带数据的第一个字节的编号;而确认号指的是期望接收到下一个字节的编号;因此当前报文段最后一个字节的编号+1即为确认号。
(3)==标志位(Flags)==:共6个,即URG、ACK、PSH、RST、SYN、FIN等。具体含义如下:
URG:紧急指针(urgent pointer)有效。
ACK:确认序号有效。占1位,仅当ACK=1时,确认号字段才有效。ACK=0时,确认号无效
PSH:接收方应该尽快将这个报文交给应用层。
RST:重置连接。
SYN:发起一个新连接。
FIN:释放一个连接。
状态转换图
==实线:client==的正常状态转换
==虚线:server==的正常状态转换
app:应用???
TCP连接的建立(三次握手)
在TCP/IP协议中,TCP协议提供可靠的连接服务,采用三次握手建立一个连接。
SYN :同步序列编号(Syn chronize Sequence Numbers)。是TCP/IP建立连接时使用的握手信号。
为什么需要三次握手
首先非常明确的是两次握手是最基本的。第一次握手,客户端发了个连接请求消息到服务端,服务端收到信息后知道自己与客户端是可以连接成功的,但此时客户端并不知道服务端是否已经接收到了它的请求,所以服务端接收到消息后的应答,客户端得到服务端的反馈后,才确定自己与服务端是可以连接上的,这就是第二次握手。
客户端只有确定了自己能与服务端连接上才能开始发数据。所以两次握手肯定是最基本的。
看到这里,你或许会问,那么为什么需要第三次握手呢?我们来看一下,假设一下如果没有第三次握手,而是两次握手后我们就认为连接成功了,那么会发生什么?
第三次握手是为了防止已经失效的连接请求报文段突然又传到服务端,因而产生错误 。
譬如发起请求遇到类似这样的情况:客户端发出去的第一个连接请求由于某些原因在网络节点中滞留了导致延迟,直到连接释放的某个时间点才到达服务端,这是一个早已失效的报文,但是此时服务端仍然认为这是客户端的建立连接请求第一次握手,于是服务端回应了客户端,第二次握手。
如果只有两次握手,那么到这里,连接就建立了,但是此时客户端并没有任何数据要发送,而服务端还在傻傻的等候佳音,造成很大的资源浪费。所以需要第三次握手,只有客户端再次回应一下,就可以避免这种情况。
三次握手过程
服务器必须准备好接受外来的连接。这通过调用socket、 bind和listen函数来完成,称为被动打开(passive open)。
第一次握手:客户通过调用connect进行主动打开(active open)。这引起客户TCP发送一个SYN(表示同步)分节(SYN=J),它告诉服务器客户将在连接中发送到数据的初始序列号 。并进入SYN_SEND状态,等待服务器的确认。
第二次握手:服务器必须确认客户的SYN,同时自己也得发送一个SYN分节,它含有服务器将在同一连接中发送的数据的初始序列号。服务器以单个字节向客户发送SYN和对客户SYN的ACK(表示确认) ,此时服务器进入SYN_RECV状态。
第三次握手:客户收到服务器的SYN+ACK。向服务器发送确认分节,此分节发送完毕,客户服务器进入ESTABLISHED状态,完成三次握手。
客户端的初始序列号为J,而服务器的初始序列号为K。在ACK里的确认号为发送这个ACK的一端所期待的下一个序列号。因为SYN只占一个字节的序列号空间,所以每一个SYN的ACK中的确认号都是相应的初始序列号加1.类似地,每一个**FIN(表示结束)**的ACK中的确认号为FIN的序列号加1.
TCP连接的终止
Step 1.应用首先调用close,主动关闭,发送一个FIN,表示数据发送完毕。
Step 2.另一端执行被动关闭,FIN由TCP确认,接收也作为文件结束符传递给接收方,在此连接上再也收不到数据
Step 3.收到文件结束的应用将调用close关闭它的套接口,它的TCP也发送一个FIN。
Step 4.接收到这个FIN的发送方TCP对之确认。
图中是客户执行主动关闭,而实际上,不管客户还是服务器都可以执行主动关闭,通常是由客户主动关闭,然而,如HTTP是客户端/服务器执行主动关闭?
第一次握手:某个应用进程首先调用close,我们称这一端执行主动关闭。这一端的TCP于是发送一个FIN分节,表示数据发送完毕。
第二次握手:接收到FIN的另一端执行被动关闭(passive close)。这个FIN由TCP确认。它的接收也作为文件结束符传递给接收端应用进程(放在已排队等候应用进程接收到任何其他数据之后)
第三次握手:一段时间后,接收到文件结束符的应用进程将调用close关闭它的套接口。这导致它的TCP也发送一个FIN。
第四次握手:接收到这个FIN的原发送端TCP对它进行确认。
面向字节的数据传送流(如TCP字节流、Unix管道等)也使用EOF表示在某个方向上不再有数据待传送。在TCP字节流中,EOF的读或写通过收发一个特殊的FIN分节来实现。
如何计算时延,抖动???时延一会小一会大
datetimes.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 #include "datetime.h" int main (int argc, char **argv) { int listenfd, connfd; struct sockaddr_in servaddr ; char buff[MAXLINE]; time_t ticks; listenfd = socket(AF_INET, SOCK_STREAM, 0 ); memset (&servaddr, 0 , sizeof (servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_port = htons(50000 ); servaddr.sin_addr.s_addr = htonl(INADDR_ANY); bind(listenfd, (struct sockaddr *)&servaddr, sizeof (servaddr)); listen (listenfd, 1024 ); char * str = "Start listening" ; printf ("%s\n" , str); for (;;) { connfd = accept(listenfd, (struct sockaddr *)NULL , NULL ); ticks = time(NULL ); printf ("client: %s:%d\n" ,servaddr.sin_addr,servaddr.sin_port); snprintf (buff, sizeof (buff), "%.24s\r\n" , ctime(&ticks)); write (connfd, buff, strlen (buff)); close (connfd); } }
相关函数
不清楚的概念
TCP/IP
FTP/Telnet
API
网络编程API有两种:sockets和XTI???
协议族