Linux系统编程(37)—— socket编程之UDP服务器与客户端
典型的UDP客户端/服务器通讯过程:
编写UDP Client程序的步骤
1、初始化sockaddr_in结构的变量,并赋值。这里使用“8888”作为连接的服务程序的端口,从命令行参数读取IP地址,并且判断IP地址是否符合要求。
2、使用socket()来建立一个UDPsocket,第二个参数为SOCK_DGRAM。
3、使用connect()来建立与服务程序的连接。与TCP协议不同,UDP的connect()并没有与服务程序三次握手。上面说了UDP是非连接的,实际上也可以是连接的。使用连接的UDP,kernel可以直接返回错误信息给用户程序,从而避免由于没有接收到数据而导致调用recvfrom()一直等待下去,看上去好像客户程序没有反应一样。
4、向服务程序发送数据,因为使用连接的UDP,所以使用write()来替代sendto()。这里的数据直接从标准输入读取用户输入。
5、接收服务程序发回的数据,同样使用read()来替代recvfrom()。
6、处理接收到的数据,这里是直接输出到标准输出上。
以下是简单的UDP服务器和客户端程序。
/* server.c */ #include <stdio.h> #include <string.h> #include <netinet/in.h> #include "wrap.h" #define MAXLINE 80 #define SERV_PORT 8000 int main(void) { structsockaddr_in servaddr, cliaddr; socklen_tcliaddr_len; intsockfd; charbuf[MAXLINE]; charstr[INET_ADDRSTRLEN]; inti, n; sockfd= Socket(AF_INET, SOCK_DGRAM, 0); bzero(&servaddr,sizeof(servaddr)); servaddr.sin_family= AF_INET; servaddr.sin_addr.s_addr= htonl(INADDR_ANY); servaddr.sin_port= htons(SERV_PORT); Bind(sockfd,(struct sockaddr *)&servaddr, sizeof(servaddr)); printf("Acceptingconnections ...\n"); while(1) { cliaddr_len= sizeof(cliaddr); n= recvfrom(sockfd, buf, MAXLINE, 0, (struct sockaddr *)&cliaddr,&cliaddr_len); if(n == -1) perr_exit("recvfromerror"); printf("receivedfrom %s at PORT %d\n", inet_ntop(AF_INET,&cliaddr.sin_addr, str, sizeof(str)), ntohs(cliaddr.sin_port)); for(i = 0; i < n; i++) buf[i]= toupper(buf[i]); n= sendto(sockfd, buf, n, 0, (struct sockaddr *)&cliaddr, sizeof(cliaddr)); if(n == -1) perr_exit("sendtoerror"); } }
/* client.c */ #include <stdio.h> #include <string.h> #include <unistd.h> #include <netinet/in.h> #include "wrap.h" #define MAXLINE 80 #define SERV_PORT 8000 int main(int argc, char *argv[]) { structsockaddr_in servaddr; intsockfd, n; charbuf[MAXLINE]; charstr[INET_ADDRSTRLEN]; socklen_tservaddr_len; sockfd= Socket(AF_INET, SOCK_DGRAM, 0); bzero(&servaddr,sizeof(servaddr)); servaddr.sin_family= AF_INET; inet_pton(AF_INET,"127.0.0.1", &servaddr.sin_addr); servaddr.sin_port= htons(SERV_PORT); while(fgets(buf, MAXLINE, stdin) != NULL) { n= sendto(sockfd, buf, strlen(buf), 0, (struct sockaddr *)&servaddr,sizeof(servaddr)); if(n == -1) perr_exit("sendtoerror"); n= recvfrom(sockfd, buf, MAXLINE, 0, NULL, 0); if(n == -1) perr_exit("recvfromerror"); Write(STDOUT_FILENO,buf, n); } Close(sockfd); return0; }
由于UDP不需要维护连接,程序逻辑简单了很多,但是UDP协议是不可靠的,实际上有很多保证通讯可靠性的机制需要在应用层实现。
编译运行server,在两个终端里各开一个client与server交互,看看server是否具有并发服务的能力。用Ctrl+C关闭server,然后再运行server,看此时client还能否和server联系上。和前面TCP程序的运行结果相比较,体会无连接的含义。