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); } } }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器
· DeepSeek如何颠覆传统软件测试?测试工程师会被淘汰吗?