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的介绍。

相关文章
相关标签/搜索