TCP
三次握手,四次挥手
TCP server
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <netdb.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <arpa/inet.h>
int main(int argc, char *argv[])
{
int skfd, cnfd, addr_len;
struct sockaddr_in srv_addr, clt_addr;
int portnumber=8888;
char hello[] = "Hello! Long time no see.\n";
if (2 != argc || 0 > (portnumber = atoi(argv[1])))
{
printf("Usage:%s prot\n", argv[0]);
exit(1);
}
/* 创建IPv4的流式套接字描述符 */
if (-1 == (skfd = socket(AF_INET, SOCK_STREAM, 0)))
{
perror("Socket Error:");
exit(1);
}
/* 填充服务器端sockaddr地址结构 */
bzero(&srv_addr, sizeof(struct sockaddr_in));
srv_addr.sin_family = AF_INET;
srv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
srv_addr.sin_port = htons(portnumber);
/* 将套接字描述符skfd和地址信息结构体绑定起来 */
if (-1 == bind(skfd, (struct sockaddr *)(&srv_addr), sizeof(struct sockaddr)))
{
perror("Bind error");
exit(1);
}
/* 将skfd转换为被动监听模式 */
if (-1 == listen(skfd, 4))
{
perror("Listen error:");
exit(1);
}
while (1)
{
/* 调用accept,服务器端一直阻塞,直到客户程序与其建立连接成功为止 */
addr_len = sizeof(struct sockaddr_in);
if (-1 == (cnfd = accept(skfd, (struct sockaddr *)(&clt_addr), &addr_len)))
{
perror("Accept error:");
exit(1);
}
printf("Connect from %s:%u ...!\n", inet_ntoa(clt_addr.sin_addr), ntohs(clt_addr.sin_port));
if (-1 == write(cnfd, hello, strlen(hello)))
{
perror("Send error");
exit(1);
}
close(cnfd);
}
close(skfd);
exit(0);
}
GDB调试
抓包分析
开启远程抓包,连接tcpServer
./rpcapd -n -d -p10033
nc 192.168.146.129 8888
三次握手建立连接
传输数据,回复ACK,序号
四次挥手断开连接
TCP状态
三次握手状态
四次挥手状态
11种状态说明
半关闭
主动方发生在FIN_WAIT_2状态,这个状态时,主动方不可以在应用层发送数据了,但是应用层还可以接收数据,这个状态称为半关闭
#include <sys/socket.h>
int shutdown(int sockfd, int how);
sockfd: 需要关闭的socket的描述符
how: 允许为shutdown操作选择以下几种方式:
SHUT_RD(0): 关闭sockfd上的读功能,此选项将不允许sockfd进行读操作。
该套接字不再接收数据,任何当前在套接字接受缓冲区的数据将被无声的丢弃掉。
SHUT_WR(1): 关闭sockfd的写功能,此选项将不允许sockfd进行写操作。进程不能在对此套接字发出写操作。
SHUT_RDWR(2): 关闭sockfd的读写功能。相当于调用shutdown两次:首先是以SHUT_RD,然后以SHUT_WR
2MSL
2MSL即两倍的MSL,TCP的TIME_WAIT状态也称为2MSL等待状态,当TCP的一端发起主动关闭,在发出最后一个ACK包后,
即第3次握 手完成后发送了第四次握手的ACK包后就进入了TIME_WAIT状态,必须在此状态上停留两倍的MSL时间,
等待2MSL时间主要目的是怕最后一个 ACK包对方没收到,那么对方在超时后将重发第三次握手的FIN包,主动关闭端接到重发的FIN包后可以再发一个ACK应答包。
在TIME_WAIT状态 时两端的端口不能使用,要等到2MSL时间结束才可继续使用。当连接处于2MSL等待阶段时任何迟到的报文段都将被丢弃。
不过在实际应用中可以通过设置 SO_REUSEADDR选项达到不必等待2MSL时间结束再使用此端口
ime_wait状态到底持续多长时间?
MSL 为一个数据包在网络中存活的最长时间,一般1-2分钟的样子;RFC 793中规定MSL为2分钟,实际应用中常用的是30秒,1分钟和2分钟等。
心跳包
如果对方异常断开,本机检测不到,一直等待,浪费资源
需要设置tcp的保持连接,作用就是每隔一定的时间间隔发送探测分节,如果连续发送多个探测分节对方还未回,就将次连接断开
int keepAlive = 1;
setsockopt(listenfd, SOL_SOCKET, SO_KEEPALIVE, (void*)&keepAlive, sizeof(keepAlive));
心跳包: 最小粒度
乒乓包: 携带比较多的数据的心跳包
端口利用
int opt = 1;
setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
注意: 程序中设置某个端口重新使用,在这之前的其他网络程序将不能使用这个端口