UDP bind connect

1:UDP中可以使用connect系统调用

2:UDP中connect操作与TCP中connect操作有着本质区别。

TCP中调用connect会引起三次握手,client与server建立连结.UDP中调用connect内核仅仅把对端ip&port记录下来.

3:UDP中可以多次调用connect,TCP只能调用一次connect.  

UDP多次调用connect有两种用途:1,指定一个新的ip&port连结. 2,断开和之前的ip&port的连结.

指定新连结,直接设置connect第二个参数即可.
断开连结,需要将connect第二个参数中的sin_family设置成 AF_UNSPEC即可. 


4:UDP中使用connect可以提高效率.原因如下:

普通的UDP发送两个报文内核做了如下:#1:建立连结#2:发送报文#3:断开连结#4:建立连结#5:发送报文#6:断开连结

采用connect方式的UDP发送两个报文内核如下处理:#1:建立连结#2:发送报文#3:发送报文另外一点,  每次发送报文内核都由可能要做路由查询.

5:采用connect的UDP发送接受报文可以调用send,write和recv,read操作.当然也可以调用sendto,recvfrom.

调用sendto的时候第五个参数必须是NULL,第六个参数是0.调用recvfrom,recv,read系统调用只能获取到先前connect的ip&port发送的报文. 

UDP中使用connect的好处:1:会提升效率.前面已经描述了.2:高并发服务中会增加系统稳定性.原因:假设client A 通过非connect的UDP与server B,C通信.B,C提供相同服务.为了负载均衡,我们让A与B,C交替通信.A 与 B通信IPa:PORTa <----> IPb:PORTb;


A 与 C通信IPa:PORTa' <---->IPc:PORTc 


假设PORTa 与 PORTa'相同了(在大并发情况下会发生这种情况),那么就有可能出现A等待B的报文,却收到了C的报文.导致收报错误.解决方法内就是采用connect的UDP通信方式.在A中创建两个udp,然后分别connect到B,C.



///


对于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 port
	addrLocal.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/sendto
				nMsgLen = recvfrom(sockClient,szRecv,1024,0,NULL,NULL);
				perror("error in connecting recvfrom");
				// unconnect
				addrServer.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);
				// connect
				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);
				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上每次发送数据报时的连接建立/连接拆除的过程,只需要建立一次即可
已标记关键词 清除标记
相关推荐
©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页