本文介绍的内容是在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 协议 ,转载请注明出处!

管道 上一篇
socket应用编程(1) 下一篇