1. 程式人生 > >【網路】UDP伺服器的實現

【網路】UDP伺服器的實現

UDP伺服器實現的基本步驟

(1)利用socket函式建立套接字

(2)伺服器用bind進行IP和埠號的繫結

(3)不需要設定監聽狀態

(4)繫結後直接讀寫

相關函式

recvfrom

作用

用來資料的接收

標頭檔案

#include<sys/types.h>

#include<sys/socket.h>

函式原型

int recvfrom(int sockfd, void* buf, ssize_t len, int flags, struct sockaddr* src_addr,socklen_t *addrlen);

引數

sockfd表示當前的套接字

buf表示要接收的資料指標

len表示接收的長度

flags引數設定為0

src_addr表示傳送方的協議地址

addrlen表示src_addr的大小

返回值

成功返回0

錯誤返回-1

sendto

作用

用來進行資料的傳送

標頭檔案

#include<sys/types.h>

#include<sys/socket.h>

函式原型

int sendto(int sockfd, void* buf , ssize_t len, int flags, struct sockaddr * src_addr, socklen_t addrlen);

引數

sockfd表示當前的套接字

buf表示要傳送的資料指標

len表示要傳送的資料長度

flags設定為0

src_addr為傳送方的協議地址

addrlen為src_addr的大小

返回值

成功返回0

錯誤返回-1

程式碼實現

server.c

#include<stdio.h>
#include<stdlib.h>
#include<error.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<string.h>

static void Usage()
{
	printf("Usage: [ipaddr] [port]\n");
	exit(1);
}

int main(int argc, char* argv[])
{
	if(argc != 3)
	{
		Usage();
	}

	//建立socket
	int sockfd = socket(AF_INET,SOCK_DGRAM,0);

	if(sockfd < 0)
	{
		perror("socket");
		exit(4);
	}

	struct sockaddr_in addr;
	addr.sin_family = AF_INET;
	addr.sin_port = htons(atoi(argv[2]));
	addr.sin_addr.s_addr = inet_addr(argv[1]);

	//進行繫結
	if(bind(sockfd,(struct sockaddr*)&addr,sizeof(addr)) < 0)
	{
		perror("bind");
		exit(2);
	}

	char buf[1024];

	//收發資料
	while(1)
	{
		struct sockaddr_in client;
		socklen_t clientLen = sizeof(client);
		ssize_t s = recvfrom(sockfd, buf ,sizeof(buf)-1,0,(struct sockaddr*)&client,&clientLen);

		if(s < 0)
		{
			perror("recvfrom");
			exit(3);
		}
		else if(s == 0)
		{
			continue;
		}
		else 
		{
			buf[s] = 0;
			printf("IP: %s , Port : %d  : %s\n",inet_ntoa(client.sin_addr),ntohs(client.sin_port),buf);
			sendto(sockfd,buf,strlen(buf),0,(struct sockaddr*)&client,clientLen);
		}

	}
	close(sockfd);
	return 0;
}

client.c

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<error.h>
#include<arpa/inet.h>
#include<netinet/in.h>
#include<string.h>

static Usage()
{
	printf("Usage: [ipaddr] [port]\n");
	exit(1);
}

int main(int argc,char* argv[])
{
	if(argc != 3)
	{
		Usage();
	}

	//建立socket
	int sockfd = socket(AF_INET,SOCK_DGRAM,0);
	if(sockfd < 0)
	{
		perror("sockfd");
		exit(2);
	}

	printf("請輸入...\n");
	char buf[1024];
	//進行寫和讀
	while(1)
	{
		printf("#client : ");
		fflush(stdout);
		ssize_t s = read(0,buf,sizeof(buf)-1);
		if(s < 0)
		{
			perror("read");
			exit(3);
		}
		buf[s-1] = '\0';

		//進行傳送
		struct sockaddr_in client;
		client.sin_family = AF_INET;
		client.sin_port = htons(atoi(argv[2]));
		client.sin_addr.s_addr = inet_addr(argv[1]);
	
		s = sendto(sockfd,buf,strlen(buf),0,(struct sockaddr*)&client,sizeof(client));

		if(s < 0)
		{
			perror("sendto");
			exit(4);
		}
		struct sockaddr_in server;
		socklen_t len = sizeof(server);

		s = recvfrom(sockfd,buf,sizeof(buf)-1,0,(struct sockaddr*)&server,&len);

		if(s < 0)
		{
			perror("recvfrom");
			exit(5);
		}
		
		//正常收到
		buf[s] = 0;
		printf("IP : %s , Port : %d  : %s\n",inet_ntoa(server.sin_addr),ntohs(server.sin_port),buf);

	}
	
	close(sockfd);
	return 0;
}

執行結果

客戶端傳送訊息


伺服器接受訊息


如何保證UDP的可靠性

傳輸層無法保證資料的可靠傳輸,只能通過應用層來實現了。

實現的方式可以參照tcp可靠性傳輸的方式,只是實現不在傳輸層,實現轉移到了應用層。

實現確認機制、重傳機制、視窗確認機制。

除了利用Linux協議棧以及上層socket機制之外,自己可以通過抓包和發包的方式去實現可靠性傳輸

需要實現如下功能:

傳送:包的分片、包確認、包的重發

接收:包的調序、包的序號確認

目前有如下開源程式利用udp實現了可靠的資料傳輸

分別為RUDP、RTP、UDT。

方法1:RUDP

RUDP 提供一組資料服務質量增強機制,如擁塞控制的改進、重發機制及淡化伺服器演算法等,從而在包丟失和網路擁塞的情況下, RTP 客戶機(實時位置)面前呈現的就是一個高質量的 RTP 流。

在不干擾協議的實時特性的同時,可靠 UDP 的擁塞控制機制允許 TCP 方式下的流控制行為。

方法2:RTP

實時傳輸協議(RTP)為資料提供了具有實時特徵的端對端傳送服務,如在組播或單播網路服務下的互動式視訊音訊或模擬資料。

應用程式通常在 UDP 上執行 RTP 以便使用其多路結點和校驗服務;

這兩種協議都提供了傳輸層協議的功能。但是 RTP 可以與其它適合的底層網路或傳輸協議一起使用

如果底層網路提供組播方式,那麼 RTP 可以使用該組播表傳輸資料到多個目的地。

RTP 本身並沒有提供按時傳送機制或其它服務質量(QoS)保證,它依賴於底層服務去實現這一過程。 

RTP 並不保證傳送或防止無序傳送,也不確定底層網路的可靠性。

 RTP 實行有序傳送, RTP 中的序列號允許接收方重組傳送方的包序列,同時序列號也能用於決定適當的包位置,例如:在視訊解碼中,就不需要順序解碼。

方法3:UDT

基於UDP的資料傳輸協議(UDP-basedData Transfer Protocol,簡稱UDT)是一種網際網路資料傳輸協議。

UDT的主要目的是支援高速廣域網上的海量資料傳輸,而網際網路上的標準資料傳輸協議TCP在高頻寬長距離網路上效能很差。

顧名思義,UDT建於UDP之上,並引入新的擁塞控制和資料可靠性控制機制。

UDT是面向連線的雙向的應用層協議。它同時支援可靠的資料流傳輸和部分可靠的資料報傳輸。

由於UDT完全在UDP上實現,它也可以應用在除了高速資料傳輸之外的其它應用領域,例如點到點技術(P2P),防火牆穿透,多媒體資料傳輸等等。