UDP套接口编程
常用的UDP实现的程序:DNS域名系统,NFS网络文件系统,SNMP简单网络管理协议
ssize_t recvfrom(int sockfd,void *buff,size_t nbytes,int flags,struct sockaddr * from,socklen_t *addrlen); ssize_t sendto(int sockfd,void *buff,size_t nbytes,int flags,struct sockaddr * to,socklen_t addrlen);
sockfd:描述字
buff:缓冲区指针
nbytes 读写字节数
UDP服务器端
int main(int argc,char ** argv){ int sockfd; struct sockaddr_in servaddr,cliaddr; 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,(SA *)&servaddr,sizeof(servaddr)); dg_echo(sockfd,(SA *)&cliaddr,sizeof(cliaddr)); } void dg_echo(int sockfd,SA *pcliaddr,socklen_t clilen){ int n; socklen_t len; char mesg[MAXLINE]; for(;;){ len = clilen; n = Recvfrom(sockfd,mesg,MAXLINE,0,pcliaddr,&len); //读一个到达的数据包 Sendto(sockfd,mesg,n,0,pcliaddr,len); //发送回给客户机 } }
SOCK_DGRAM:UDP套接口
1 函数不能终止
2 服务器是迭代服务器,没有fork调用,单一服务器进程处理所有客户。
UDP客户机程序:
int main(int argc,char ** argv){ int sockfd; struct sockaddr_t servaddr; if(argc != 2) err_quit("usage:udpcli<IPaddress>"); bzero(&servaddr,sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_port = htons(SERV_PORT); inet_pton(AF_INET,argv[1],&servaddr,sin_addr); //装填套接字 sockfd = Socket(AF_INET,SOCK_DGRAM,0); dg_cli(stdin,sockfd,(SA *)&servaddr,sizeof(servaddr)); exit(0); } void dg_cli(FILE *fp,int sockfd,const SA *pservaddr,socklen_t servlen){ int n; char sendline[MAXLINE],recvline[MAXLINE+1]; while(Fgets(sendline,MAXLINE,fp) != NULL){ //从标准输入读一行 Sendto(sockfd,sendline,strlen(sendline),0,pservaddr,servlen); //使用sendto发送给服务器 n = Recvfrom(sockfd,recvline,MAXLINE,0,NULL,NULL); //使用recvfrom接收服务器回射,NULL无视目标主机 recvline[n] = 0; Fputs(recvline,stdout); //fputs输出回射行到标准输出 } }
改进的dg_cli
void dg_cli(FILE *fp,int sockfd,const SA * pservaddr,socklen_t servlen){ int n; char sendline[MAXLINE],recvline[MAXLINE+1]; socklen_t len; struct sockaddr *preply_addr; preply_addr = Malloc(servlen); while(Fgets(sendline,MAXLINE,fp)!= NULL ){ Sendto(sockfd,sendline,strlen(sendline),0,pservaddr,servlen); len = servlen; n = Recvfrom(sockfd,recvline,MAXLINE,0,preply_addr,&len); if(len != servlen || memcpy(pservaddr,preply_addr,len) != 0){ printf("reply from %s (ignored)\n"); Sock_ntop(preply_addr,len); continue; } recvline[n] = 0; Fputs(recvline,stdout); } }
解决办法:
1 给定由recvfrom返回的IP,在DNS中查找服务器验证
2 服务器配置每个IP地址创建套接口,捆绑IP地址此套接口,
仅在进程已将UDP套接口连接到确切的对方后,这些一步错误才返回给进程。
在已连接的UDP套接口上调用connect达到下面两个目的:
1 指定IP地址和端口号
2 断开套接口
使用connect连接后再调用read write
void dg_cli(FILE *fp,int sockfd,const SA * pservaddr,socklen_t servlen){ int n; char sendline[MAXLINE],recvline[MAXLINE+1]; Connect(sockfd,(SA *)pservaddr,servlen); while(Fgets(sendline,MAXLINE,fp)!= NULL){ Write(sockfd,sendline,strlen(sendline)); n=Read(sockfd,recvline,MAXLINE); recvline[n] = 0; Fputs(recvline,stdout); } }
对发送的UDP进行统计:
static void recvfrom_int(int); static int count; void dg_echo(int sockfd,SA *pcliaddr,socklen_t clilen){ socklen_t len; char mesg[MAXLINE]; Signal(SIGINT,recvfrom_int); for(;;){ len = clilen; Recvfrom(sockfd,mesg,MAXLINE,0,pcliaddr,&len); count++; } } static void recvfrom_int(int signo){ printf("\nreceived %d datagrams\n",count); exit(0); }
UDP与TCP的服务器复用:
int main(int argc,char ** argv){ int listenfd,connfd,updfd,nready,maxfdp1; char mesg[MAXLINE]; pid_t childpid; fd_set rset; ssize_t n; socklen_t len; const int on = 1; struct sockaddr_in cliaddr,servaddr; void sig_child(int); listenfd = Socket(AF_INET,SOCKSTREAM,0); bzero(&servaddr,sizeof(servaddr)); servaddr.sin_family=AF_INET; servaddr.sin_addr.s_add=htonl(INADDR_ANY); servaddr.sin_port=htons(SERV_PORT); Setsockopt(listenfd,SOL_SOCKET,SO_RESSEADDR,&on,sizeof(on)); Bind(listenfd,(SA *)&servaddr,sizeof(servaddr)); Listen(listenfd,LISTENQ); updfd=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(udpfd,(SA *)&servaddr,sizeof(servaddr)); Signal(SIGCHLD,sig_chld); FD_ZERO(&rset); maxfdp1=max(listenfd,udpfd)+1; for(;;){ FD_SET(listenfd,&rset); FD_SET(udpfd,&rset); if((nready=select(maxfdp1,&rset,NULL,NULL,NULL,NULL))<0){ if(errno==EINTR) continue; else err_sys("select error"); } if(FD_ISSET(listenfd,&rset)){ len = sizeof(cliaddr); connfd=Accept(listenfd,(SA *)&cliaddr,&len); if((childpid=Fork())==0){ Close(listenfd); str_echo(connfd); exit(0); } Close(connfd); } if(FD_ISSET(udpfd,&rset)){ len=sizeof(cliaddr); n=Recvfrom(udpfd,mesg,MAXLINE,0,(SA *)&cliaddr,&len); Sendto(udpfd,mesg,n,0,(SA *)&cliaddr,len); } } }