Chinaunix首页 | 论坛 | 博客
  • 博客访问: 2764302
  • 博文数量: 505
  • 博客积分: 1552
  • 博客等级: 上尉
  • 技术积分: 2514
  • 用 户 组: 普通用户
  • 注册时间: 2007-09-23 18:24
文章分类

全部博文(505)

文章存档

2019年(12)

2018年(15)

2017年(1)

2016年(17)

2015年(14)

2014年(93)

2013年(233)

2012年(108)

2011年(1)

2009年(11)

分类: 网络与安全

2014-11-10 18:00:09

上一篇我们提到,除非套接字已连接,否则异步错误是不会返回到UDP套接字的。我们确实可以给UDP套接字调用connect,然而这样做的结果却与TCP连接大相径庭:没有三次握手。只是检查是否存在立即可知的错误(例如一个显然不可达的目的地),记录对端的IP地址和端口号(取自传递给connect的套接字地址结构),然后立即返回到调用进程。

    有了这个能力后,我们必须区分:

(1)未连接UDP套接字,新创建UDP套接字默认如此;

(2)已连接UDP套接字,对UDP套接字调用connect的结果。

    对于已连接UDP套接字,与默认的未连接UDP套接字相比,发生了三个变化:

(1) 我们再也不能给输出操作指定目的IP地址和端口号。也就是说,我们不使用sendto,而改用write或send。写到已连接UDP套接字上的任何内容 都自动发送到由connect指定的协议地址(例如IP地址和端口号)。(其实我们可以给已连接UDP套接字调用sendto,但是不能指定目的地址。 sendto的第五个参数必须为空,第六个参数应该为0)。

    后面有在 10.04系统下的验证。

(2) 我们不必使用recvfrom以获悉数据报的发送者,而改用read,recv或recvmsg。在一个已连接UDP套接字上,由内核为输入操作返回的数 据报只有那些来自connect所指定协议地址的数据报。(确切的说,个已连接的UDP套接字仅仅与一个IP地址交换数据报,因为connect到多播 或广播地址是可能的)。

(3)由已连接的UDP套接字引发的异步错误会返回给他们所在的进程,而未连接UDP套接字不接受任何异步错误。

    应用进程首先调用connect指定对端的IP地址和端口号,然后使用read和write与对端进程交换数据。来自任何其他IP地址或端口的数据报(上 中我们用“???”表示)不投递给这个已连接套接字,因为他们要么源IP地址要么源UDP端口不与该套接字connect到的协议地址相匹配。这些数据报 可能投递给同一个主机上的其他某个UDP套接字。如果没有相匹配的其他套接字,UDP将丢弃他们并生成相应的ICMP端口不可达错误。

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   
#include   
#include   
#include   
#include   
#include   
#include   
#include   
#include   
      
#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 ");  
      
    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);  
}
阅读(1677) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~