如何使用紧急URG控制位,在socket编程中send函数flag参数

send(int socket, const void *buffer, size_t length, int flags);

flags参数传MSG_OOB宏时,表示此时有紧急数据。MSG_OOB是个宏,

URG与PSH两者都使用于紧急处理的情况,用来快速传输紧急数据。

URG置为1时,对于发送发,“带外数据”与正常情况下应该发送的消息数据一起,封装成数据报发送,省去了在队列中等待的时间。在接收方,解析报文后,获取数据之后还是要放在缓存区中,等待满了之后在向上往应用层交付。

PSH置为1时,对于发送方,表明这些数据不需要等向下发送的缓存区满,立刻封装成报文,发送,省去了等待发送缓存区到达满的状态的时间。在接收方,也不需要等接受缓存区满,直接向上交付给应用层。

TCP头部是基础知识,必须了解才能更好的理解TCP数据如何封装和传输,以及在建立链接和断开链接时都在操作那些地方。

三次握手建立连接

三次握手如何建立连接?

从图中可以清楚的看到,三次握手的过程,我在在把过程清楚的解释一遍,顺便说下每个过程容易被问到的知识点。

采用C/S模式解释,假设C端发起传输请求。

在发送建立链接请求之前,C端是保持CLOSED状态,S端最开始也是处于CLOSED状态,当执行listen函数套接字进入被动监听状态

所谓被动监听,是指当没有客户端请求时,套接字处于“睡眠”状态,只有当接收到客户端请求时,套接字才会被“唤醒”来响应请求。

第一次:C端发送SYN=1的请求报文,此时C端进入SYN SENT状态,等待服务器确认。

此时如果报文丢失发送不到对端会如何?

第二次:S端收到C端发送的SYN报文(建立链接请求)后,S端必须返回确认号并且同时发送一条SYN报文,此时进入SYN RCVD状态。

为啥要连带发送SYN报文?

如果第二次报文丢失怎么办?

第三次:C端收到S端发的ACK+SYN报文,需要返回一个应答ACK的报文,此时该连接会进入半连接状态的队列,当S端收到ACK后,一条完整的全双工TCP链接建立完成,双方进入ESTABLISHED状态。

如果第三次报文丢失怎么办?

这里面的每个状态都必须搞明白,面试官也超级爱问上面的状态转移。

龙叔还遇到过一个面试官问我用过socket编程么?问我用过哪些socket函数?

C端socket编程代码

//C端
#define PORT  8080
#define BUFFER_SIZE 1024
int main(int argc, char **argv)
{
    //定义IPV4的TCP连接的套接字描述符
    int sock_cli = socket(AF_INET,SOCK_STREAM, 0);
    //定义sockaddr_in
    struct sockaddr_in servaddr;
    memset(&servaddr, 0sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_addr.s_addr = inet_addr(argv[1]);
    servaddr.sin_port = htons(PORT);  
 
    //连接服务器,成功返回0,错误返回-1
    int ret = connect(sock_cli, (struct sockaddr *)&servaddr, sizeof(servaddr));
 
    //客户端将控制台输入的信息发送给服务器端,服务器原样返回信息,阻塞
    while (fgets(sendbuf, sizeof(sendbuf), stdin) != NULL)
    {   
        ret=send(sock_cli, sendbuf, strlen(sendbuf),0); ///发送
        recv(sock_cli, recvbuf, sizeof(recvbuf),0); ///接收
        fputs(recvbuf, stdout);
    }
 
    close(sock_cli); // 关闭连接
    return 0;
}

S端socket编程代码

int main(int argc, char **argv)
{
    //定义IPV4的TCP连接的套接字描述符
    int server_sockfd = socket(AF_INET,SOCK_STREAM, 0);
    //定义sockaddr_in
    struct sockaddr_in server_sockaddr;
    server_sockaddr.sin_family = AF_INET;
    server_sockaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    server_sockaddr.sin_port = htons(PORT);
 
    //bind成功返回0,出错返回-1
    if(bind(server_sockfd,(struct sockaddr *)&server_sockaddr,sizeof(server_sockaddr))==-1)
 
    //listen成功返回0,出错返回-1,允许同时监听的连接数为QUEUE_SIZE
    if(listen(server_sockfd,QUEUE_SIZE) == -1)
 
    for(;;)
    {
        struct sockaddr_in client_addr;
        socklen_t length = sizeof(client_addr);
        //进程阻塞在accept上,成功返回非负描述字,出错返回-1
        int conn = accept(server_sockfd, (struct sockaddr*)&client_addr,&length);
 
        //处理数据部分
      ...
    }
 
    close(server_sockfd);
    return 0;
}

为什么需要三次握手建立链接,2次可以么,4次行不行?

这问题问的,面试官是咋了?在这明知故问的,整些有的没的。肯定是不行啊,RFC 标准就是这样写的啊。

可不敢这样回答啊,标准是说的三次握手建立链接,可没说四次不行啊。要是这样答,妥妥的会收到,同学我们今天的面试到此基本结束了,你回家等消息...

龙叔来说说这个问题,为什么不能两次?

如果第二次不发送SYN+ACK,只是发送确认应答消息ACK,会造成只能建立单向通信,而且不能应答。而TCP是全双工通信的,而且必须保证可靠性。

如果第二发送SYN+ACK,不用应答。此时会出现三种情况

一、二次握手失败,C端会重复发送SYN报文,等待对端发送确认报文,S端会保存tcp连接的所有资源,大量的这种情况会导致S资源耗尽。

二、二次握手成功,S收不到ACK会重复发送SYN+ACK报文。

三、二次握手完以后,双方以为连接建立成功,即可开始通信。假如此时连接并没有真的建立成功,S端开始发送消息,会造成网络拥堵发生。

为什么不能是四次?

四次其实原则上来说是可以的,就是把第二次的ACK和SYN分两次发送。在理论上是完全可以行得通的,但是TCP本着节约网络网络资源的前提。

还有一种是不拆开二次握手的捎带应答,三次握手之后C端继续发送SYN报文,其时这是徒劳的。第三次完成以后链接已经建立,后面无论多少次都是徒劳。

如果双方同时建立连接,会发生什么情况?

这就是双方同时建立链接的情况,情况还不错,反正能建立成功,这点是肯定的。但是要注意两点

第一、此时只会建立一条全双工的TCP链接,不是两条。

第二、双方没有CS之分,两端都是同时承担两个角色,客户端和服务器。

四次挥手断开链接

先整个图看下四次挥手的整个过程和状态转移。状态转移会考看仔细点。

依旧采用C/S模式解释此过程。

第一次:当C端的应用程序结束数据传输是,会向S端发送一个带有FIN附加标记的报文段(FIN表示英文finish),此时C端进入FIN_WAIT1状态,C端不能在发送数据到S端。

第二次:S端收到FIN报文会响应一个ACK报文,S端进入CLOSE_WAIT状态。进入此状态后S端把剩余未发送的数据发送到C端,C端收到S端的ACK之后,进入FIN_WAIT2状态。

同时继续接受S端传输的其他数据包。

第三次:S端处理完自己待发送的数据之后,也会发送FIN断开链接的请求,S端进入LAST_ACK状态。

第四次:C端收到S端的断开链接请求后会启动一个定时器,该定时器时长是2MSL(最大段报文生存时间),同时发送最后一次ACK报文。

为什么要四次挥手?

为什么不能用三次握手中捎带应答机制减少一次握手?

为何最后一次ACK之后需要等待2MSL的时间?

为何是2MSL的时间?

如果已经建立了连接,但是客户端突然出现故障了怎么办?

总结

三次握手和四次挥手的知识基本告一段落了,就讲到这里了,如果有什么不明白的地方可以加我微信探讨。

后面还会出一篇网络编程常用的linux命令行工具,比如ping、tcpdump、netstat、nc等等,在出一篇计算机网络的总结文章。计算机网络这部分基本完结了,如果有不懂得可以看看公号里面前面的文章。

04-15 09:07