UNIX網絡編程——UDP 的connect函數(改進版)

     上一篇咱們提到,除非套接字已鏈接,不然異步錯誤是不會返回到UDP套接字的。咱們確實能夠給UDP套接字調用connect,然而這樣作的結果卻與TCP鏈接截然不同:沒有三次握手。內核只是檢查是否存在當即可知的錯誤(例如一個顯然不可達的目的地),記錄對端的IP地址和端口號(取自傳遞給connect的套接字地址結構),而後當即返回到調用進程。編程

     

     有了這個能力後,咱們必須區分:ubuntu

(1)未鏈接UDP套接字,新建立UDP套接字默認如此;網絡

(2)已鏈接UDP套接字,對UDP套接字調用connect的結果。異步


     對於已鏈接UDP套接字,與默認的未鏈接UDP套接字相比,發生了三個變化:socket

(1)咱們不再能給輸出操做指定目的IP地址和端口號。也就是說,咱們不使用sendto,而改用write或send。寫到已鏈接UDP套接字上的任何內容都自動發送到由connect指定的協議地址(例如IP地址和端口號)。(其實咱們能夠給已鏈接UDP套接字調用sendto,可是不能指定目的地址。sendto的第五個參數必須爲空,第六個參數應該爲0)。函數

                                         

     後面有在ubuntu 10.04系統下的驗證。性能

(2)咱們沒必要使用recvfrom以獲悉數據報的發送者,而改用read,recv或recvmsg。在一個已鏈接UDP套接字上,由內核爲輸入操做返回的數據報只有那些來自connect所指定協議地址的數據報。確切的說,一個已鏈接的UDP套接字僅僅與一個IP地址交換數據報,由於connect到多播或廣播地址是可能的)。spa

(3)由已鏈接的UDP套接字引起的異步錯誤會返回給他們所在的進程,而未鏈接UDP套接字不接受任何異步錯誤.net

                           

     應用進程首先調用connect指定對端的IP地址和端口號,而後使用read和write與對端進程交換數據。來自任何其餘IP地址或端口的數據報(上中咱們用「???」表示)不投遞給這個已鏈接套接字,由於他們要麼源IP地址要麼源UDP端口不與該套接字connect到的協議地址相匹配。這些數據報可能投遞給同一個主機上的其餘某個UDP套接字。若是沒有相匹配的其餘套接字,UDP將丟棄他們並生成相應的ICMP端口不可達錯誤code

 

1.給一個UDP套接字屢次調用connect

   擁有一個已鏈接UDP套接字的進程可出於下列兩個目的之一再次調用connect:

  • 指定新的IP地址和端口號;
  • 斷開套接字。

      第一個目的(即給一個已鏈接UDP套接字指定新的對端)不一樣於TCP套接字中的connect的使用:對於TCP套接字,connect只能調用一次

      爲了斷開一個已UDP套接字鏈接,咱們再次調用connect時把套接字地址結構的地址族成員(sin_family)設置爲AF_UNSPEC使套接字斷開鏈接的是在已鏈接UDP套接字上調用connect的進程。

 

2.性能

       在一個未鏈接的UDP套接字上給兩個數據報調用sendto函數因而涉及內核執行下列6個步驟:

(1)鏈接套接字;

(2)輸出第一個數據報;

(3)斷開套接字鏈接;

(4)鏈接套接字;

(5)輸出第二個數據報;

(6)斷開套接字鏈接。

      調用connect後調用兩次write涉及內核執行3個步驟:

(1)鏈接套接字;

(2)輸出第一個數據報;

(3)輸出第二個數據報。

 

客戶端程序:

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

#define SERV_PORT 3333
#define MAXLINE 1024
#define ERR_EXIT(m) \
        do \
        { \
                perror(m); \
                exit(EXIT_FAILURE); \
        } while(0)

typedef struct sockaddr SA;
void dg_cli(FILE *fp, int sockfd, const SA *pservaddr, socklen_t servlen)
{
	int     n;  
    char    sendline[MAXLINE], recvline[MAXLINE + 1];
/////////////////////////////////////////////////////////////////////////
	struct sockaddr_in	servaddr;
	bzero(&servaddr, sizeof(servaddr));
	servaddr.sin_family = AF_INET;
	servaddr.sin_port = htons(SERV_PORT);
	inet_pton(AF_INET, "192.168.2.103", &servaddr.sin_addr);	
/////////////////////////////////////////////////////////////////////////////  
    connect(sockfd, (SA *) pservaddr, servlen);  
  
    while (fgets(sendline, MAXLINE, fp) != NULL) {  
  
        n = write(sockfd, sendline, strlen(sendline)); 
        //n = sendto(sockfd, sendline, strlen(sendline), 0, pservaddr, servlen);
        //n = sendto(sockfd, sendline, strlen(sendline), 0, &servaddr, sizeof(servaddr));		
        //n = sendto(sockfd, sendline, strlen(sendline), 0, NULL, 0);
        if (n == -1)  
        {  
            if (errno == EISCONN)  
                ERR_EXIT("sendto");  
            else
                perror("sendto huangcheng");			
        } 		
		
		
		//struct sockaddr_in preply_addr;
		//socklen_t addrlen;
        n = read(sockfd, recvline, MAXLINE);
        //n = recvfrom(sockfd, recvline, MAXLINE, 0, NULL, NULL);
        //n = recvfrom(sockfd, recvline, MAXLINE, 0, (SA*)&preply_addr, &addrlen);		
        if (n == -1)  
        {  
            if (errno == EINTR)  
                continue;  
            ERR_EXIT("recvfrom");  
        } 
		//printf("reply from %s \n",inet_ntoa(preply_addr.sin_addr));
        recvline[n] = 0;    /* null terminate */  
        fputs(recvline, stdout);
	}
}

int main(int argc, char **argv)
{
	int					sockfd;
	struct sockaddr_in	servaddr;

	if (argc != 2)
		ERR_EXIT("usage: udpcli <IPaddress>");

	bzero(&servaddr, sizeof(servaddr));
	servaddr.sin_family = AF_INET;
	servaddr.sin_port = htons(SERV_PORT);
	inet_pton(AF_INET, argv[1], &servaddr.sin_addr);

	sockfd = socket(AF_INET, SOCK_DGRAM, 0);

	dg_cli(stdin, sockfd, (SA *) &servaddr, sizeof(servaddr));

	exit(0);
}

運行結果:

huangcheng@ubuntu:~$ ./cli 127.0.0.1
huangcheng
recvfrom: Connection refused


虛擬機:

huangcheng@ubuntu:~$ ifconfig
eth0      Link encap:以太網  硬件地址 00:0c:29:88:e0:1f
          inet 地址:192.168.2.103  廣播:192.168.2.255  掩碼:255.255.255.0
          inet6 地址: fe80::20c:29ff:fe88:e01f/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  躍點數:1
          接收數據包:43472 錯誤:0 丟棄:0 過載:0 幀數:0
          發送數據包:19785 錯誤:0 丟棄:0 過載:0 載波:0
          碰撞:0 發送隊列長度:1000
          接收字節:52561935 (52.5 MB)  發送字節:1925585 (1.9 MB)
          中斷:19 基本地址:0x2000

lo        Link encap:本地環回
          inet 地址:127.0.0.1  掩碼:255.0.0.0
          inet6 地址: ::1/128 Scope:Host
          UP LOOPBACK RUNNING  MTU:16436  躍點數:1
          接收數據包:396 錯誤:0 丟棄:0 過載:0 幀數:0
          發送數據包:396 錯誤:0 丟棄:0 過載:0 載波:0
          碰撞:0 發送隊列長度:0
          接收字節:38912 (38.9 KB)  發送字節:38912 (38.9 KB)

huangcheng@ubuntu:~$


驗證UDP套接字,已鏈接:

write或send:能夠

不指定目的地址的sendto:能夠

指定目的地址的send:

(1)connect指定的IP地址是:127.0.0.1,sendto指定的IP地址爲:127.0.0.1或者192.168.2.103  均正常。

(2)connect指定的IP地址是:127.0.0.1,sendto指定的IP地址爲:192.168.4.103 即不爲虛擬機的IP地址時,運行結果sendto huangcheng:Invalid argument,即出錯。


注意:在<<UNIX網絡編程——基於UDP協議的網絡程序>>中也有UDP connect的介紹。

相關文章
相關標籤/搜索