9.1 SO_SNDBUF & SO_RCVBUF

This commit is contained in:
riba2534
2019-01-19 11:26:27 +08:00
parent ce1f35fde9
commit e481face11
4 changed files with 189 additions and 0 deletions

118
README.md
View File

@@ -1916,7 +1916,125 @@ gcc gethostbyaddr.c -o hostaddr
## 第 9 章 套接字的多种可选项
本章代码,在[TCP-IP-NetworkNote](https://github.com/riba2534/TCP-IP-NetworkNote)中可以找到。
### 9.1 套接字可选项和 I/O 缓冲大小
我们进行套接字编程时往往只关注数据通信,而忽略了套接字具有的不同特性。但是,理解这些特性并根据实际需要进行更改也很重要
#### 9.1.1 套接字多种可选项
我们之前写得程序都是创建好套接字之后直接使用的,此时通过默认的套接字特性进行数据通信,这里列出了一些套接字可选项。
| 协议层 | 选项名 | 读取 | 设置 |
| :---------: | :---------------: | :--: | :--: |
| SOL_SOCKET | SO_SNDBUF | O | O |
| SOL_SOCKET | SO_RCVBUF | O | O |
| SOL_SOCKET | SO_REUSEADDR | O | O |
| SOL_SOCKET | SO_KEEPALIVE | O | O |
| SOL_SOCKET | SO_BROADCAST | O | O |
| SOL_SOCKET | SO_DONTROUTE | O | O |
| SOL_SOCKET | SO_OOBINLINE | O | O |
| SOL_SOCKET | SO_ERROR | O | X |
| SOL_SOCKET | SO_TYPE | O | X |
| IPPROTO_IP | IP_TOS | O | O |
| IPPROTO_IP | IP_TTL | O | O |
| IPPROTO_IP | IP_MULTICAST_TTL | O | O |
| IPPROTO_IP | IP_MULTICAST_LOOP | O | O |
| IPPROTO_IP | IP_MULTICAST_IF | O | O |
| IPPROTO_TCP | TCP_KEEPALIVE | O | O |
| IPPROTO_TCP | TCP_NODELAY | O | O |
| IPPROTO_TCP | TCP_MAXSEG | O | O |
从表中可以看出,套接字可选项是分层的。
- IPPROTO_IP 可选项是IP协议相关事项
- IPPROTO_TCP 层可选项是 TCP 协议的相关事项
- SOL_SOCKET 层是套接字的通用可选项。
#### 9.1.2 `getsockopt` & `setsockopt`
可选项的读取和设置通过以下两个函数来完成
```c
#include <sys/socket.h>
int getsockopt(int sock, int level, int optname, void *optval, socklen_t *optlen);
/*
成功时返回 0 ,失败时返回 -1
sock: 用于查看选项套接字文件描述符
level: 要查看的可选项协议层
optname: 要查看的可选项名
optval: 保存查看结果的缓冲地址值
optlen: 向第四个参数传递的缓冲大小。调用函数候,该变量中保存通过第四个参数返回的可选项信息的字节数。
*/
```
上述函数可以用来读取套接字可选项下面的函数可以更改可选项d
```c
#include <sys/socket.h>
int setsockopt(int sock, int level, int optname, const void *optval, socklen_t optlen);
/*
成功时返回 0 ,失败时返回 -1
sock: 用于更改选项套接字文件描述符
level: 要更改的可选项协议层
optname: 要更改的可选项名
optval: 保存更改结果的缓冲地址值
optlen: 向第四个参数传递的缓冲大小。调用函数候,该变量中保存通过第四个参数返回的可选项信息的字节数。
*/
```
下面的代码可以看出 getsockopt 的使用方法。下面示例用协议层为 SOL_SOCKET 、名为 SO_TYPE 的可选项查看套接字类型TCP 和 UDP )。
- [sock_type.c](https://github.com/riba2534/TCP-IP-NetworkNote/blob/master/ch09/sock_type.c)
编译运行:
```shell
gcc sock_type.c -o sock_type
./sock_type
```
结果:
```
SOCK_STREAM: 1
SOCK_DGRAM: 2
Socket type one: 1
Socket type two: 2
```
首先创建了一个 TCP 套接字和一个 UDP 套接字。然后通过调用 getsockopt 函数来获得当前套接字的状态。
验证套接类型的 SO_TYPE 是只读可选项,因为**套接字类型只能在创建时决定,以后不能再更改**。
#### 9.1.3 `SO_SNDBUF` & `SO_RCVBUF`
创建套接字的同时会生成 I/O 缓冲。关于 I/O 缓冲,可以去看第五章。
SO_RCVBUF 是输入缓冲大小相关可选项SO_SNDBUF 是输出缓冲大小相关可选项。用这 2 个可选项既可以读取当前 I/O 大小,也可以进行更改。通过下列示例读取创建套接字时默认的 I/O 缓冲大小。
- [get_buf.c](https://github.com/riba2534/TCP-IP-NetworkNote/blob/master/ch09/get_buf.c)
编译运行:
```shell
gcc get_buf.c -o getbuf
./getbuf
```
运行结果:
```
Input buffer size: 87380
Output buffer size: 16384
```
可以看出本机的输入缓冲和输出缓冲大小。

1
ch09/README.md Normal file
View File

@@ -0,0 +1 @@
## 第 9 章 套接字的多种可选项

34
ch09/get_buf.c Normal file
View File

@@ -0,0 +1,34 @@
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>
void error_handling(char *message);
int main(int argc, char *argv[])
{
int sock;
int snd_buf, rcv_buf, state;
socklen_t len;
sock = socket(PF_INET, SOCK_STREAM, 0);
len = sizeof(snd_buf);
state = getsockopt(sock, SOL_SOCKET, SO_SNDBUF, (void *)&snd_buf, &len);
if (state)
error_handling("getsockopt() error");
len = sizeof(rcv_buf);
state = getsockopt(sock, SOL_SOCKET, SO_RCVBUF, (void *)&rcv_buf, &len);
if (state)
error_handling("getsockopt() error");
printf("Input buffer size: %d \n", rcv_buf);
printf("Output buffer size: %d \n", snd_buf);
return 0;
}
void error_handling(char *message)
{
fputs(message, stderr);
fputc('\n', stderr);
exit(1);
}

36
ch09/sock_type.c Normal file
View File

@@ -0,0 +1,36 @@
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>
void error_handling(char *message);
int main(int argc, char *argv[])
{
int tcp_sock, udp_sock;
int sock_type;
socklen_t optlen;
int state;
optlen = sizeof(sock_type);
tcp_sock = socket(PF_INET, SOCK_STREAM, 0);
udp_sock = socket(PF_INET, SOCK_DGRAM, 0);
printf("SOCK_STREAM: %d\n", SOCK_STREAM);
printf("SOCK_DGRAM: %d\n", SOCK_DGRAM);
state = getsockopt(tcp_sock, SOL_SOCKET, SO_TYPE, (void *)&sock_type, &optlen);
if (state)
error_handling("getsockopt() error");
printf("Socket type one: %d \n", sock_type);
state = getsockopt(udp_sock, SOL_SOCKET, SO_TYPE, (void *)&sock_type, &optlen);
if (state)
error_handling("getsockopt() error");
printf("Socket type two: %d \n", sock_type);
return 0;
}
void error_handling(char *message)
{
fputs(message, stderr);
fputc('\n', stderr);
exit(1);
}