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


    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)
    ",
    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             else
    53                 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
    ", pid);
    14     return;
    15 }
    View Code
  • 相关阅读:
    【Linux基础总结】Linux基本环境
    mysql 源码安装
    windows内存映射文件
    TCHAR和CHAR类型的互转
    删除链表中重复的结点
    iptables防火墙
    两个链表的第一个公共结点
    无人值守安装linux系统
    dns服务 很多问题,后续再研究
    string 类型转换
  • 原文地址:https://www.cnblogs.com/runnyu/p/4661101.html
Copyright © 2020-2023  润新知