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
實驗結果:
服務器端:
客戶端: