UNP——第四章,TCP套接字编程
阅读原文时间:2023年07月09日阅读:3

1.socket 函数

  首先被调用的函数,用于选择通信协议。

  socket调用成功后,得到的套接字为主动套接字CLOSED状态。

  PF 和 AF 的关系

    PF的是协议族,AF是地址族,理论上一个PF包括多个AF,但实际上一个PF只实现了一个AF,所以两者在编程上是同等的。

2.connect

  connect 发起主动握手,

  connect 调用时,TCP 发送 SYN

  TCP接收到 ACK时,connect 返回。

  connect 前,可以不bind,内核会在connect 时随机分配原套接字。

  connect 开始时,TCP从 CLOSED --> SYN_SEND,成功时, SYN_SEND --> ESTABLISH

  每次 connect 失败后,都必须 close 套接字,并重新调用 socket。

3.bind

  设置源套接字地址,套接字状态不改变

  可以指定也可以不指定,不指定内核会分配,

  设置为0则为不指定,

  指定任意IP时,下面两种都一样,因为 INADDR_ANY == 0,大小端都一样

serv.sin_addr.s_addr = INADDR_ANY;
serv.sin_addr.s_addr = htonl(INADDR_ANY);

  bind 返回的常见错误是, EADDRINUSE (地址已使用)。

4.listen

  两个作用:

    (1)套接字状态从主动状态--> 被动状态 CLOSED --> LISTEN

  l  (2)isten 初始化排队的最大连接个数。

  内核为套接字分配了两个队列:

    未完成连接队列,即正在进行三次握手的,这些套接字处于 SYN_RECV 状态。

    已完成连接队列,即已经完成三次握手的,这些套接字处于 ESTABLISHED 状态。

  backlog >= 两队列总和

5.accept

  用于已连接队列头返回已连接。

  如果已连接队列为空,则进程随眠。

  accept 若成功,则返回内核重新分配的套接字。

6.典型的多进程并发服务器

for(;;) {
connfd = accept(listenfd, NULL, NULL);
if (fork() == 0) {
close(listenfd);
work();
close(connfd);
}
close(connfd);
}

父子进程都要 close 未用的套接字,以减少引用计数。

当 套接字关闭时,才会发出 FIN。

另外也避免了 文件描述符用尽。

7.close

减少套接字引用计数,当引用计数为0,则关闭套接字(TCP会发送FIN,对端TCP接受到FIN后,向应用层发送EOF)。

close后应用程序无法通过套接字继续读写操作。

但TCP会将剩余的数据进行收发。

8.getsockname 和 getpeername

通过 文件描述符,获得 套接字对信息。

用于子进程 exec 后,需要获得 父进程时的套接字对信息,因为套接字信息在内核区,不会被exec后覆盖,所以子进程只要获得 文件描述符即可(可通过命令行参数获得)