1.socket 函数
family
AF_INET IPv4
AF_INET6 IPv6
AF_LOCAL Unix域
AF_ROUTE 路由套接字
AF_KEY 密钥套接字
type
SOCK_STREAM 字节流
SOCK_DGRAM 数据报
SOCK_SEQPACKET 有序分组
SOCK_RAW 原始套接字
AF_INET 和 AF_INET6 的 protocol
IPPROTO_TCP
IPPROTO_UDP
IPPROTO_SCTP
(2)AF_xxx 和 PF_xxx
AF_ 表示地址族,PF_xxx 表示协议族,但实际上 AF_ 和 PF_ 相同。
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
listen 初始化排队的最大连接个数。
内核为套接字分配了两个队列:
未完成连接队列,即正在进行三次握手的,这些套接字处于 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会将剩余的数据进行收发。
8.getsockname 和 getpeername
通过 文件描述符,获得 套接字对信息。
用于子进程 exec 后,需要获得 父进程时的套接字对信息,因为套接字信息在内核区,不会被exec后覆盖,所以子进程只要获得 文件描述符即可(可通过命令行参数获得)