1. 程式人生 > >Linux學習之網路程式設計(UDP程式設計)

Linux學習之網路程式設計(UDP程式設計)

言之者無罪,聞之者足以戒。 - “詩序”

前幾篇文章說的都是TCP通訊的問題,這篇文章說一下UDP通訊的相關內容:

下面來看一下基於UDP的獲取時間的客戶伺服器的程式框圖:

(1)、建立一個基於IPv4(AF_INET)的資料報套接字(SOCK_DGRAM)

(2)、ssize_t  recvfrom(int sockfd, void *buf, size_t len, int flags,
                        struct sockaddr *src_addr, socklen_t *addrlen);

(3)、ssize_t  sendto(int sockfd, const void *buf, size_t len, int flags,
                      const struct sockaddr *dest_addr, socklen_t addrlen);

1、從指定地址接收UDP資料報

ssize_t  recvfrom(int sockfd, void *buf, size_t len, int flags,
                        struct sockaddr *src_addr, socklen_t *addrlen);

第一個引數:socket的描述符

第二個引數:UDP資料報快取地址

第三個引數:UDP資料報長度

第四個引數:該引數一般為0

第五個引數:傳送端的地址

第六個引數:傳送端的地址長度

返回值:成功則返回接收到的字元數,失敗則返回-1,錯誤原因存於errno中。

2、把UDP資料報發給指定地址

ssize_t  sendto(int sockfd, const void *buf, size_t len, int flags,
                      const struct sockaddr *dest_addr, socklen_t addrlen);

第一個引數:socket的描述符

第二個引數:UDP資料報快取地址

第三個引數:UDP資料報長度

第四個引數:該引數一般為0

第五個引數:接收端的地址

第六個引數:接收端的地址長度

返回值:成功則返回實際傳送出去的字元數,失敗返回-1,錯誤原因存於errno 中。

下面看一下伺服器的程式:

#include<stdio.h>
#include<string.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<stdlib.h>
#include<netinet/in.h>
#include<arpa/inet.h>

#define PORT 6666
#define MAXLEN 100

int main()
{
    int sockfd;
    struct sockaddr_in server;
    struct sockaddr_in client;
    socklen_t addrlen;
    int num;
    char buf[MAXLEN];
    //建立套接字
    if((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
    {
      perror("Creatingsocket failed.");
      exit(1);
    }
    //初始化結構體
    bzero(&server,sizeof(server));
    server.sin_family=AF_INET;//IPv4
    server.sin_port=htons(PORT);//埠號
    server.sin_addr.s_addr= htonl (INADDR_ANY);//設定為可以連線任何一個客戶端
    //將套接字和伺服器的地址繫結
    if(bind(sockfd, (struct sockaddr *)&server, sizeof(server)) == -1)
    {
      perror("Bind()error.");
      exit(1);
    }
    //得到儲存客戶端的結構體的長度
    addrlen=sizeof(client);
    while(1)
    {
        //從客戶端讀取資料,並存儲在buf中
        num =recvfrom(sockfd,buf,MAXLEN,0,(struct sockaddr*)&client,&addrlen);
        if (num < 0)
        {
           perror("recvfrom() error\n");
           exit(1);
        }
        //最後一個字元為空(防止打出亂碼)
        buf[num] = '\0';
        //列印讀取的內容以及ip地址和埠號
        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));
        //傳送資料到客戶端
        sendto(sockfd,"Welcometo client,I am xiaoyi.",32,0,(struct sockaddr *)&client,addrlen);
        //判斷接收到的資料是不是bye,是的話就直接退出
        if(!strcmp(buf,"bye"))
          break;
   }
   //關閉套接字
   close(sockfd);
}

接下來看一下客戶端的程式:

#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 6666
#define MAXLEN 100

int main(int argc, char *argv[])
{
       int sockfd, num;
       char buf[MAXLEN];

       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;//IPv4
       server.sin_port = htons(PORT);//埠號
//      inet_pton(AF_INET,"192.168.177.133",&server.sin_addr.s_addr);
       server.sin_addr= *((struct in_addr *)he->h_addr);//ip地址
        //向伺服器傳送資料
       sendto(sockfd, argv[2],strlen(argv[2]),0,(struct sockaddr *)&server,sizeof(server));
       socklen_t  addrlen;
        //獲取伺服器的結構體長度
       addrlen=sizeof(server);
       while (1)
       {
                //從伺服器讀取資料,並存在buf中
              if((num=recvfrom(sockfd,buf,MAXLEN,0,(struct sockaddr *)&peer,&addrlen))== -1)
              {
                   printf("recvfrom() error\n");
                   exit(1);
                }
                //
              if (addrlen != sizeof(server) ||memcmp((const void *)&server, (const void *)&peer,addrlen) != 0)
                {
                        printf("Receive message from otherserver.\n");
                        continue;
                }

                //將陣列的最後一位賦值為空
                buf[num]='\0';
                printf("Server Message:%s\n",buf);
                break;
       }

       close(sockfd);
}

在這裡我說一下程式執行的步驟:

(1)開啟一個終端首先編譯伺服器程式(server)。命令:gcc -o server server.c

(2)執行server程式。命令:./server

(3)開啟另一個終端(開啟終端的快捷鍵CTRL+ALT+T)。編譯客戶端程式(client)。命令:gcc -o client client.c

(4)執行client程式。命令:./client 192.168.177.133 hello

(5)在第二個終端中再次輸入指令:./client 192.168.177.133 bye

觀察兩次執行的結果: