關於socket選項SO_REUSEADDR的探究

       本文是根據《UNIX網絡編程》一書中對於選項SO_REUSEADDR的描述而進行的一個“局部”驗證。書中給出了該選項適用於的四種場景:

1.該選項允許啓動一個監聽服務器並捆綁其衆所周知端口,即使其以前建立的將該端口用作他們的本地端口的連接仍存在。該場景通常是因爲監聽服務器派生了一個子線程對某客戶建立了連接,而當服務器終止,子線程仍然爲現有未關閉連接的客戶提供服務,再當服務器重啓,出現bind失敗的錯誤,而如果該服務器在socket()和bind()之間設置了SO_REUSEADDR選項,則服務器重啓將成功bind該端口。項目中已驗證。

2.該選項允許在同一端口上啓動同一服務器的多個實例,只要每個實例捆綁一個不同的本地IP地址即可。

3.該選項允許單個進程捆綁同一個端口到多個套接字上,只要每次捆綁指定不同的本地地址即可。

4.該選項允許完全重複的捆綁;當一個IP地址和端口已綁定到某個套接字上時,如果傳輸協議支持,同樣的IP地址和端口還可以捆綁到另一個套接字上。一般來說本特性只支持UDP套接字。

       因爲目前遇到的問題只是UDP的,因此本文將只針對第四點進行驗證。對應可執行程序test_for_server的UDP服務器代碼:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>

#define SERVER_IP "127.0.0.1"
#define SERVER_PORT 1024

typedef int thy_bool_t;
#define thy_true_t 1
#define thy_false_t 0


int main(int argc, char** argv){
	struct sockaddr_in skaddr;
	struct sockaddr_in addr_inp;
	int inplg = 0;
	int reslg = 0;
	int reslg_o = 0;
	char buff[256];
	
	int sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
	if(sockfd < 0){
		printf("ERROR:create socket Failed\n");
		return -1;
	}

	bzero(&skaddr, sizeof(struct sockaddr_in));
	skaddr.sin_family = AF_INET;
	skaddr.sin_addr.s_addr = inet_addr(SERVER_IP);
	//skaddr.sin_addr.s_addr = htonl(INADDR_ANY);
	skaddr.sin_port = htons(SERVER_PORT);
	
	thy_bool_t bReuseaddr = thy_true_t;
	if(setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (const char*)&bReuseaddr, sizeof(thy_bool_t))){
		printf("Setsockopt -reuseaddr- Failed\n");
		return -2;
	}
	if(bind(sockfd, (struct sockaddr*)&skaddr, sizeof(struct sockaddr)) < 0){
		printf("Bind ERROR\n");
		return -3;
	}
	
	while(1){
		bzero(buff, sizeof(buff));
		bzero(&addr_inp, sizeof(struct sockaddr_in));
		if(reslg = recvfrom(sockfd, buff, sizeof(buff), 0, (struct sockaddr*)&addr_inp, &inplg)){
			printf("Recvfrom.Cont:\n%s\nlg:%d\n", buff, reslg);
		}
		if(reslg_o = sendto(sockfd, "I get it", 9, 0, (struct sockaddr*)&addr_inp, inplg)){
			printf("Have reply %d bytes\n", reslg_o);
		}
	}
	
	getchar();

	if(close(sockfd)){
		printf("Close ERROR\n");
		return -5;
	}
	printf("\n\nBYE~\n\n");
	return 0;
}

       運行test_for_server,之後運行該可執行程序的副本test_for_server_re,兩者都bind成功。下一步,將寫客戶端向服務器發送數據報。以下是對應可執行程序test_for_conn的UDP客戶端代碼:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>

#define LOCAL_IP "127.0.0.1"
#define LOCAL_PORT 64401
#define SERVER_IP "127.0.0.1"
#define SERVER_PORT 1024

int main(int argc, char** argv){
	char buff[256];
	int reslg = 0;
	struct sockaddr_in skaddr;
	struct sockaddr_in localaddr;
	
	int sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
	if(sockfd < 0){
		printf("ERROR:create socket Failed\n");
	}
	
	bzero(&localaddr, sizeof(struct sockaddr_in));
	localaddr.sin_family = AF_INET;
	localaddr.sin_addr.s_addr = inet_addr(LOCAL_IP);
	localaddr.sin_port = htons(LOCAL_PORT);
	
	bzero(&skaddr, sizeof(struct sockaddr_in));
	skaddr.sin_family = AF_INET;
	skaddr.sin_addr.s_addr = inet_addr(SERVER_IP);
	skaddr.sin_port = htons(SERVER_PORT);
	
	if(bind(sockfd, (struct sockaddr*)&localaddr, sizeof(struct sockaddr)) < 0){
		printf("The UDP Client bind local Failed\n");
	}
	while(1){
		if(connect(sockfd, (struct sockaddr*)&skaddr, sizeof(struct sockaddr)) < 0){
			printf("Conn ERROR\n");
			sleep(1);
			continue;
		}
		printf("Connect Success\n");
		break;
	}
	while(1){
		bzero(buff, sizeof(buff));
		scanf("%s", buff);
		if( (reslg = sendto(sockfd, buff, sizeof(buff), 0, (struct sockaddr*)&skaddr, sizeof(struct sockaddr))) != -1 ){
			printf("Have sended %d bytes\n", reslg);
		}
		if(strcmp(buff, "bye") == 0){
			break;
		}
	}
	printf("\n\nBYE~\n\n");
	return 0;
}

       由客戶端向服務器發送消息

       第二個啓動(最新)的服務器收到消息,第一條服務器沒有能獲得正確的遠端地址,猜測與UDP的無連接屬性有關。

       此外,是否前後兩次的服務器都需要設置SO_REUSEADDR選項呢?修改可執行程序test_for_server的代碼,註釋掉setsockopt,再次先後執行test_for_server和test_for_server_re。

       同樣地,註釋test_for_server_re的setsockopt,保留另一個程序的setsockopt,得到的是同樣的結果。

本文總結:

       1.SO_REUSEADDR選項允許同一個主機上同時運行同一個應用程序的多個副本(或描述爲需要綁定同樣地址和端口的程序);

       2.這多個程序均需要設置SO_REUSEADDR選項,否則後續程序將不能bind成功;

       3.多個socket同時監聽相同地址,客戶端將向最新發起的socket發送數據。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章