linux网络编程之udp的connect趣谈

来源:互联网 发布:空间数据编辑 编辑:程序博客网 时间:2024/06/05 15:14

         对于linux网络编程,UDP协议不是面向连接的协议,直接把数据报发送到链路层,至于能不能到达目的IP和端口,它不关注;大部分时候再编写代码时候只需要在sendto函数中指定你要发送的端口和IP地址就可以了,不用绑定IP和端口。不过你是否考虑过,UDP到底是否可以进行connect,如果对UDP进行connect函数的调用,会发生什么现象呢?

        在进行socket网络编程代码编写时,我个人觉得有如下三个步骤在客户端和服务端是一致的,只是触发的时机不同。

  • socket创建通信套接口句柄(fd)---------------创建插座(抽象比喻)
  • bind绑定本地的IP和端口到句柄--------------在插座的后面连接上电源线
  • connect连接远端的IP和端口号到句柄,建立数据交换线路------------在插座的前面连接上使用者

以上三个步骤在TCP协议通信中client是必须存在,但是UDP协议中,可能针对connect看到的不多。

依据上面的说明,UDP可以分为如下两种:

  1. 未连接的UDP,新创建的UDP套接字。
  2. 已连接的UDP,调用connect就会这样。


比较1/2两种UDP,已经连接的UDP有如下特性:

  • 不需要给输出操作指定目的IP和目的端口,写到UDP的缓冲区里的数据,将自动发送到你调用connect指定的IP和端口。
  • 在一个已连接的UDP套接字上,内核由输入操作返回的数据报只有那些来自connect所指定的协议地址的数据报。目的地为这个已连接的UDP套接字的本地协议地址(IP和端口),远端地址不是该套接字早先connect到的协议地址的数据报,不会投递到该套接字。这样就限制了已连接的UDP套接字能且只能与一个对端交换数据报。
  • 由已连接的套接字引发的异步错误发回给他们所在的进程,而未连接的UDP套接字不接受任何异步错误。
  • 读写的操作接口方法增多了,除了可以使用sendto和recvfrom的接口外,还可以使用tcp的那套操作接口--read/readv/readmsg和write/writev等

对一个UDP的套接口多次调用connect的情况如何?

  • 连接新的IP和端口
  • 断开前面的连接

第一个目的不同于TCP连接connect的使用:对于TCP连接,connect只能调用一次;针对UDP则可以connect到不同的server,eg:client需要和多个服务器同时通信。

第二个目的为了断开一个已连接的UDP连接,再次调用connect时,把套接字地址结构的地址簇成员(IPv4为sin_family,IPv6为sin6_family),设置为AF_UNSPEC即可。



具体的操作例子如下:

回显服务器

#include<iostream>#include<stdio.h>#include<stdlib.h>#include<string.h>#include<unistd.h>#include<sys/socket.h>#include<netinet/in.h>#include<arpa/inet.h>using namespace std;int main(){int sockListener,nMsgLen;char szBuf[1024];struct sockaddr_in addrListener;socklen_t addrLen;addrLen=sizeof(struct sockaddr_in);bzero(&addrListener,sizeof(addrListener));addrListener.sin_family=AF_INET;addrListener.sin_port=htons(8000);if((sockListener=socket(AF_INET,SOCK_DGRAM,0))==-1){perror("error in getting a socket");exit(1);}if(bind(sockListener,(struct sockaddr*)&addrListener,sizeof(addrListener))==-1){perror("bind a listener for a socket");exit(2);}struct sockaddr_in addrClient;cout<<"callback server begin to listen"<<endl;while(true){nMsgLen=recvfrom(sockListener,szBuf,1024,0,(struct sockaddr*)&addrClient,&addrLen);if(nMsgLen>0){szBuf[nMsgLen]='\0';cout<<"send back:"<<szBuf<<endl;sendto(sockListener,szBuf,nMsgLen,0,(struct sockaddr*)&addrClient,addrLen);}}}


客户端1的例子

刚开始是connect,收到一个数据包后断开connect,再收到一个包后再次connect,依次.......

#include<iostream>#include<stdlib.h>#include<stdio.h>#include<string.h>#include<unistd.h>#include<sys/socket.h>#include<netinet/in.h>#include<arpa/inet.h>#include<sys/select.h>using namespace std;int main(){int sockClient,nMsgLen,nReady;char szRecv[1024],szSend[1024],szMsg[1024];struct sockaddr_in addrServer,addrClient,addrLocal;socklen_t addrLen;fd_set setHold,setTest;sockClient=socket(AF_INET,SOCK_DGRAM,0);addrLen=sizeof(struct sockaddr_in);bzero(&addrServer,sizeof(addrServer));addrServer.sin_family=AF_INET;addrServer.sin_addr.s_addr=inet_addr("127.0.0.1");addrServer.sin_port=htons(8000);addrLocal.sin_family=AF_INET;//bind to a local portaddrLocal.sin_addr.s_addr=htonl(INADDR_ANY);addrLocal.sin_port=htons(9000);if(bind(sockClient,(struct sockaddr*)&addrLocal,sizeof(addrLocal))==-1){perror("error in binding");exit(2);}int f = 0;if(connect(sockClient,(struct sockaddr*)&addrServer,sizeof(addrServer))==-1){perror("error in connecting");exit(1);}f = 1;FD_ZERO(&setHold);FD_SET(STDIN_FILENO,&setHold);FD_SET(sockClient,&setHold);cout<<"you can type in sentences any time"<<endl;while(true){setTest=setHold;nReady=select(sockClient+1,&setTest,NULL,NULL,NULL);if(FD_ISSET(0,&setTest)){nMsgLen=read(0,szMsg,1024);write(sockClient,szMsg,nMsgLen);}if(FD_ISSET(sockClient,&setTest)){if ( 1 == f ){// 1:by read/write........//nMsgLen=read(sockClient,szRecv,1024);// 2:by recvfrom/sendtonMsgLen = recvfrom(sockClient,szRecv,1024,0,NULL,NULL);perror("error in connecting recvfrom");// unconnectaddrServer.sin_family=AF_UNSPEC;connect(sockClient,(struct sockaddr*)&addrServer,sizeof(addrServer));f = 0;}else{f = 1;nMsgLen = recvfrom(sockClient,szRecv,1024,0,(struct sockaddr*)&addrServer,&addrLen);// connectbzero(&addrServer,sizeof(addrServer));addrServer.sin_family=AF_INET;addrServer.sin_addr.s_addr=inet_addr("127.0.0.1");addrServer.sin_port=htons(8000);connect(sockClient,(struct sockaddr*)&addrServer,sizeof(addrServer));}szRecv[nMsgLen]='\0';cout<<"read:"<<szRecv<<endl;}}}


客户端2,主动向客户端1发送数据

主要是为了证明,在client1向server建立连接后,不会接受client2的数据,断开连接后会接受client2的数据

#include<string.h>#include<iostream>#include<stdio.h>#include<stdlib.h>#include<unistd.h>#include<sys/socket.h>#include<netinet/in.h>#include<arpa/inet.h>using namespace std;int main(){socklen_t addrLen=sizeof(struct sockaddr_in);struct sockaddr_in addrServer;char szMsg[1024];int sockClient;addrServer.sin_family=AF_INET;addrServer.sin_addr.s_addr=inet_addr("127.0.0.1");addrServer.sin_port=htons(9000);sockClient=socket(AF_INET,SOCK_DGRAM,0);while(true){static int id=0;snprintf(szMsg,sizeof(szMsg),"this is %d",id++);sendto(sockClient,szMsg,strlen(szMsg),0,(struct sockaddr*)&addrServer,sizeof(addrServer));sleep(1);}}


通过connect建立的UDP套接口,可以有效的提高系统的整体性能,减少未连接的UDP上每次发送数据报时的连接建立/连接拆除的过程,只需要建立一次即可。











0 0
原创粉丝点击