UNP学习笔记(第八章 基本UDP套接字编程)

来源:互联网 发布:php查询数据库并输出 编辑:程序博客网 时间:2024/04/19 19:16
UDP应用程序客户不与服务器建立连接,而是只管使用sendto函数给服务器发送数据报,其中必须指定目的地的地址作为参数。

下图给出典型的UDP客户/服务器程序的函数调用。

 

 

 

 

recvfrom和sendto函数

这两个函数类似于标准的read和write函数,不过需要3个额外的参数

#include <sys/socket.h>ssize_t recvfrom(int sockfd, void *buff, size_t nbytes, int flags,struct sockaddr *src_addr, socklen_t *addrlen);ssize_t sendto(int sockfd, const void *buff, size_t nbytes, int flags,const struct sockaddr *to, socklen_t addrlen);                                                                         //均返回:若成功则为读或写的字节数,若出错则为-1

 

 

 

UDP回射服务器程序

main函数

 1 #include    "unp.h" 2  3 int 4 main(int argc, char **argv) 5 { 6     int                    sockfd; 7     struct sockaddr_in    servaddr, cliaddr; 8  9     sockfd = Socket(AF_INET, SOCK_DGRAM, 0);10 11     bzero(&servaddr, sizeof(servaddr));12     servaddr.sin_family      = AF_INET;13     servaddr.sin_addr.s_addr = htonl(INADDR_ANY);14     servaddr.sin_port        = htons(SERV_PORT);15 16     Bind(sockfd, (SA *) &servaddr, sizeof(servaddr));17 18     dg_echo(sockfd, (SA *) &cliaddr, sizeof(cliaddr));19 }
View Code

dg_echo函数

 1 #include    "unp.h" 2  3 void 4 dg_echo(int sockfd, SA *pcliaddr, socklen_t clilen) 5 { 6     int            n; 7     socklen_t    len; 8     char        mesg[MAXLINE]; 9 10     for ( ; ; ) {11         len = clilen;12         n = Recvfrom(sockfd, mesg, MAXLINE, 0, pcliaddr, &len);13 14         Sendto(sockfd, mesg, n, 0, pcliaddr, len);15     }16 }
View Code

 

 

 

UDP回射客户程序

main函数

 1 #include    "unp.h" 2  3 int 4 main(int argc, char **argv) 5 { 6     int                    sockfd; 7     struct sockaddr_in    servaddr; 8  9     if (argc != 2)10         err_quit("usage: udpcli <IPaddress>");11 12     bzero(&servaddr, sizeof(servaddr));13     servaddr.sin_family = AF_INET;14     servaddr.sin_port = htons(SERV_PORT);15     Inet_pton(AF_INET, argv[1], &servaddr.sin_addr);16 17     sockfd = Socket(AF_INET, SOCK_DGRAM, 0);18 19     dg_cli(stdin, sockfd, (SA *) &servaddr, sizeof(servaddr));20 21     exit(0);22 }
View Code

dg_cli函数

 1 #include    "unp.h" 2  3 void 4 dg_cli(FILE *fp, int sockfd, const SA *pservaddr, socklen_t servlen) 5 { 6     int    n; 7     char    sendline[MAXLINE], recvline[MAXLINE + 1]; 8  9     while (Fgets(sendline, MAXLINE, fp) != NULL) {10 11         Sendto(sockfd, sendline, strlen(sendline), 0, pservaddr, servlen);12 13         n = Recvfrom(sockfd, recvline, MAXLINE, 0, NULL, NULL);14 15         recvline[n] = 0;    /* null terminate */16         Fputs(recvline, stdout);17     }18 }
View Code

 

 

 

验证接收到的相应

知道客户临时端口的任何进程都可往客户发送数据报,而且这些数据报会与正常的服务器应答混杂。

我们应该修改recvfrom调用以返回数据报的发送者的IP地址和端口号,保留来自数据报所发往服务器的应答,而忽略任何其他数据报。

下面是验证返回套接字地址的dg_cli函数版本

 1 #include    "unp.h" 2  3 void 4 dg_cli(FILE *fp, int sockfd, const SA *pservaddr, socklen_t servlen) 5 { 6     int                n; 7     char            sendline[MAXLINE], recvline[MAXLINE + 1]; 8     socklen_t        len; 9     struct sockaddr    *preply_addr;10 11     preply_addr = Malloc(servlen);12 13     while (Fgets(sendline, MAXLINE, fp) != NULL) {14 15         Sendto(sockfd, sendline, strlen(sendline), 0, pservaddr, servlen);16 17         len = servlen;18         n = Recvfrom(sockfd, recvline, MAXLINE, 0, preply_addr, &len);19         if (len != servlen || memcmp(pservaddr, preply_addr, len) != 0) {20             printf("reply from %s (ignored)\n",21                     Sock_ntop(preply_addr, len));22             continue;23         }24 25         recvline[n] = 0;    /* null terminate */26         Fputs(recvline, stdout);27     }28 }
View Code

 

 

 

UDP的connect函数

UDP套接字可以调用connect,但是跟TCP套接字不一样,它不会有三次握手过程。

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

1.我们再也不能给输出操作指定目的IP地址和端口号。也就是说,我们不使用sendto而改用write或send。

2.我们并不必使用recvfrom以获悉数据报的发送者,而改用read、recv或recvmsg。

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

一个已连接的UDP套接字可以再次调用connect以用于:

1.指定新的IP地址和端口号

2.断开套接字

 

 

 

dg_cli函数(修订版)

把上面dg_cli函数重写成调用connect的新函数

 1 #include    "unp.h" 2  3 void 4 dg_cli(FILE *fp, int sockfd, const SA *pservaddr, socklen_t servlen) 5 { 6     int        n; 7     char    sendline[MAXLINE], recvline[MAXLINE + 1]; 8  9     Connect(sockfd, (SA *) pservaddr, servlen);10 11     while (Fgets(sendline, MAXLINE, fp) != NULL) {12 13         Write(sockfd, sendline, strlen(sendline));14 15         n = Read(sockfd, recvline, MAXLINE);16 17         recvline[n] = 0;    /* null terminate */18         Fputs(recvline, stdout);19     }20 }
View Code

 

 

 

使用select的TCP和UDP回射服务器程序

 1 /* include udpservselect01 */ 2 #include    "unp.h" 3  4 int 5 main(int argc, char **argv) 6 { 7     int                    listenfd, connfd, udpfd, nready, maxfdp1; 8     char                mesg[MAXLINE]; 9     pid_t                childpid;10     fd_set                rset;11     ssize_t                n;12     socklen_t            len;13     const int            on = 1;14     struct sockaddr_in    cliaddr, servaddr;15     void                sig_chld(int);16 17         /* 4create listening TCP socket */18     listenfd = Socket(AF_INET, SOCK_STREAM, 0);19 20     bzero(&servaddr, sizeof(servaddr));21     servaddr.sin_family      = AF_INET;22     servaddr.sin_addr.s_addr = htonl(INADDR_ANY);23     servaddr.sin_port        = htons(SERV_PORT);24 25     Setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));26     Bind(listenfd, (SA *) &servaddr, sizeof(servaddr));27 28     Listen(listenfd, LISTENQ);29 30         /* 4create UDP socket */31     udpfd = Socket(AF_INET, SOCK_DGRAM, 0);32 33     bzero(&servaddr, sizeof(servaddr));34     servaddr.sin_family      = AF_INET;35     servaddr.sin_addr.s_addr = htonl(INADDR_ANY);36     servaddr.sin_port        = htons(SERV_PORT);37 38     Bind(udpfd, (SA *) &servaddr, sizeof(servaddr));39 /* end udpservselect01 */40 41 /* include udpservselect02 */42     Signal(SIGCHLD, sig_chld);    /* must call waitpid() */43 44     FD_ZERO(&rset);45     maxfdp1 = max(listenfd, udpfd) + 1;46     for ( ; ; ) {47         FD_SET(listenfd, &rset);48         FD_SET(udpfd, &rset);49         if ( (nready = select(maxfdp1, &rset, NULL, NULL, NULL)) < 0) {50             if (errno == EINTR)51                 continue;        /* back to for() */52             else53                 err_sys("select error");54         }55 56         if (FD_ISSET(listenfd, &rset)) {57             len = sizeof(cliaddr);58             connfd = Accept(listenfd, (SA *) &cliaddr, &len);59     60             if ( (childpid = Fork()) == 0) {    /* child process */61                 Close(listenfd);    /* close listening socket */62                 str_echo(connfd);    /* process the request */63                 exit(0);64             }65             Close(connfd);            /* parent closes connected socket */66         }67 68         if (FD_ISSET(udpfd, &rset)) {69             len = sizeof(cliaddr);70             n = Recvfrom(udpfd, mesg, MAXLINE, 0, (SA *) &cliaddr, &len);71 72             Sendto(udpfd, mesg, n, 0, (SA *) &cliaddr, len);73         }74     }75 }76 /* end udpservselect02 */
View Code

sig_chld信号处理函数

 1 Signal(SIGCHLD,sig_chld); 2  3  4 #include    "unp.h" 5  6 void 7 sig_chld(int signo) 8 { 9     pid_t    pid;10     int        stat;11 12     pid = wait(&stat);13     printf("child %d terminated\n", pid);14     return;15 }
View Code

 

0 0
原创粉丝点击