1. 程式人生 > >linux網路程式設計四:socket選項: SO_REUSEADDR, SO_RCVBUF, SO_SNDBUF

linux網路程式設計四:socket選項: SO_REUSEADDR, SO_RCVBUF, SO_SNDBUF

最近在看《linux高效能伺服器程式設計》,在此做個日記,以激勵自己,同時分享於有需要的朋友。

1. 讀取和設定socket檔案描述符屬性:

#include <sys/socket.h>

int getsockopt(int sockfd, int level, int option_name, void *option_value, socklen_t *restrict option_len);

int setsockopt(int sockfd, int level, int option_name, const void *option_value, socklen_t option_len);
sockfd引數指定被操作的目標socket。

level引數指定要操作哪個協議的選項,即屬性,比如:IPv4, IPv6, TCP 和通用socket選項。

option_name引數指定選項的名字。

option_value引數指定選項的值。

option_len參引數指定選項的長度。

呼叫成功時返回0, 失敗時返回-1, 並設定errno。

2. 對伺服器而言,有部分socket選項只能在呼叫listen前設定才會有效。因為連線socket只能由accept呼叫返回,而accept從listen監聽佇列中接受的連線至少已經完成了TCP三次握手的前兩個步聚,listen監聽佇列的連線至少已進入SYN_RCVD狀態,這時伺服器已經往被連線上傳送TCP同步報文。

3. SO_REUSeADDR選項:重用本地地址

未設定此項前,若服務端開啟後,又關閉,此時sock處於TIME_WAIT狀態,與之繫結的socket地址不可重用,而導致再次開啟服務端失敗。

經過setsockopt設定之後, 即使處於TIME_WAIT些狀態也可以立即被重用。

int reuse = 1;
setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &reuse, sizof(reuse));

4. SO_RCVBUF 和 SO_SNDBUF :TCP接收緩衝區和傳送緩衝區的大小

當然,即使我們設定了這兩項的大小時, 系統都會自動將其加倍, 並且不得小於某個最小值。

TCP接收緩衝區的最小值是 256 位元組, 而傳送緩衝區的最小值是 2048 位元組。(不同系統可能會有差異)

這麼做的目的是確保一個TCP連線擁有足夠多的空閒緩衝區來處理擁塞。

setsockopt(sock, SOL_SOCKET, SO_SNDBUF, &sendbuf, sizeof(sendbuf));
getsockopt(sock, SOL_SOCKET, SO_SNDBUF, &sendbuf, (socklen_t*)&len);

setsockopt(sock, SOL_SOCKET, SO_RCVBUF, &recvbuf, sizeof(recvbuf));
getsockopt(sock, SOL_SOCKET, SO_RCVBUF, &recvbuf, (socklen_t*)&len);

5. 程式碼

//服務端
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <assert.h>

#define BUFFER_SIZE 1024

int main(int argc, char **argv)
{
	if (argc <= 3) {
		printf("Usage: %s ip port revc_size\n", basename(argv[0]));
		return 1;
	}
	
	const char *ip = argv[1];
	int port = atoi(argv[2]);
	
	struct sockaddr_in address;
	bzero(&address, sizeof(address));
	address.sin_family = AF_INET;
	address.sin_port = htons(port);
	inet_pton(AF_INET, ip, &address.sin_addr);
	
	int sock = socket(PF_INET, SOCK_STREAM, 0);
	assert(sock >= 0);

        //設定地址可重用
	int reuse = 1;
	setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &reuse, sizof(reuse));
	
	int recvbuf = atoi(argv[3]);
	int len = sizeof(recvbuf);
	
        //設定接受緩衝區大小
	setsockopt(sock, SOL_SOCKET, SO_RCVBUF, &recvbuf, sizeof(recvbuf));
        //獲取系統修改後的大小
        getsockopt(sock, SOL_SOCKET, SO_RCVBUF, &recvbuf, (socklen_t*)&len);
	printf("the receive buffer size after setting is %d\n", recvbuf);
	
	int ret = bind(sock, (struct sockaddr*)&address, sizeof(address));
	assert(ret != -1);
	
	ret = listen(sock, 5);
	assert(ret != -1);
	
	struct sockaddr_in client;
	socklen_t client_addrlength = sizeof(client);
	
	int connfd = accept(sock, (struct sockaddr*)&client, &client_addrlength);
	if (connfd < 0) {
		printf("errno is: %d\n", errno);
	}
	else {
		char buffer[BUFFER_SIZE];
		memset(buffer, '\0', BUFFER_SIZE);
		
		while (recv(connfd, buffer, BUFFER_SIZE-1, 0) > 0);
		
		printf("recv: %s\n", buffer);
		close(connfd);
	}
	
	close(sock);
	
	return 0;
}

執行後:

[email protected]:~/myproject/test_recv$ ./test_recv localhost 8000 50
the receive buffer size after setting is 2280

很明顯被修改過了, 我們給的50, 被改為2280。

//客戶端
#include <stdio.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <assert.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>

#define BUFFER_SIZE 1024

int main(int argc, char **argv)
{
	if(argc <= 3) {
		fprintf(stderr, "Usage: %s ip port send_buffer_size\n", 
				basename(argv[0]));
		return 1;
	}
	
	const char *ip = argv[1];
	int port = atoi(argv[2]);
	
	struct sockaddr_in server_address;
	bzero(&server_address, sizeof(server_address));
	server_address.sin_family = AF_INET;
	server_address.sin_port = htons(port);
	inet_pton(AF_INET, ip, &server_address.sin_addr);

	int sock = socket(PF_INET, SOCK_STREAM, 0);
	assert(sock >= 0);
	
	int sendbuf = atoi(argv[3]);
	int len = sizeof(sendbuf);
	
        //設定傳送緩衝區大小
	setsockopt(sock, SOL_SOCKET, SO_SNDBUF, &sendbuf, sizeof(sendbuf));
        //獲取系統修改後的大小
        getsockopt(sock, SOL_SOCKET, SO_SNDBUF, &sendbuf, (socklen_t*)&len);
	
	printf("the tcp send buffer size after setting is %d\n",
			sendbuf);
	
	if (connect(sock, (struct sockaddr*)&server_address, sizeof(server_address)) != -1) {
		char buffer[BUFFER_SIZE];
		memset(buffer, 'a', BUFFER_SIZE);
		send(sock, buffer, BUFFER_SIZE, 0);
	}
	else {
		printf("connect %s failed\n", ip);
	}
	
	close(sock);
	
	return 0;
}

執行後:

[email protected]:~/myproject/test_send$ ./test_send localhost 8000 2000
the tcp send buffer size after setting is 4000

給的是2000, 被改成4000了。

相關推薦

linux網路程式設計socket選項 SO_REUSEADDR, SO_RCVBUF, SO_SNDBUF

最近在看《linux高效能伺服器程式設計》,在此做個日記,以激勵自己,同時分享於有需要的朋友。 1. 讀取和設定socket檔案描述符屬性: #include <sys/socket.h> int getsockopt(int sockfd, int lev

linux網路程式設計二十socket選項SO_RCVTIMEO和SO_SNDTIMEO

SO_RCVTIMEO和SO_SNDTIMEO ,它們分別用來設定socket接收資料超時時間和傳送資料超時時間。 因此,這兩個

linux網路程式設計之用socket實現簡單客戶端和服務端的通訊(基於TCP)

一、介紹基於TCP協議通過socket實現網路程式設計常用API 1、讀者如果不是很熟悉,可以先看我之前寫的幾篇部落格,有socket,地址結構的理解,更加方便讀者理解 地址分別是: 2、socket(TCP)程式設計API簡介 1)、socket int s

linux網路程式設計之用socket實現簡單客戶端和服務端的通訊(基於UDP)

1、sendto和recvfrom函式介紹 sendto(經socket傳送資料) 相關函式 send , sendmsg,recv , recvfrom , socket 表頭檔案 #include < sys/types.h >#includ

Linux網路程式設計基礎API--socket檔案描述符API

《Linux高效能伺服器程式設計》閱讀筆記: 1. 建立socket   Linux系統上”一切皆是檔案“,socket也不例外,它是可讀/可寫/可控制/可關閉的檔案描述符。要實現socket通訊,雙方都需要建立各自的socket物件。 #include

Linux網路程式設計TCP客戶/伺服器模型及基本socket函式

TCP客戶/伺服器模型 TCP連線的分組交換 在使用socket API的時候應該清楚應用程式和TCP協議棧是如何互動的: 呼叫connect()會發出SYN段(SYN是TCP報文段頭部的一個標誌位,置為1) 阻塞的read()函式返回0就表明收到了FIN段 客戶端呼叫c

Linux網路程式設計socket程式設計簡介、網路位元組序及相關函式

Socket(套接字) socket可以看成是使用者程序與核心網路協議棧的程式設計介面(API函式)。 socket不僅可以用於本機的程序間通訊,還可以用於網路上不同主機的程序間通訊。 IPv4套接字地址結構 IPv4套接字地址結構通常也稱為“網際套接字地址結構”,它以

Linux網路程式設計(一)一個簡單的socket程式

伺服器: /* *tcp_server.c */ #include <stdio.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include

linux網路程式設計socket(十六)通過UNIX域套接字傳遞描述符和 sendmsg/recvmsg 函式

void send_fd(int sock_fd, int send_fd) {     int ret;     struct msghdr msg;     struct cmsghdr *p_cmsg;     struct iovec vec;     char cmsgbuf[CMSG_SPACE(

linux網路程式設計之posix 執行緒(posix 條件變數與互斥鎖 示例生產者--消費者問題

#include <unistd.h>#include <sys/types.h>#include <pthread.h>#include <semaphore.h>#include <stdlib.h>#include <stdio.h>

Linux網路程式設計使用select函式實現socket 收發資料

 所謂的回射是指:客戶端A向服務端B傳送資料,服務端B接收到資料之後,再將接收到的資料傳送回客戶端B。所謂的迭代伺服器,

網路程式設計基礎【day08】簡單socket例項(二)

本節內容 1、概述 2、socket例項 3、總結 一、概述   之前我們只是介紹了soket的概念和一些邏輯圖表,下面我們來看看,socket的客戶端和服務端到底是怎麼用的? 二、socket例項 2.1 客戶端 2.1.1 客戶端程式碼邏輯圖 2.1.2 客戶端程式碼

網路程式設計基礎【day09】socket接收大資料(五)

本節內容 1、概述 2、socket接收大資料 3、中文字元的坑 一、概述   上篇部落格寫到了,就是說當伺服器傳送至客戶端的資料,大於客戶端設定的資料,則就會把資料服務端發過來的資料剩餘資料存在IO緩衝區中,那我們如何解決這個問題呢?   有的同學就說了: 改大客戶端接收的資料的大小=&

網路程式設計基礎【day09】socket解決粘包問題之MD5(八)

本節內容 1、概述 2、程式碼實現 一、概述   上一篇部落格講到的用MD5來校驗還是用的之前解決粘包的方法,就是客戶端傳送一個請求,等待服務端的確認的這樣的一個笨方法。下面我們用另外一種方法:就是客戶端已經知道可接收多少資料了,既然客戶端已經知道接收多少資料了,那麼客戶端在接收資料的時候,正好接收已

網路程式設計基礎【day10】我是一個執行緒(

本節內容 1、第一回 初生牛犢 2、第二回 漸入佳境 3、第三回 虎口脫險 4、第四回 江湖再見 第一回 初生牛犢 我是一個執行緒,我一出生就被編了個號:0x3704,然後被領到一個昏暗的屋子裡,在這裡我發現了很多和我一模一樣的同伴。 我身邊的同伴0x6900 待的時間比較長,他帶著滄桑的口氣對

Linux網路程式設計——原始套接字例項MAC 頭部報文分析

通過《Linux網路程式設計——原始套接字程式設計》得知,我們可以通過原始套接字以及 recvfrom( ) 可以獲取鏈路層的資料包,那我們接收的鏈路層資料包到底長什麼樣的呢? MAC 頭部(有線區域網) 注意:CRC、PAD 在組包時可以忽略 鏈路層資料包的其中一

Linux 網路程式設計——原始套接字例項MAC 地址掃描器

如果 A (192.168.1.1 )向 B (192.168.1.2 )傳送一個數據包,那麼需要的條件有 ip、port、使用的協議(TCP/UDP)之外還需要 MAC 地址,因為在乙太網資料包中 MAC 地址是必須要有的。那麼怎樣才能知道對方的 MAC 地址?答案是:它通

Linux 網路程式設計——原始套接字例項傳送 UDP 資料包

乙太網(Ethernet)報文格式(MAC頭部報文格式): IP 報文格式: UDP 報文格式: 校驗和函式: /******************************************************* 功能:     

Linux網路程式設計 無連線和麵向連線協議的區別

網路程式設計中最基本的概念就是面向連線(connection-oriented)和無連線(connectionless)協議。儘管本質上來說,兩者之間的區別並不難理解,但對那些剛剛開始進行網路程式設計的人來說,卻是個很容易混淆的問題。這個問題與上下文有些關聯:很顯然,如果兩臺計算機要進行通訊,就必須

Linux網路程式設計 網路協議入門

我們每天使用網際網路,你是否想過,它是如何實現的? 全世界幾十億臺電腦,連線在一起,兩兩通訊。北京的某一塊網絡卡送出訊號,深圳的另一塊網絡卡居然就收到了,兩者實際上根本不知道對方的物理位置,你不覺得這是很神奇的事情嗎? 為了使各種不同的計算機之間可以互聯,ARPANet指定了一套計算