本文介绍的内容是在socket应用编程(1)的基础上进行一些错误的分析,返回值的判断以及对基本的API进行封装,目的是增强服务器的鲁棒性,可以应对复杂的网络环境,理解TCP/IP的11种状态以及使用代码复现某些状态是非常关键的,可以帮助你在发送错误的时候发现问题出在了什么地方。
本文除了介绍socket应用编程进阶的一些知识点之外,还将介绍TCP/IP协议的11种状态
netstat -na | grep xxx可以查看某个端口的连接状态,xxx表示端口
图片来自于xmind导出的svg格式,右键打开图片,查看大图。。。
TCP/IP协议的11种状态
TCP/IP的这11种状态的某些中间状态是TCP/IP协议栈自动推着连接往前走的,不需要上层参与,但是对于一些没有输入箭头的状态,如图中的FIN_WAIT_1等状态需要上层应用调用close才能使连接往下进行
三次握手
- TCP/IP协议需要确定双方的身份,服务器先监听套接字,客户端发起连接,会发生SYN报文到客户端,客户端返回一个SYN报文和一个应答信号给客户端,然后双方进入ESTABLSH状态,至此已经完成连接,accept从完成连接的队列里拿出连接并返回一个新的套接字
四次断开
- 客户端(或服务器)显式的调用close系统调用(前提是引用计数减少为0),会发送FIN报文给服务器,服务器接收到FIN报文时会返回ACK信号给客户端,同时服务器read调用会返回0,此时,客户端接收到ACK报文会到达FIN_WAIT_2状态,也就是半连接状态,客户端必须一直等服务器显式调用close之后才能进入TIME_WAIT状态,同时客户端会返回ACK信号给服务器,服务器会进入CLOSED状态,客户端的TIME_WAIT状态会持续一段时间,原因防止(ACK y+1)发送失败,便于重发。
- 通过read调用是否返回0来判断对方是否已经关闭,如果返回0,则调用close,关闭套接字
CLOSING状态
- 上图少了一种状态,就是closing状态,两端同时关闭将产生closing状态,最后双方都进入TIME_WAIT状态
socket编程对于返回值的判断十分关键,这可以精准的定位到问题所在。
select函数用法
#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
int main(void)
{
fd_set rfds;
struct timeval tv;
int retval;
/* Watch stdin (fd 0) to see when it has input. */
FD_ZERO(&rfds);
FD_SET(0, &rfds);
/* Wait up to five seconds. */
tv.tv_sec = 5;
tv.tv_usec = 0;
retval = select(1, &rfds, NULL, NULL, &tv);
/* Don't rely on the value of tv now! */
if (retval == -1)
perror("select()");
else if (retval)
printf("Data is available now.\n");
/* FD_ISSET(0, &rfds) will be true. */
else
printf("No data within five seconds.\n");
exit(EXIT_SUCCESS);
}
- 通过select管理标准输入,5s内无输入则返回,代码来自man手册示例程序。
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!