• [C语言]一个很实用的服务端和客户端进行UDP通信的实例


    前段时间发了个TCP通信的例子,现在再来一个UDP通信的例子。这些可以作为样本程序,用到开发中。“裸写”socket老是记不住步骤,经常被鄙视……

    下面的例子很简单,写一个UDP的server用于收包,写一个UDP的client用于发包并接收来自server的回复。其中UDP的client写了两个,一个是不需要connect的,另一个是带上connect的,两个client实现的功能是一样的。从效率上,带上connect的UDP肯定效率稍微高一些。不过UDP的connect和TCP里面非常不一样。在UDP里面connect的时候并没有三次握手的过程,但是它指定了与自己通信的对方的具体地址,内核中会将次地址记录下来,如果你的UDP就是在确定了两台机器之间传送信息,建议选取带有connect的套接字。connect之后与对方通信直接write或者read函数就可以,不用再指定对方ip和port,并且connect之后的套接字可以自动过滤掉不是来自指定通信方的信息。UDP可以调用多次connect函数,但是TCP套接字只能调用一次,再次调用会出现错误。

    1. 首先是服务端的程序:

    UDPserver.cpp

     1 #include <stdio.h>
     2 #include <string.h>
     3 #include <unistd.h>
     4 #include <sys/types.h>
     5 #include <sys/socket.h>
     6 #include <stdlib.h>
     7 #include <netinet/in.h>
     8 #include <arpa/inet.h>
     9 
    10 #define PORT 1234
    11 #define MAXDATASIZE 100
    12 
    13 int main(void)
    14 {
    15     int sockfd;
    16     struct sockaddr_in server;
    17     struct sockaddr_in client;
    18     socklen_t len;
    19     int num;
    20     char buf[MAXDATASIZE];
    21     if((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
    22     {
    23         perror("Creating socket failed.
    ");
    24         exit(1);
    25     }
    26     bzero(&server, sizeof(server));
    27     server.sin_family = AF_INET;
    28     server.sin_port = htons(PORT);
    29     server.sin_addr.s_addr = htonl(INADDR_ANY);
    30     if(bind(sockfd, (struct sockaddr *)&server, sizeof(server)) == -1)
    31     {
    32         perror("Bind() error.
    ");
    33         exit(1);
    34     }
    35     
    36     len = sizeof(client);
    37     while(1)
    38     {
    39         num = recvfrom(sockfd, buf, MAXDATASIZE, 0, (struct sockaddr *)&client, &len);
    40         if(num < 0)
    41         {
    42             perror("recvfrom() error.
    ");
    43             exit(1);
    44         }
    45         buf[num] = '';
    46         printf("You got a message <%s> from client. 
    It's ip is %s, port is %d. 
    ", buf, inet_ntoa(client.sin_addr),htons(client.sin_port));
    47         sendto(sockfd, "Welcome
    ", 8, 0, (struct sockaddr *)&client, len);
    48         if ( !strcmp(buf, "bye") ){
    49             break;
    50         }
    51     }
    52     close(sockfd);
    53 }

    2. 然后,我们给出带有connect的客户端程序:

    UDPclientWithConnect.cpp

     1 #include <stdio.h>
     2 #include <stdlib.h>
     3 #include <unistd.h>
     4 #include <string.h>
     5 #include <sys/types.h>
     6 #include <sys/socket.h>
     7 #include <netinet/in.h>
     8 #include <netdb.h>
     9 #include <arpa/inet.h>
    10 
    11 #define PORT 1234
    12 
    13 #define MAXDATASIZE 100
    14 
    15 int main(int argc, char *argv[])
    16 {
    17     int sockfd, num;
    18     char buf[MAXDATASIZE];
    19     struct hostent *he;
    20     struct sockaddr_in server, peer;
    21     if(argc != 3)
    22     {
    23         printf("Usage: %s <IP address> <message>
    ", argv[0]);
    24         exit(1);
    25     }
    26 
    27     if((sockfd=socket(AF_INET, SOCK_DGRAM, 0)) == -1)
    28     {
    29         printf("socket() error
    ");
    30         exit(1);
    31     }
    32     bzero(&server, sizeof(server));
    33     server.sin_family = AF_INET;
    34     server.sin_port = htons(PORT);
    35 
    36     server.sin_addr.s_addr = inet_addr(argv[1]);
    37     //server.sin_addr.s_addr = inet_addr(argv[1]);
    38     if(connect(sockfd, (struct sockaddr *)&server, sizeof(server)) == -1)
    39     {
    40         printf("connect() error.
    ");
    41         exit(1);
    42     }
    43     
    44     send(sockfd, argv[2], strlen(argv[2]), 0);
    45 
    46     while(1)
    47     {
    48         if((num = recv(sockfd, buf, MAXDATASIZE, 0)) == -1)
    49         {
    50             printf("recv() error.
    ");
    51             exit(1);
    52         }
    53         
    54         buf[num] = '';
    55         printf("Server Message: %s.
    ", buf);
    56         break;
    57     }
    58     close(sockfd);
    59 }

    3. 最后,再给一个不带connect的客户端程序。

    UDPclientNoConnect.cpp

    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <string.h>
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <netdb.h>
    
    #define PORT 1234
    
    #define MAXDATASIZE 100
    
    int main(int argc, char *argv[])
    {
        int sockfd, num;
        char buf[MAXDATASIZE];
        struct hostent *he;
        struct sockaddr_in server, peer;
        if(argc != 3)
        {
            printf("Usage: %s <IP address> <message>
    ", argv[0]);
            exit(1);
        }
        if((he = gethostbyname(argv[1]))==NULL)
        {
            printf("gethostbyname() error
    ");
            exit(1);
        }
        if((sockfd=socket(AF_INET, SOCK_DGRAM, 0)) == -1)
        {
            printf("socket() error
    ");
            exit(1);
        }
        bzero(&server, sizeof(server));
        server.sin_family = AF_INET;
        server.sin_port = htons(PORT);
        server.sin_addr = *( (struct in_addr *)he->h_addr);
        sendto(sockfd, argv[2], strlen(argv[2]), 0, (struct sockaddr *)&server, sizeof(server));
        socklen_t len;
        len = sizeof(server);
        while(1)
        {
            if((num = recvfrom(sockfd, buf, MAXDATASIZE, 0, (struct sockaddr *)&peer, &len)) == -1)
            {
                printf("recvfrom() error
    ");
                exit(1);
            }
            if(len != sizeof(server) || memcmp((const void *)&server, (const void *)&peer, len) != 0 )
            {
                printf("Receive message from other server.
    ");
                continue;
            }
            buf[num] = '';
            printf("Server Message: %s.
    ", buf);
            break;
        }
        close(sockfd);
    }

    执行一下命令进行编译:

    $ g++ -o UDPserver UDPserver.cpp

    $ g++ -o UDPclient2 UDPclientWithConnect.cpp

    $ g++ -o UDPclient1 UDPclientNoConnect.cpp

    完了以后就看到三个可执行文件了。

    打开一个命令行,执行./UDPserver启动服务端程序,再打开另外一个命令行,执行./UDPclient1 127.0.0.1 "nihaonihao"或者./UDPclient2 127.0.0.1 "testtest"即可查看到以下效果:

    [horstxu@vps ~/Cprog/udpCSmodel]$ ./UDPserver 
    You got a message <nihaonihao> from client. 
    It's ip is 127.0.0.1, port is 25595. 
    You got a message <testtest> from client. 
    It's ip is 127.0.0.1, port is 27396. 
    [horstxu@vps ~/Cprog/udpCSmodel]$ ./UDPclient1 127.0.0.1 "nihaonihao" 
    Server Message: Welcome
    .
    [horstxu@vps ~/Cprog/udpCSmodel]$ ./UDPclient2 127.0.0.1 "testtest"  
    Server Message: Welcome
    .
    [horstxu@vps ~/Cprog/udpCSmodel]$ 

    最后再来解释一个带有connect的UDP的好处。由于UDP是不可靠传输,如果我发了数据出去,对方其实服务器是关闭的,这时会有什么结果呢?对于刚才的UDPclient1,也就是不带connect的,客户端程序会卡在recvfrom这里,因为对方是关闭的,它永远也收不到来自对方的回包。但是对于UDPclient2,也就是带有connect,我们其实可以收到一个错误,并设置errno(errno:111,connection refused)。这样看上去就比卡死在那里友好多了。对于这个问题的具体分析可以参考这篇文章:

    UDP怎么会返回Connection refused错误

    [horstxu@vps ~/Cprog/udpCSmodel]$ ./UDPclient2 127.0.0.1 "testtest"
    recv() error.
    [horstxu@vps ~/Cprog/udpCSmodel]$ ./UDPclient1 127.0.0.1 "testtest" 
     #注释:程序在这里卡死
  • 相关阅读:
    curl发送post请求,统计响应时间
    云集微店、拼多多等顽疾凸显,社交电商如何突围?
    App音频内录 录音
    nginx支持android、ios、微信扫一扫
    hadoop 2.7.1安装和配置
    Centos7上HBase的安装和配置
    HBase各版本对Hadoop版本的支持情况
    40个Java多线程问题总结
    JAVA多线程之volatile 与 synchronized 的比较
    深入解析spring中用到的九种设计模式
  • 原文地址:https://www.cnblogs.com/xuning/p/4614458.html
Copyright © 2020-2023  润新知