UNIX网络编程:TCP回射服务器/客户端程序
先编译运行服务器: huangcheng@ubuntu:~$./serv 然后在另一个终端里用netstat命令查看: huangcheng@ubuntu:~$ netstat -anp | grep 5188 (并非所有进程都能被检测到,所有非本用户的进程信息将不会显示,如果想看到所有信息,则必须切换到 root 用户) tcp 0 0 0.0.0.0:5188 0.0.0.0:* LISTEN 2998/serv 可以看到server程序监听5188端口,IP地址还没确定下来。现在编译运行客户端: huangcheng@ubuntu:~$ ./cli 回到server所在的终端,看看server的输出: huangcheng@ubuntu:~$ ./serv recv connect ip=127.0.0.1 port=42107 可见客户端的端口号是自动分配的。再次netstat 一下: huangcheng@ubuntu:~$ netstat -anp | grep 5188 (并非所有进程都能被检测到,所有非本用户的进程信息将不会显示,如果想看到所有信息,则必须切换到 root 用户) tcp 0 0 0.0.0.0:5188 0.0.0.0:* LISTEN 2998/serv tcp 0 0 127.0.0.1:5188 127.0.0.1:42107 ESTABLISHED 2998/serv tcp 0 0 127.0.0.1:42107 127.0.0.1:5188 ESTABLISHED 3198/cli 应用程序中的一个socket文件描述符对应一个socket pair,也就是源地址:源端口号和目的地址:目的端口号,也对应一个TCP连接。 上面第一行即serv.c 中的listenfd;第二行即serv.c 中的sock; 第三行即cli 中的conn。2998和3198分别是进程id。 现在来做个测试,先serv.c中的把33~35行的代码注释掉。 首先启动server,然后启动client,然后用Ctrl-C使server终止,这时马上再运行server,结果是: huangcheng@ubuntu:~$ ./serv bind error: Address already in use 这是因为,虽然server的应用程序终止了,但TCP协议层的连接并没有完全断开,因此不能再次监听同样的server端口。我们用netstat命令查看一下: huangcheng@ubuntu:~$ netstat -anp | grep 5188 (并非所有进程都能被检测到,所有非本用户的进程信息将不会显示,如果想看到所有信息,则必须切换 到 root 用户) tcp 0 0 127.0.0.1:5188 127.0.0.1:42108 FIN_WAIT2 - tcp 1 0 127.0.0.1:42108 127.0.0.1:5188 CLOSE_WAIT 3260/cli server终止时,socket描述符会自动关闭并发FIN段给client,client收到FIN后处于CLOSE_WAIT状态,但是client并没有终止,也没有关闭socket描述符,因此不会发FIN给server,因此server的TCP连接处于FIN_WAIT2状态。 现在用Ctrl-C把client也终止掉,再观察现象: huangcheng@ubuntu:~$ netstat -anp | grep 5188 (并非所有进程都能被检测到,所有非本用户的进程信息将不会显示,如果想看到所有信息,则必须切换到 root 用户) tcp 0 0 127.0.0.1:5188 127.0.0.1:42108 TIME_WAIT - huangcheng@ubuntu:~$ ./serv bind error: Address already in use client终止时自动关闭socket描述符,server的TCP连接收到client发的FIN段后处于TIME_WAIT状态。TCP协议规定,主动关闭连接的一方要处于TIME_WAIT状态,等待两个MSL(maximumsegment lifetime)的时间后才能回到CLOSED状态,需要有MSL 时间的主要原因是在这段时间内如果最后一个ack段没有发送给对方,则可以重新发送。因为我们先Ctrl-C终止了server,所以server是主动关闭连接的一方,在TIME_WAIT期间仍然不能再次监听同样的server端口。MSL在RFC1122中规定为两分钟,但是各操作系统的实现不同,在Linux上一般经过半分钟后就可以再次启动server了。至于为什么要规定TIME_WAIT的时间请大家参考UNP 2.7节。 在server的TCP连接没有完全断开之前不允许重新监听是不合理的,因为,TCP连接没有完全断开指的是connfd(127.0.0.1:5188)没有完全断开,而我们重新监听的是listenfd(0.0.0.0:5188),虽然是占用同一个端口,但IP地址不同,connfd对应的是与某个客户端通讯的一个具体的IP地址,而listenfd对应的是wildcard address。解决这个问题的方法是使用setsockopt()设置socket描述符的选项SO_REUSEADDR为1,表示允许创建端口号相同但IP地址不同的多个socket描述符。将原来注释的33~35行代码打开,问题解决。 先运行服务器,在运行客户端: huangcheng@ubuntu:~$ ./serv recv connect ip=127.0.0.1 port=42107 host 127.0.0.1:5188 huangcheng ctt huangcheng@ubuntu:~$ ./cli host 127.0.0.1:42107 huangcheng huangcheng ctt ctt (编辑:云计算网_泰州站长网) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |