1. 程式人生 > >在UDP套接字上呼叫connect與在TCP上呼叫的區別

在UDP套接字上呼叫connect與在TCP上呼叫的區別

附:

我們有兩個應用程式,一個使用TCP,一個使用UDP。TCP套接字的接受緩衝區
有4096位元組的資料,UDP套接字接受緩衝區中有兩個2048位元組的資料包。TCP
應用程式呼叫read,指定其第三個引數為4096,UDP應用程式呼叫recvfrom指定其
第三個引數為5096.這兩個應用程式有什麼差別嗎?
答:是的,read返回4096位元組的資料,它會讀完接受緩衝區中的資料。
是位元組流。而recvfrom則返回2048位元組(2個數據報中的第一個)。不管應用
請求多大,recvfrom絕不會返回多於1個數據報的資料。(一個數據報,大小不是一定
的,要看對方封裝多大的資料報,一個)。

                                    在TCP套接字上呼叫connect,它會引發三次握手 syn分節的傳送,當(syn+ack)分節迴應客戶後,connect呼叫返回,同時傳送ack分節給伺服器,

這裡的connect呼叫引發了和對端的通訊。

                                     但在UDP套接字上也可有呼叫connect,但是,呼叫後的過程和TCP上有寫區別。

1,沒有三路握手的過程。核心只是建成是否存在立即可知的錯誤(例如一個顯然不可達的目的地)(如果沒有在UDP這裡呼叫connect,那麼指定一個不可達的遠端地址,傳送發是不會受到任何錯誤訊息的,它只是等待,然後在呼叫connect後,如果指定了不可達的目的地,那麼在第一關sento呼叫之後,它會返回一個ICMP錯誤,表示不可達)(但在TCP中,它在connect呼叫後就返回了錯誤,,不必等待sendto的呼叫)。

2,只是簡單地記錄對端的IP地址和埠號(取自傳給connect的套接字地址結構),然後立即返回呼叫程序。

3,呼叫了connect後,以後只能受到來自指定方傳送的UDP報文了,其他地址的報文都被過濾掉了,不會套地到該埠。

4,可以給同一個UDP套接字多次呼叫connect,但不可有在TCP套接字上這樣操作(當connect失敗後,必須關閉這個套接字,重新建立,因為它引發了三次握手的傳送)。

5,即:在UDP上connect,它只是在本地完成了一些工作,並不涉及到遠端的任何細節。

6,在UDP上呼叫connect並不給對端主機發送任何訊息,它完全是一個本地操作,知識儲存對端的IP地址和埠號。我們還看到,在一個未繫結埠號的UDP套接字上呼叫connect同時也給該套接字指定了一個臨時埠,這可有通過下面的程式碼得到驗證:

#include "unp.h"
char * Sock_ntop1(const struct sockaddr*sa,int len)
{
	char portstr[8];
	static char str[128];
	struct sockaddr_in *sin=(struct sockaddr_in*)sa;
	if(inet_ntop(AF_INET,&sin->sin_addr,str,sizeof(str))==NULL)
		return NULL;
	if(ntohs(sin->sin_port)!=0)
	{
		snprintf(portstr,sizeof(portstr),":%d",
				ntohs(sin->sin_port));
		strcat(str,portstr);
	}
	return str;
}
int main(int argc,char**argv)
{
	int sockfd;
	socklen_t len;
	struct sockaddr_in cliaddr,servaddr;
	if(argc!=2)
		err_quit("usage:udpcli<IPAddress>");
	sockfd=Socket(AF_INET,SOCK_DGRAM,0);
	bzero(&servaddr,sizeof(servaddr));
	servaddr.sin_family=AF_INET;
	servaddr.sin_port=htons(SERV_PORT);
	Inet_pton(AF_INET,argv[1],&servaddr.sin_addr);

	Connect(sockfd,(SA*)&servaddr,sizeof(servaddr));

	len=sizeof(cliaddr);
	Getsockname(sockfd,(SA*)&cliaddr,&len);
	printf("local address %s\n",Sock_ntop1((SA*)&cliaddr,len));
	exit(0);
}
在connect後,我們通過Getsockname()函式可以得到本地的IP和埠號,存放與變數cliaddr中。

這裡有個小知識點:在寫Sock_ntop1這個函式的時候,在返回 str的時候,開始給定義的str是個區域性變數,然後gcc爆出警告:返回一個區域性變數地址。

然後上網查了一下:果然,在函式返回後,區域性變數都已回收,這樣很可能造成不可預知的錯誤,故在返回地址的時候,最後定義成全部變數或者靜態變數,這樣當函式返回後

也不至於被回收。