基本UDP套接字编程
下图为UDP客户/服务器程序的函数调用:
注意客户不与服务器建立连接,而是只管使用sendto函数给服务器发送数据报,其中必须指定目的地的地址作为参数。类似的,服务器不接受来自客户的连接,而是只管调用recvfrom函数,等待来自某个客户的数据到达。recvfrom将与所接收的数据报一道返回客户的协议地址,因此服务器可以把响应发送给正确的客户。
recvfrom和sendto函数
#include<sys/socket.h>
ssize_t recvfrom(int sockfd,void *buff,size_t nbytes,int flags,struct sockaddr *from,soclen_t *addrlen);
ssize_t sendto(int sockfd,const void *buff,size_t nbytes,int flags, const struct sockaddr *to,socklen_t addrlen);
若成功则返回读或写的字节数,若出错则为-1。
- 前三个参数sockfd、buff、nbytes表示描述符、指向读入或写出缓冲区的指针和读写字节数。
- flags参数以后再谈,此时置为0。
- sendto的to参数指向一个含有数据报接收者的协议地址的套接字地址结构,其大小由addrlen参数指定。
- recvfrom的from参数指向一个将由该函数在返回时填写数据报发送者的协议地址的套接字地址结构,而该套接字地址结构中填写的字节数则放在addrlen参数所指的整数中返回给调用者。
写一个长度为0的数据报是可行的。在UDP情况下,这会形成一个只baoh一个IP首部和一个8字节UDP首部而没有数据的IP数据报。这也就是说对于数据报协议,recvfrom返回值0是可接受的。
若recvfrom的from参数是一个空指针,那么相应的长度参数(addrlen)也必须是一个空指针,表示我们并不关心数据发送者的协议地址。
UDP回射服务器程序:
int main(int argc,char **argv){
int sockfd;
struct sockaddr_in servaddr,cliaddr;
sockfd=socket(AF_INET,SOKC_DGRAM,0); //创建一个IPv4的UDP套接字
bzero(&servaddr,sizeof(servaddr));
servaddr.sin_family=AF_INET;
servaddr.sin_addr.s_addr=htonl(INADDR_ANY);
servaddr.sin_port=htons(9877);
bind(sockfd,(struct sockaddr *)&servaddr,sizeof(servaddr));
dg_ehco(sockfd,(struct sockaddr *)&cliaddr,sizeof(cliaddr)); //服务器处理工作
}
void dg_echo(int sockfd,struct sockaddr *pcliaddr,socklen_t clilen){
int n;
socklen_t len;
char mesg[MAXLINE];
while(true){
len=clilen;
n=recvfrom(sockfd,mesg,MAXLINE,0,pcliaddr,&len); //接收客户端发来的消息
sendto(sockfd,mesg,n,0,pcliaddr,len); //回送消息
}
}
由于UDP是一个无连接的协议,没有EOF之类的终止符,所以该函数永不终止。其次该函数提供的是一个迭代服务器,单个服务器进程就得处理所有客户。一般来说,大多数TCP服务器是并发的,大多数UDP服务器是迭代的。
对于本套接字,UDP层中隐含有排队发生。事实上每个UDP套接字都有一个接收缓冲区,到达该套接字的每个数据报都进入这个套接字接收缓冲区。当进程调用recvfrom时,缓冲区中的下一个数据报以先入先出顺序返回给进程。
<