UDP程序設計


UDP套接口是無連接的、不可靠的數據報協議;既然他不可靠為什么還要用呢?
其一:當應用程序使用廣播或多播時只能使用UDP協議;
其二:由於他是無連接的,所以速度快。
因為UDP套接口是無連接的,如果一方的數據報丟失,那另一方將無限等待,解決辦法是設置一個超時。

建立UDP套接口時socket函數的第二個參數應該是SOCK_DGRAM,說明是建立一個UDP套接口;由於UDP是無連接的,所以服務器端並不需要listen或accept函數。使用UDP套接字編程可以實現基於TCP/IP協議的面向無連接的通信,它分為服務器端和客戶端兩部分,其主要實現過程如下圖所示:

 

UDP客戶/服務器的套接字函數

1、socket函數:為了執行網絡輸入輸出,一個進程必須做的第一件事就是調用socket函數獲得一個文件描述符。

第一個參數指明了協議簇,目前支持5種協議簇,最常用的有AF_INET(IPv4協議)和AF_INET6(IPv6協議);

/*
 * Address families.
 */
#define AF_UNSPEC       0               /* unspecified */
#define AF_UNIX         1               /* local to host (pipes, portals) */
#define AF_INET         2               /* internetwork: UDP, TCP, etc. */
#define AF_IMPLINK      3               /* arpanet imp addresses */
#define AF_PUP          4               /* pup protocols: e.g. BSP */
#define AF_CHAOS        5               /* mit CHAOS protocols */
#define AF_IPX          6               /* IPX and SPX */
#define AF_NS           6               /* XEROX NS protocols */
#define AF_ISO          7               /* ISO protocols */
#define AF_OSI          AF_ISO          /* OSI is ISO */
#define AF_ECMA         8               /* european computer manufacturers */
#define AF_DATAKIT      9               /* datakit protocols */
#define AF_CCITT        10              /* CCITT protocols, X.25 etc */
#define AF_SNA          11              /* IBM SNA */
#define AF_DECnet       12              /* DECnet */
#define AF_DLI          13              /* Direct data link interface */
#define AF_LAT          14              /* LAT */
#define AF_HYLINK       15              /* NSC Hyperchannel */
#define AF_APPLETALK    16              /* AppleTalk */
#define AF_NETBIOS      17              /* NetBios-style addresses */
#define AF_VOICEVIEW    18              /* VoiceView */
#define AF_FIREFOX      19              /* FireFox */
#define AF_UNKNOWN1     20              /* Somebody is using this! */
#define AF_BAN          21              /* Banyan */

#define AF_MAX          22

第二個參數指明套接口類型,有三種類型可選:SOCK_STREAM(字節流套接口)、SOCK_DGRAM(數據報套接口)和SOCK_RAW(原始套接口);

/*
 * Types
 */
#define SOCK_STREAM     1               /* stream socket */
#define SOCK_DGRAM      2               /* datagram socket */
#define SOCK_RAW        3               /* raw-protocol interface */
#define SOCK_RDM        4               /* reliably-delivered message */
#define SOCK_SEQPACKET  5               /* sequenced packet stream */

如果套接口類型不是原始套接口,那么第三個參數就為0。

2、bind函數:為套接口分配一個本地IP和協議端口,對於網際協議,協議地址是32位IPv4地址或128位IPv6地址與16位的TCP或UDP端口號的組合;如指定端口為0,調用bind時內核將選擇一個臨時端口,如果指定一個通配IP地址,則要等到建立連接后內核才選擇一個本地IP地址。

#include <sys/socket.h>  
 int bind(int sockfd, const struct sockaddr * server, socklen_t addrlen);
 //返回:0---成功   -1---失敗 

第一個參數是socket函數返回的套接口描述字;第二和第第三個參數分別是一個指向特定於協議的地址結構的指針和該地址結構的長度。

3、recvfrom函數:UDP使用recvfrom()函數接收數據,他類似於標准的read(),但是在recvfrom()函數中要指明目的地址。

#include <sys/types.h>  
#include <sys/socket.h>  
 ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr * from, size_t *addrlen);
 //返回接收到數據的長度---成功   -1---失敗 

前三個參數等同於函數read()的前三個參數,flags參數是傳輸控制標志。最后兩個參數類似於accept的最后兩個參數。

4、sendto函數:UDP使用sendto()函數發送數據,他類似於標准的write(),但是在sendto()函數中要指明目的地址。

 

#include <sys/types.h>  
#include <sys/socket.h>  
 ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr * to, int addrlen);
 //返回發送數據的長度---成功   -1---失敗

前三個參數等同於函數read()的前三個參數,flags參數是傳輸控制標志。參數to指明數據將發往的協議地址,他的大小由addrlen參數來指定。

參考程序(udpserver.c):

Linux下UDP服務器套接字程序,服務器接收客戶端發送的信息並顯示,同時顯示客戶的IP地址、端口號,並向客戶端發送信息。如果服務器接收的客戶信息為“bye”,則退出循環,並關閉套接字。

#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 1234
#define MAXDATASIZE 100
 
int main()
{
    int sockfd;
    struct sockaddr_in server;
    struct sockaddr_in client;
    socklen_t addrlen;
    int num;
    char buf[MAXDATASIZE];
 
    if((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) 
    {
       perror("Creatingsocket failed.");
       exit(1);
    }
 
    bzero(&server,sizeof(server));
    server.sin_family=AF_INET;
    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)  
    {
       num =recvfrom(sockfd,buf,MAXDATASIZE,0,(struct sockaddr*)&client,&addrlen);                                   
 
       if (num < 0)
       {
          perror("recvfrom() error\n");
          exit(1);
       }
 
       buf[num] = '\0';
       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 my server.\n",22,0,(struct sockaddr *)&client,addrlen);
       if(!strcmp(buf,"bye"))
       break;
    }
    close(sockfd);  
}
//執行命令./ udpserver,觀察結果

參考程序(udpclient.c):

1、客戶根據用戶提供的IP地址將用戶從終端輸入的信息發送給服務器,然后等待服務器的回應。
2、服務器接收客戶端發送的信息並顯示,同時顯示客戶的IP地址、端口號,並向客戶端發送信息。如果服務器接收的客戶信息為“bye”,則退出循環,並關閉套接字。
3、客戶接收、顯示服務器發回的信息,並關閉套接字。

#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  addrlen;
    addrlen=sizeof(server);
    while (1)
    {
       if((num=recvfrom(sockfd,buf,MAXDATASIZE,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);
}
//執行命令./ udpclient 127.0.0.1 hello

實驗結果:

服務器端:

客戶端:


免責聲明!

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



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