1. 程式人生 > >UDP網路程式設計

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
複製程式碼

實驗結果:

伺服器端:

客戶端:

http://www.cnblogs.com/sooner/p/3192685.html