APUE第16章 套接字
上一章的IPC仅限于本机的进程间通讯,这一章讲述的是网络间的进程间通讯(network IPC)
套接字描述符
Socket描述符在UNIX系统中被当做是一种文件描述符,有很多文件I/O相关函数可以直接用,但跟偏移相关等的函数不行。
1 |
|
shutdown函数的应用和close的区别
- close函数只是减少引用计数1,只有引用计数为0了内核才会发送FIN包;而shutdown会影响所有持有这个socket的进程(如通过fork函数得到的)
- shutdown可以只关闭读端或者写端,close不可设置选项
shutdown后的影响
来自于Stack Overflow:
Shutting down the read side of a socket will cause any blocked recv (or similar) calls to return 0 (indicating graceful shutdown). I don’t know what will happen to data currently traveling up the IP stack. It will most certainly ignore data that is in-flight from the other side. It will not affect writes to that socket at all.
socket中遇到SIGPIPE信号
- 当服务器close一个连接时,若client端接着发数据。根据TCP协议的规定,会收到一个RST响应,client再往这个服务器发送数据时,系统会发出一个SIGPIPE信号给进程,告诉进程这个连接已经断开了,不要再写了。
- 又或者当一个进程向某个已经收到RST的socket执行写操作是,内核向该进程发送一个SIGPIPE信号。该信号的缺省行为是终止进程,因此进程必须捕获它以免不情愿的被终止。
- 检测方式:先设置忽略该信号,然后read或者write失败的话检测errno是不是EPIPE。
- 另外,如果使用多进程模型且父进程不想关心子进程的话,为防止子进程崩溃产生了僵死进程,应该设置忽略SIGCHLD信号。
数据链路层的包监听
sock_raw(注意一定要在root下使用)原始套接字编程可以接收到本机网卡上的数据帧或者数据包,对于监听网络的流量和分析是很有作用的。可用下列方式创建这种socket
SOCK_RAW, IPPROTO_TCP|IPPROTO_UDP|IPPROTO_ICMP)```发送接收ip数据包,不能用IPPROTO_IP,因为如果是用了IPPROTO_IP,系统根本就不知道该用什么协议。 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
172. ```socket(PF_PACKET, SOCK_RAW, htons(ETH_P_IP|ETH_P_ARP|ETH_P_ALL))```发送接收以太网数据帧
3. 如socket的第一个参数使用PF_INET,第二个参数使用SOCK_RAW,则可以得到原始的IP包。
## 寻址
### 字节序
1. 大端:高字节地址放低位,低字节地址放高位
2. 小端:高字节地址放高位,低字节地址放低位
```cpp
#include <arpa/inet.h>
//host to network in long
uint32_t htonl(uint32_t hostint32);
//host to network in short
uint16_t htons(uint16_t hostint16);
//network to host in long
uint32_t ntohl(uint32_t netint32);
//network to host in short
uint16_t ntohs(uint16_t netint16);
地址格式
1 | struct sockaddr{ |
地址查询
1 |
|
bind
用于关联套接字和地址
1 |
|
连接
1 |
|
- 如果connect的时候,sockfd没有绑定到一个地址,connect会给调用者默认绑定一个地址。
- 对于UDP,也可调用connect,这样每次发送的时候就不需要一直填地址。
监听
1
2
3
4
5
int listen(int sockfd, int backlog);
//backlog表示所要入队的未完成连接的请求数量,当队列满时,就会拒绝连接请求
int accept(int sockfd, struct sockaddr *restrict addr, socklen_t *restrict len);
//成功返回套接字(文件)描述符,出错返回-1
数据传输
- send
- sendto
- sendmsg
- recv
- recvfrom
- recvmsg
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//For send
ssize_t send(int sockfd, const void *buf, size_t nbytes, int flags);
ssize_t sendto(int sockfd, const void *buf, size_t nbytes, int flags, conststruct sockaddr *destaddr, socklen_t destlen);
ssize_t sendmsg(int sockfd,const struct msghdr *msg, int flags);
struct msghdr{
void *msg_name;
socklen_t msg_namelen;
struct iovec *msg_iov;
int msg_iovlen;
void *msg_control;
socklen_t msg_controllen;
int msg_flags;
//.......
};
//For receive
ssize_t recv(int sockfd, void *buf, size_t nbytes, int flags);
//出错返回-1,无数据返回0
ssize_t recvfrom(int sockfd, void *restrict buf, size_t len, int flags, struct sockaddr *restict addr, socklen_t *restrict addrlen);
ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags);
套接字选项
1 |
|
level的详细请见《APUE》P503,另外这个函数通常作用是来设置SO_REUSEADDR和SO_REUSEPORT等选项。两者的区别和相关的影响见0517日报中的相关内容。
带外数据
带外数据一般是针对TCP而言的,相当于紧急数据(urgent data),这些数据会优先发送,哪怕传输队列已经有数据。TCP仅支持一个字节的紧急数据,只需在发送函数中指定MSG_OOB的标识,当发送内容超过一个字节,只有最后一个字节被视为紧急数据。上一个紧急数据未处理又收到一个,会被覆盖。详见《APUE》16.7
1 |
|
查了下应用场景,比较特殊,如远程传输文件突然想取消,则使用这种方式
非阻塞I/O
设置接受和发送为异步操作的顺序