轉自:http://blog.chinaunix.net/xmlrpc.php?r=blog/article&uid=28502731&id=3651271
大多數UDP服務器程序是迭代運行的,服務器等待一個客戶請求,讀入這個請求,處理這個請求,送回其應答,再等待下一個客戶請求。
然而當客戶請求的處理需要消耗過長的時間時,我們期望UDP服務器程序具有某種形式的並發性。
當使用TCP時,服務器的並發處理得以簡化的根源在於每個客戶連接都是唯一的(不同的已連接套接字),標識每個客戶連接的是一個唯一的套接字對。
然而當使用UDP時,服務端通過同一個套接字和所有的客戶端進行通信,當采用並發模式時,每一個子進程共享同一個UDP套接字,因此無法簡單地綁定於一個客戶並為其服務。
有兩種情況下可以使用並發的UDP服務器:
1 讀入一個客戶請求並發送一個應答后,與這個客戶不再有任何聯系。在這種情形下,當一個UDP請求到達時,阻塞在epoll_wait調用上的父進程被喚醒,然后fork一個子進程去調用recv_from讀取一個請求(一個完整的數據報),處理完該請求后再調用send_to發送回去。
2 第二種UDP服務器與客戶交互多個數據報。問題在於每個客戶都是往服務器端的同一個的端口發送數據。並發服務器的每一個子進程如何正確區分每一個客戶的數據報(涉及到進程的調度問題,如何避免一個子進程讀取到不該它服務的客戶發送來的數據報)。解決的方法是為每個客戶創建一個的新的套接字,在其上bind一個臨時端口,fork一個子進程使用該套接字發送對該客戶的所有應答。這個辦法要求客戶查看服務器第一個應答的中的源端口號,並把本請求的后續數據報發送到該端口。

廢話少說,上代碼!
/server.c/ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/stat.h> #include <fcntl.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <errno.h> int main(int argc,char *argv[]) { int socket_fd, file_fd; struct sockaddr_in netaddr; struct sockaddr_in fromaddr; socklen_t len = sizeof(struct sockaddr); memset(&netaddr, 0, len); netaddr.sin_family = AF_INET; netaddr.sin_port = htons(5000); netaddr.sin_addr.s_addr = INADDR_ANY; if((socket_fd = socket(AF_INET,SOCK_DGRAM,0)) < 0) { perror("Fail to create socket"); return -1; } if (bind(socket_fd, (struct sockaddr*)&netaddr, sizeof(struct sockaddr)) == -1) { perror("bind"); exit(1); } while (1) { char buf[100] = {}; int ret = recvfrom(socket_fd, buf, 100, 0, (struct sockaddr *)&fromaddr, &len); if (ret == -1) { perror("recvfrom"); exit(1); } printf("buf=%s\n", buf); if (fork() == 0) break; printf("next==============\n"); } int sock2; if((sock2 = socket(AF_INET,SOCK_DGRAM,0)) < 0) { perror("child Fail to create socket"); return -1; } if (sendto(sock2, "world", 5, 0, (struct sockaddr*)&fromaddr, len) == -1) { perror("child sendto"); exit(1); } while (1) { char buf[100] = {}; int ret = recvfrom(sock2, buf, 100, 0, (struct sockaddr *)&fromaddr, &len); if (ret == -1) { perror("child recvfrom"); exit(1); } printf("child buf=%s\n", buf); } close(socket_fd); return 0; }
/*client.c*/ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/stat.h> #include <fcntl.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <errno.h> int main(int argc,char *argv[]) { int socket_fd; struct sockaddr_in fromaddr; struct sockaddr_in servaddr; socklen_t len = sizeof(struct sockaddr); if((socket_fd = socket(AF_INET,SOCK_DGRAM,0)) < 0) { perror("Fail to create socket"); return -1; } memset(&servaddr, 0, len); servaddr.sin_family = AF_INET; servaddr.sin_port = htons(5000); servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");; if (sendto(socket_fd, "hello", 5, 0, (struct sockaddr*)&servaddr, len) == -1) { perror("sendto"); exit(1); } char buf[100] = {}; if (recvfrom(socket_fd, buf, 100, 0, (struct sockaddr*)&fromaddr, &len) == -1) { perror("recvfrom"); exit(1); } printf("recvfrom ip %s : %d\n", inet_ntoa(fromaddr.sin_addr), ntohs(fromaddr.sin_port)); printf("buf=%s\n", buf); int i; for (i = 0; i < 10; i++) { char buf[100]; int ret = sprintf(buf, "shit %d", i); if (sendto(socket_fd, buf, ret, 0, (struct sockaddr*)&fromaddr, len) == -1) { perror("sendto"); exit(1); } printf("send ok\n"); sleep(1); } //sleep(5); close(socket_fd); return 0; }
