一些概念
当编写Linux网络程序时,通常做法是编写一个服务端和一个客户端,在程序中要用到以下数据结构及系统调用:
#include <netinet/in.h> /*for struct sockaddr_in*/
#include <unistd.h> /*for write,read*/
#include <sys/types.h>
#include <sys/socket.h> /*for other system calls*/
struct sockaddr_in;
int socket(int domain, int type, int protocol);
/*server需要指定端口:区分lintening socket以及connected socket
只有一个lintening socket,可以有多个connected socket
未连接的数据报发向listening socket
已连接的数据报发向connted socked*/
int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
int listen(int sockfd, int backlog);
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
/*client:使用传输协议指定的临时端口。*/
struct sockaddr_in;
int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
/*common*/
ssize_t recv(int sockfd, void *buf, size_t len, int flags);
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,struct sockaddr *src_addr, socklen_t *addrlen);
ssize_t send(int sockfd, const void *buf, size_t len, int flags);
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,const struct sockaddr *dest_addr, socklen_t addrlen);
ssize_t write(int fd, const void *buf, size_t count);
ssize_t read(int fd, void *buf, size_t count);
int close(int fd);
经典的OSI模型:
1~4层主要处理通信细节,通常作为系统内核的一部分。
5~7层主要处理应用逻辑,通常构成用户进程。
UDP与TCP
UDP
User Datagram Protocol:UDP是不可靠的传输层协议,其长度包含在发送的数据中。协议不保证数据报的顺序,不保证数据报会到达
目的地,也不保证数据报只会到达一次。我们可以在程序中应用一些机制来进行确认,例如应答机制,超时机制,重发机制等。UDP
是无连接的,即客户端与服务端之间不需要长连接。
TCP
TCP是基于连接的协议,并且提供可靠性,它要求对端进行应答,如果在一定时间内没有收到应答将进行一定次数的重发,如果仍未
成功,TCP放弃发送数据,断开连接并通知用户。TCP使用了动态估算往返时间(round-trip time)的算法,并且在连接期间持续估算
,所以协议知道应当为应答设置多久的等待时间。
TCP将发送的每字节数据都关联序列号以对数据进行排序,并且还可以根据序列号对数据去重。
TCP提供流控制,协议通知对端在某一时刻,它能够从对端接收多少字节,称为通知窗口(advertised window),就是当前接收端可用
的缓存空间,以保证不会溢出缓冲区。窗口是动态变化的,当接受到数据时,窗口空间减少;当数据被读取后,窗口空间增加。当窗
口空间减少到0时,必须等待数据被读取后才能继续接收。
TCP连接是全双工的。连接双方可以同时双向发送或接收数据。因此,协议必须为每个方向的数据流记录状态信息。连接建立后也可
以变成单工的。
三次握手
- 服务端通过
socket
,bind
,listen
等调用被动打开连接 - 客户端调用
connent
打开主动连接。客户端发送SYN包,向服务端告知其序列号 - 服务端应答(ACK)客户端的SYN,并且发送其SYN
- 客户端应答ACK服务端的SYN
四次挥手
- 一端调用
close
,这一端称为主动关闭,发送FIN包 - 另一端收到FIN包,称为被动关闭。TCP会对收到的FIN进行应答。FIN向接收数据加上EOF标志,即此端无法收到数据
- 一段时间后,被动关闭端读到EOF,调用
close
,TCP发送FIN包 - 主动关闭方收到FIN,进行应答
在2,3步之间,数据仍可能由被动关闭方传向主动关闭方,称为半关闭。
任何一端都可以主动关闭。除了调用close,当程序终止时,所有打开的描述符都被关闭,也会发送FIN包。
TCP状态转换图
共有11种状态
TIME_WAIT状态
上图中看到,主动关闭的一方要经过这一状态。这一状态持续时间为2倍的最长报文寿命(maximum segment lifetime)。MSL是IP数据报在网络中存活的最长时间。每个数据报都包含8位的跳限制,最大为255。跳限制为255的数据报存活时间也不能超过MSL。数据包在网络中丢失通常是路由异常的原因。假设一个TCP包丢失并且被超时重发,重发的包经由其他路由路径到达目的地。但是在不多于MSL时间之后,路由恢复,之前丢失的包也到达了,TCP必须处理这些重复情况。
TIME_WAIT状态有两个原因:
- 实现TCP可靠全双工终止连接
- 假设在四次挥手过程中,最后一个ACK丢失了。被动关闭方会重发FIN,所以主动关闭方必须保存状态信息,以便重发最后一个ACK。
- 确保重复的报文在网络中消失
- 假设两端之间的连接被关闭,不久之后又重新打开。TCP应当防止上次连接的包出现在新的连接中。因为2倍MSL的TIME_WAIT状态的存在,TCP不会立即开始新的连接。第一个MSL使数据包消失,第二个MSL使应答消失。