UDP通信中的客户端不必与服务器端建立连接,而是直接使用发送函数给服务器发送数据,调用发送函数时要指明目的地址。
事实上,对于UDP来说,并没有严格的服务器和客户端之分
UDP 服务流程
1、UDP服务器通过socket()函数创建套接字,获得套接字描述符
2、UDP服务器调用bind()函数绑定IP地址和端口绑定
3、此时UDP就可以调用recvfrom()函数阻塞的等待数据到来
4、对于客户端,只需要调用socket()函数创建套接字描述符,然后用sendto发送数据即可,调用sendto时要指定服务器的地址和端口。
5、服务器接受到来自客户端的数据后,就会得到客户端的IP地址和端口号等信息,此时就可以用sendto给客户端发消息。
6、结束通信后调用close()关闭套接字描述符
显然,当客户端调用bind函数后,也可作为”服务器“使用。
int socket(int domain,int type,int protocol);
参数:1、指明所用协议族 常用的有AF_INET AF_INET6 AF_LOCAL AF_ROUTE
在TCP连接中一般使用AF_INET表示用ipv4和16位端口号
2、套接字类型。SOCK_STREAM:TCP SOCK_DGRAM:UDP SOCK_RAW:允许对底层访问
3、协议类别。可以为0让系统自动选择
返回值若小于0说明发生错误,否则返回一个值表示创建套接字,类似每个人的身份证号一样,
之后都用套接字描述符来形容这个值
int bind(int sockfd,const struct sockaddr*,socklen_t addrlen)
参数:1、套接字描述符
2、协议地址,用于指定协议族、端口和IP地址
3、第二个参数的长度
发生错误返回-1,绑定成功返回0; 该函数用于将某个套接字与协议地址关联起来
int sendto(int sockfd,const void *msg, int len,unsigned int flags,struct sockaddr *dst_addr,int addrlen)
参数:1、套接字描述符
2、待发送数据缓冲区
3、第二个参数的长度
4、0个或多个标志的组合体,可直接设置为0 或用“|”连在一起。
MSG_DONTWAIT:非阻塞操作
MSG_DONTROUTE:告知网际层协议,目的主机在网络,不需要查找路由表
MSG_NOSIGNAL:动作不愿被SIGPIPE信号中断
MSG_OOB:表示接受到需要优先处理的数据
5、数据发送目的地址
6、第五个参数的长度
用于非可靠数据发送,成功返回实际发送字节数,失败返回-1
int recvfrom(int sockfd,void *buf,size_t nbytes,int flags,struct sockaddr *from,socklen_t *addrlen)
参数:1、套接字描述符
2、接受缓冲区
3、第二个参数的大小
4、0个或多个标志的组合体,可直接设置为0 或用“|”连在一起。
MSG_DONTWAIT:非阻塞操作
MSG_DONTROUTE:告知网际层协议,目的主机在网络,不需要查找路由表
MSG_NOSIGNAL:动作不愿被SIGPIPE信号中断
MSG_OOB:表示接受到需要优先处理的数据
5、用于存储数据来源的目的地址
6、第五个参数的长度
非可靠数据传输中用于数据的接受,成功返回接受数据的字节数,失败返回-1
服务器:
1 #include<iostream> 2 #include<arpa/inet.h> 3 #include<stdio.h> 4 #include<string.h> 5 #include<sys/socket.h> 6 #include<sys/types.h> 7 #include<netinet/in.h> 8 #include<unistd.h> 9 using namespace std; 10 11 int main() 12 { 13 struct sockaddr_in addr,recvaddr; 14 socklen_t len=sizeof(recvaddr); 15 int sockfd,op; 16 char buf[512]; 17 18 sockfd=socket(AF_INET,SOCK_DGRAM,0); 19 if(sockfd<0) 20 { 21 cout<<"socket error"<<endl; 22 return -1; 23 } 24 25 addr.sin_family=AF_INET; 26 addr.sin_port=htons(2501); 27 addr.sin_addr.s_addr=htons(INADDR_ANY); 28 29 op=bind(sockfd,(struct sockaddr*)&addr,sizeof(addr)); 30 if(op<0) 31 { 32 cout<<"bind error"<<endl; 33 return -1; 34 } 35 36 recvfrom(sockfd,buf,sizeof(buf),0,(struct sockaddr*)&recvaddr,&len); 37 cout<<"recv data from "<<ntohs(recvaddr.sin_port)<<" :"<<buf<<endl; 38 39 char buff[512]="1234567890987654321"; 40 sendto(sockfd,buff,strlen(buff),0,(struct sockaddr*)&recvaddr,len); 41 return 0; 42 }
客户端:
1 #include<sys/socket.h> 2 #include<sys/types.h> 3 #include<iostream> 4 #include<stdio.h> 5 #include<stdlib.h> 6 #include<string.h> 7 #include<netinet/in.h> 8 #include<arpa/inet.h> 9 using namespace std; 10 11 int main() 12 { 13 int sockfd; 14 int op; 15 struct sockaddr_in addr,saddr; 16 socklen_t len=sizeof(saddr); 17 int i,j; 18 19 sockfd=socket(AF_INET,SOCK_DGRAM,0); 20 if(sockfd<0) 21 { 22 cout<<"socket error"<<endl; 23 return -1; 24 } 25 26 addr.sin_family=AF_INET; 27 addr.sin_port=htons(2501); 28 addr.sin_addr.s_addr=htons(INADDR_ANY); 29 30 char buff[512]="0987654321234567890"; 31 sendto(sockfd,buff,strlen(buff),0,(struct sockaddr*)&addr,sizeof(addr)); 32 33 char buf[512]=""; 34 recvfrom(sockfd,buf,sizeof(buf),0,(struct sockaddr*)&saddr,&len); 35 cout<<"recv data from "<<ntohs(saddr.sin_port)<<" :"<<buf<<endl; 36 37 return 0; 38 }
参考书籍——《Linux网络编程》