1. 概述
Socket編程中,使用send()傳送數據時,返回結果受到以下幾個因素的影響:• Blocking模式或non-blocking模式
• 發送緩衝區的大小
• 接收窗口大小
本文檔介紹通過實驗的方式,得出(收發)緩衝區大小對send結果的影響。實驗使用C語言。
2 數據發送和接收的過程
如下圖所示,程序調用send()發送數據時,數據將首先進入發送緩衝區,等待發送。系統底層socket負責數據的傳送,數據通過網絡到達接收方的緩衝區。接收方緩衝區內的數據,等待應用程序調用recv()讀取。
3 實驗一:Blocking模式下
3.1 實驗步驟
發送端:blocking模式send()發送8192字節,使用setsockopt設置SO_SNDBUF改變發送緩衝區的大小。接收端:建立連接後進入睡眠。使用setsockopt設置SO_RCVBUF改變接收緩衝區的大小。
3.2 實驗得到的數據
3.3實驗結論
“已發送字節 + 緩衝區中待發送字節 > 總字節”時,send()能立即返回,否則處於阻塞等待。4 實驗二:Non-Blocking模式下
4.1 實驗步驟
發送端:non-blocking模式send()發送8192字節,使用setsockopt設置SO_SNDBUF改變發送緩衝區的大小。接收端:建立連接後進入睡眠。使用setsockopt設置SO_RCVBUF改變接收緩衝區的大小。
4.2 實驗數據
4.3 實驗結論
隨着SNDBUF增大,send()返回已發送字節越大。接收窗口大小對結果影響不是線性的。實際已接收的只有窗口大小。5. 實驗原代碼
5.1 服務器端代碼
1 #include <netinet/in.h>2 #include <sys/socket.h>
3 #include <stdio.h>
4 #include <stdlib.h>
5 #include <stdint.h>
6 #include <string.h>
7 #include <errno.h>
8
9 int
10 init_server(int type, const struct sockaddr_in *addr)
11 {
12 int fd;
13 int err = 0;
14 int reuse = 1;
15
16 if ((fd = socket(AF_INET, type, 0)) < 0) {
17 printf("Failed to create socket.\n");
18 exit(1);
19 }
20 if (bind(fd, (struct sockaddr *)addr, sizeof(*addr)) < 0) {
21 printf("Server Bind Failed: %d\n", errno);
22 exit(1);
23 }
24 if (listen(fd, 5) < 0) {
25 printf("Fail to listen\n");
26 exit(1);
27 }
28 return fd;
29 }
30
31 void
32 serve(int fd, int n_to_send, int s_buf_size, int flag)
33 {
34 int clfd, clfd_2;
35 struct sockaddr_in client_addr;
36 char *buf;
37 const char *addr;
38 socklen_t alen = sizeof(int);
39 int n=0;
40 int i;
41 ssize_t num;
42
43 /* initialize the send buffer */
44 buf = malloc(n_to_send * sizeof(char));
45 for (i = 0; i < n_to_send; i++)
46 buf[i] = '=';
47
48 /* change the send buffer size */
49 getsockopt(fd, SOL_SOCKET, SO_SNDBUF, (char *)&n, &alen);
50 printf("SEND buffer size: %d\n", n);
51 getsockopt(fd, SOL_SOCKET, SO_SNDLOWAT, (char *)&n, &alen);
52 printf("SEND LOWAT size: %d\n", n);
53 if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, (char *)&s_buf_size, sizeof(int)) < 0) {
54 printf("fail to change SNDbuf.\n");
55 exit(2);
56 }
57 getsockopt(fd, SOL_SOCKET, SO_SNDBUF, (char *)&n, &alen);
58 printf("Current SEND buffer size: %d\n", n);
59
60 /* loop: accept a client, and send buffer to it */
61 while(1) {
62 printf("waiting for clients...\n");
63 clfd = accept(fd, (struct sockaddr*)&client_addr, &alen);
64 if(clfd < 0) {
65 printf("accept error.");
66 exit(4);
67 }
68 printf("new client\n");
69 printf("Sending %d bytes...\n", n_to_send);
70 i = send(clfd, buf, n_to_send, flag);
71 printf("send %d bytes.\n", i);
72 close(clfd);
73 printf("\n\n");
74 }
75 }
76
77 int
78 main(int argc, char *argv[])
79 {
80 char host[] = "127.0.0.1";
81 struct sockaddr_in server_addr;
82 uint32_t s_addr;
83 int fd;
84 int n_to_send, s_buf_size, flag;
85
86 if (argc != 4) {
87 printf("useage %s <num to send> <send buf size> <wait_flag:1|0>\n", argv[0]);
88 exit(1);
89 }
90 n_to_send = atoi(argv[1]);
91 s_buf_size = atoi(argv[2]);
92 flag = atoi(argv[3]) ? 0 : MSG_DONTWAIT;
93
94 bzero(&server_addr, sizeof(server_addr));
95 inet_pton(AF_INET, host, &s_addr);
96 server_addr.sin_family = AF_INET;
97 server_addr.sin_addr.s_addr = s_addr;
98 server_addr.sin_port = htons(9000);
99
100 fd = init_server(SOCK_STREAM, &server_addr);
101 serve(fd, n_to_send, s_buf_size, flag);
102
103 exit(0);
104 }
5.2 客戶端代碼
1 #include <netinet/in.h>2 #include <unistd.h>
3 #include <errno.h>
4 #include <sys/socket.h>
5 #include <stdio.h>
6 #include <stdlib.h>
7 #include <string.h>
8 #include <stdint.h>
9
10 #define SLEEP_TIME 120
11
12 void
13 tcp_client(int sockfd, struct sockaddr_in *s_addr, int r_buf_size)
14 {
15 int n, i;
16 socklen_t slen = sizeof(int);
17 int rcv_len;
18
19 /* change receiving buffer size */
20 getsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, &rcv_len, &slen);
21 printf("Receive buffer size: %d\n", rcv_len);
22 setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, &r_buf_size, sizeof(int));
23 getsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, &rcv_len, &slen);
24 printf("Current Receive buffer size: %d\n", rcv_len);
25
26 /* connect to server */
27 if(connect(sockfd, (struct sockaddr*)s_addr, sizeof(*s_addr))) {
28 printf("Can not connect to server.\n");
29 exit(2);
30 }
31
32 sleep(SLEEP_TIME);
33 }
34
35 int
36 main(int argc, char *argv[])
37 {
38 char server_ip[] = "127.0.0.1";
39 int port = 9000;
40 uint32_t s_addr;
41 int s_fd;
42 struct sockaddr_in server_addr;
43 int r_buf_size;
44
45 if (argc != 2) {
46 printf("usage: %s <recv_buf_size>\n", argv[0]);
47 exit(1);
48 }
49 r_buf_size = atoi(argv[1]);
50
51 bzero(&server_addr, sizeof(server_addr));
52 inet_pton(AF_INET, server_ip, &s_addr);
53 server_addr.sin_family = AF_INET;
54 server_addr.sin_addr.s_addr = s_addr;
55 server_addr.sin_port = htons(port);
56 if ((s_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
57 printf("Fail to create socket.");
58 exit(1);
59 }
60
61 tcp_client(s_fd, &server_addr, r_buf_size);
62 exit(0);
63 }