[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.\n");
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.\n");
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.\n");
43             exit(1);
44         }
45         buf[num] = '\0';
46         printf("You got a message <%s> from client. \nIt's ip is %s, port is %d. \n", buf, inet_ntoa(client.sin_addr),htons(client.sin_port));
47         sendto(sockfd, "Welcome\n", 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>\n", argv[0]);
24         exit(1);
25     }
26 
27     if((sockfd=socket(AF_INET, SOCK_DGRAM, 0)) == -1)
28     {
29         printf("socket() error\n");
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.\n");
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.\n");
51             exit(1);
52         }
53         
54         buf[num] = '\0';
55         printf("Server Message: %s.\n", 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>\n", argv[0]);
        exit(1);
    }
    if((he = gethostbyname(argv[1]))==NULL)
    {
        printf("gethostbyname() error\n");
        exit(1);
    }
    if((sockfd=socket(AF_INET, SOCK_DGRAM, 0)) == -1)
    {
        printf("socket() error\n");
        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\n");
            exit(1);
        }
        if(len != sizeof(server) || memcmp((const void *)&server, (const void *)&peer, len) != 0 )
        {
            printf("Receive message from other server.\n");
            continue;
        }
        buf[num] = '\0';
        printf("Server Message: %s.\n", 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" 
 #注釋:程序在這里卡死

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM