socket编程缓冲区大小对send()的影响

本文探讨了socket编程中缓冲区大小如何影响send()函数的性能。在Blocking模式和Non-Blocking模式下,分别进行了实验,通过详细步骤、实验数据和结论,揭示了缓冲区大小变化对数据发送和接收效率的影响。

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 }
### 缓冲区大小对传输速率的影响分析 在网络编程中,缓冲区大小的选择直接影响数据传输的效率和性能。以下将从理论和实践两个方面详细探讨缓冲区大小对传输速率的影响,并通过C语言示例代码展示其具体作用。 #### 理论分析 缓冲区大小决定了每次读取或发送数据的块大小。较大的缓冲区可以减少系统调用次数,从而降低CPU开销,但过大的缓冲区会增加内存占用并可能导致网络拥塞[^1]。相反,较小的缓冲区虽然减少了内存使用,但会增加系统调用频率,从而导致更高的CPU负载和更长的传输时间[^2]。 通常建议将缓冲区大小设置为8KB或16KB,因为这些值与大多数操作系统的默认TCP窗口大小相匹配,能够达到较好的性能平衡[^3]。 #### C语言示例代码 以下示例展示了如何通过调整缓冲区大小来观察其对文件传输速率的影响。 ##### 服务器端代码 服务器端负责接收来自客户端的数据并保存到本地文件中。 ```c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <arpa/inet.h> #define BUF_SIZE 8192 // 缓冲区大小设置为8KB int main() { int serv_sock, clnt_sock; struct sockaddr_in serv_addr, clnt_addr; socklen_t clnt_addr_size; FILE *fp; serv_sock = socket(AF_INET, SOCK_STREAM, 0); memset(&serv_addr, 0, sizeof(serv_addr)); serv_addr.sin_family = AF_INET; serv_addr.sin_addr.s_addr = htonl(INADDR_ANY); serv_addr.sin_port = htons(1234); bind(serv_sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr)); listen(serv_sock, 5); clnt_addr_size = sizeof(clnt_addr); clnt_sock = accept(serv_sock, (struct sockaddr*)&clnt_addr, &clnt_addr_size); fp = fopen("received_file", "wb"); if (!fp) { perror("fopen"); return -1; } char buf[BUF_SIZE]; int read_len; while ((read_len = recv(clnt_sock, buf, BUF_SIZE, 0)) > 0) { fwrite(buf, 1, read_len, fp); // 将接收到的数据写入文件 } fclose(fp); close(clnt_sock); close(serv_sock); return 0; } ``` ##### 客户端代码 客户端负责将本地文件发送到服务器。 ```c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <arpa/inet.h> #define BUF_SIZE 8192 // 缓冲区大小设置为8KB int main() { int sock; struct sockaddr_in serv_addr; FILE *fp; sock = socket(AF_INET, SOCK_STREAM, 0); memset(&serv_addr, 0, sizeof(serv_addr)); serv_addr.sin_family = AF_INET; serv_addr.sin_addr.s_addr = inet_addr("127.0.0.1"); serv_addr.sin_port = htons(1234); connect(sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr)); fp = fopen("file_to_send", "rb"); if (!fp) { perror("fopen"); return -1; } char buf[BUF_SIZE]; int read_len; while ((read_len = fread(buf, 1, BUF_SIZE, fp)) > 0) { send(sock, buf, read_len, 0); // 发送文件数据 } fclose(fp); close(sock); return 0; } ``` #### 实验与结果分析 可以通过改变`BUF_SIZE`的值(如4KB、8KB、16KB等)来观察不同缓冲区大小对传输速率的影响。通常情况下,随着缓冲区大小的增加,传输速率会先提高后趋于平稳,最终可能因网络拥塞或其他因素而下降[^3]。 #### 性能优化建议 - **选择合适的缓冲区大小**:根据实际应用场景和硬件条件选择最佳缓冲区大小。 - **使用非阻塞I/O模型**:避免因等待数据导致的线程阻塞。 - **实现多线程或异步传输**:充分利用系统资源以提高传输效率。 --- ###
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值