UDP编程
udp是无连接的,没有listen 与 accept。
bind
服务端一定需要bind,告知别人自己的ip和port。
客户端可以不bind,在发送数据时候可以随机分配,客户端推荐bind(与tcp不同,udp本身无连接)。
注意:
- 服务端当中另一个进程已经用tcp bind了 8080,该进程还可以使用udp bind 8080,tcp 与 udp没有关系。
udp只能使用sendto
sendto携带ip
如果客户端没有bind,那么只能由客户端sendto
udp接收消息只能使用recvfrom
可以获取发送方的ip和port
udp 中的close
由于无连接,另一端无影响(另一端不知道对方断开连接)。
如果一端已经close了,对面继续sendto,消息丢失不重传,可以在应用层实现可靠性。
如果一端已经close了,对面继续recvfrom,一直阻塞。
udp 和 tcp 的区别
udp 不可靠,无连接,消息有边界
tcp 可靠,有连接,是一种流式协议,消息无边界
使用udp实现即时聊天
尽管传输层没有建立连接,但是我们在应用层实现了。在做聊天之前,需要客户端先发一条消息给服务端,让服务端知道客户端的ip 和 port。
退出时可以利用recvfrom的返回值,在udp中,如果recvfrom的返回值为0,说明是空包,对面已经退出,此时本方可以退出。
代码实现
server_udp.c
#include <my_header.h> #define BUFSIZE 4096 /* Usage: */ int main(int argc, char *argv[]){ ARGS_CHECK(argc,3);int sockfd = socket(AF_INET,SOCK_DGRAM,0);struct sockaddr_in addr;addr.sin_family = AF_INET;addr.sin_addr.s_addr = inet_addr(argv[1]);addr.sin_port = htons(atoi(argv[2]));int ret = bind(sockfd,(struct sockaddr*)&addr,sizeof(addr));ERROR_CHECK(ret,-1,"bind");char buf[BUFSIZE];struct sockaddr_in clientAddr;socklen_t clientAddrlen = sizeof(clientAddr);recvfrom(sockfd,buf,sizeof(buf),0,(struct sockaddr*)&clientAddr,&clientAddrlen);printf("client connected! ip = %s, port = %d, [buf] %s\n",inet_ntoa(clientAddr.sin_addr),htons(clientAddr.sin_port),buf);fd_set fd;while(1){FD_ZERO(&fd);FD_SET(STDIN_FILENO,&fd);FD_SET(sockfd,&fd);select(sockfd+1,&fd,NULL,NULL,NULL);if(FD_ISSET(sockfd,&fd)){bzero(buf,sizeof(buf));ssize_t sret = recvfrom(sockfd,buf,sizeof(buf),0,(struct sockaddr*)&clientAddr,&clientAddrlen);if(sret == 0){printf("client disconnected\n");break;}printf("[buf] %s\n",buf);}if(FD_ISSET(STDIN_FILENO,&fd)){bzero(buf,sizeof(buf));ssize_t sret = read(STDIN_FILENO,buf,sizeof(buf));if(sret == 0){printf("server disconnected\n");sendto(sockfd,buf,0,0,(struct sockaddr*)&clientAddr,clientAddrlen);break;}sendto(sockfd,buf,strlen(buf),0,(struct sockaddr*)&clientAddr,clientAddrlen);}}close(sockfd);return 0; }
client_udp.c
#include <my_header.h> #define BUFSIZE 4096 /* Usage: */ int main(int argc, char *argv[]){ ARGS_CHECK(argc,3);int sockfd = socket(AF_INET,SOCK_DGRAM,0);struct sockaddr_in addr;addr.sin_family = AF_INET;addr.sin_addr.s_addr = inet_addr(argv[1]);addr.sin_port = htons(atoi(argv[2])); socklen_t serverlen = sizeof(addr);sendto(sockfd,"nihao",5,0,(struct sockaddr *)&addr,sizeof(addr));char buf[BUFSIZE];fd_set fd;while(1){FD_ZERO(&fd);FD_SET(STDIN_FILENO,&fd);FD_SET(sockfd,&fd);select(sockfd+1,&fd,NULL,NULL,NULL);if(FD_ISSET(sockfd,&fd)){bzero(buf,sizeof(buf));ssize_t sret = recvfrom(sockfd,buf,sizeof(buf),0,(struct sockaddr*)&addr,&serverlen);if(sret == 0){printf("server disconnected\n");break;}printf("[buf] %s\n",buf);}if(FD_ISSET(STDIN_FILENO,&fd)){bzero(buf,sizeof(buf));ssize_t sret = read(STDIN_FILENO,buf,sizeof(buf));if(sret == 0){printf("I will close connection\n");sendto(sockfd,buf,0,0,(struct sockaddr*)&addr,sizeof(addr));break;}sendto(sockfd,buf,strlen(buf),0,(struct sockaddr*)&addr,sizeof(addr));}}close(sockfd);return 0; }
服务端回复消息,服务端先recvfrom获取ip和ort,随后就可以sendto回去了。
对比tcp与udp来说,tcp操作更简单。但是udp的性能更好,在音视频等领域应用更广。