UDP编程中的connect

来源:互联网 发布:mysql 5.0.45中文版 编辑:程序博客网 时间:2024/05/19 15:22

标准的udp客户端开了套接口后,一般使用sendto和recvfrom函数来发数据,最近看到ntpclient的代码里面是使用send函数直接法的,就分析了一下,原来udp发送数据有两种方法供大家选用的,顺便把udp的connect用法也就解释清楚了。 
方法一: 
socket----->sendto()或recvfrom() 
方法二: 
socket----->connect()----->send()或recv() 
首先从这里看出udp中也是可以使用connect的,但是这两种方法到底有什么区别呢?首先把这四个发送函数的定义列出来: 
int send(int s, const void *msg, size_t len, int flags); 
int sendto(int s, const void *msg, size_t len, int flags, 
const struct sockaddr *to, socklen_t tolen); 
int recv(int s, void *buf, size_t len, int flags); 
int  recvfrom(int  s, void *buf, size_t len, int flags, 
struct sockaddr *from,  socklen_t *fromlen); 
从他们的定义可以看出,sendto和recvfrom在收发时指定地址,而send和recv则没有,那么他们的地址是在那里指定的呢,答案就在于connect. 
int  connect(int  sockfd,  const  struct sockaddr *serv_addr, socklen_t 
addrlen); 
在udp编程中,如果你只往一个地址发送,那么你可以使用send和recv,在使用它们之前用connect把它们的目的地址指定一下就可以了。connect函数在udp中就是这个作用,用它来检测udp端口的是否开放是没有用的。下面是ntpclient中的代码 
struct sockaddr_in sa_dest; 
bzero((char *) sa_dest, sizeof(*sa_dest)); 
sa_dest->sin_family=AF_INET; 
if(StuffNetAddr(&(sa_dest->sin_addr),host)) 
return 1; 
sa_dest->sin_port=htons(port); 
if (connect(usd,(struct sockaddr *)&sa_dest,sizeof(sa_dest))==-1) 
{perror("connect");return 1;} 
return 0; 
=================================

除非套接口已连接,否则异步错误是不会返回到UDP套接口的,我们确实可以给UDP套接口调用connect,然而这样做的结果却与TCP连接大相径庭:没有三路握手过程。

相反内核只是检查是否存在立即可知的错误(例如一个显然不可达的目的地),记录对端的IP地址和端口号(取自传递给connect的套接口地址结构),然后立即返回到调用进程。

对于已连接UDP套接口,与缺省的未连接套接口相比,发生了三个变化: 
1 我们再也不能给输出操作指定宿IP和端口号,也就是说我们不使用sendto,而改用write或send,写到已连接UDP套接口上的任何内容都自动发送到由connect指定的协议地址(例如IP地址和端口号) 
2 我们不必使用recvfrom以获悉数据报的发送者,而改用read,recv或recvmsg,在一个已连接UDP套接口上由内核为输入操作返回的数据 报仅仅是那些来自connect所指定协议地址的数据报。目的地为这个已连接UDP套接口的本地协议地址,发源地却不是该套接口早先connect到的协 议地址的数据报,不会投递到该套接口。这样就限制了一个已连接UDP套接口而且仅能与一个对端交换数据报。 
3 由已连接的UDP套接口引发的异步错误返回给他们所在的进程。 
相反我们说过未连接UDP套接口不接收任何异步错误给一个UDP套接口多次调用connect拥有一个已连接UDP套接口的进程可以为下列2个目的之一: 
a.指定新的IP地址和端口号; 
b.断开套接口 
第一个目的(即给一个已连接UDP套接口指定新的对端)不同于TCP套接口中connect的使用:对于TCP套接口,connect只能调用一次。 
为了断开一个已connect的UDP套接口连接,我们再次调用connect时把套接口地址结构的地址簇成员(sin_family)设置为AF_UNSPEC。 
这么做可能返回一个EAFNOSUPPORT错误,不过没有关系。 
使得套接口断开连接的是在已连接UDP套接口上调用connect的进程。

================================= 
有如下的一些好处: 
1)选定了对端,内核只会将帮定对象的对端发来的数据报传给套接口,因此在一定环境下可以提升安全性; 
2)会返回异步错误,如果对端没启动,默认情况下发送的包对应的ICMP回射包不会给调用进程,如果用了connect,嘿嘿 
3)发送两个包间不要先断开再连接,提升了效率。 
做个实验测试下吧 
先弄个UDP回射服务器,把所有收到的数据报回射回去: 
a@a-desktop:~/d/lab$ cat rollbackserver.cpp 
#include<iostream> 
#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); 



再写个客户端,绑定个端口,再连接服务器端。随时接受键盘输入并发送到服务器端,随时接受端口到来的数据并打印。如果没有连接 ,发送到此端口的数据会被接受,但是调用connect后会怎样呢? 
a-desktop:~/d/lab$ cat udpclient.cpp 
#include<iostream> 
#include<stdlib.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); 

if(connect(sockClient,(struct sockaddr*)&addrServer,sizeof(addrServer))==-1) 

perror("error in connecting"); 
exit(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)) 

nMsgLen=read(sockClient,szRecv,1024); 
szRecv[nMsgLen]='\0'; 
cout<<"read:"<<szRecv<<endl; 



最后来个“第三者”,向第二个的端口发数据报。看她会不会成为忠贞的感情守护人: 
a@a-desktop:~/d/lab$ cat clienta.cpp 
#include<string.h> 
#include<iostream> 
#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); 


实验结果: 
现运行第一个程序,再运行第三个程序,然后运行第二个程序。 
服务器端: 
a@a-desktop:~/d/lab$ ./rollback 
callback server begin to listen 
send back:xinheblue likes playing 
send back:and listenning to music 
第二个程序: 
a@a-desktop:~/d/lab$ ./udpclient 
you can type in sentences any time 
xinheblue likes playing 
read:xinheblue likes playing 
and listenning to music 
read:and listenning to music 
实现结果证明,第二个程序调用connect后,不甩第三个程序发来的数据包。对于感情,希望将来我的她也能这样。。。

原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 正好压本科线该怎么办 大脚趾被砸了怎么办 脊柱侧弯每天疼怎么办 内衣扣的位置脊柱疼怎么办 练完瑜伽颈椎疼怎么办 乳胶枕头太高了怎么办 枕头太高脖子痛怎么办 颈椎突出症状缓解后怎么办 外痔疼的的历害怎么办 小肚子疼得历害怎么办 练瑜伽伤到颈椎怎么办 鼻子干口干胃烧怎么办 颈椎病压迫神经引起手麻怎么办 4个月婴儿睡觉少怎么办 晚上睡觉睡不好老是醒来怎么办 吃了没熟的香蕉怎么办 70岁父母老吵架怎么办 怀孕七个月晚上睡不着怎么办 九个月宝宝睡眠不好怎么办 一个月的宝宝放不下怎么办 宝宝被吓了发烧怎么办 体内火气重睡不着觉怎么办 宝宝睡觉一直翻身发出声音怎么办 严重失眠怎么办整夜睡不着觉 腿疼得睡不着觉怎么办 19岁晚上睡不着该怎么办 工作累的想哭怎么办 心累迷茫想哭怎么办 白天很累晚上又睡不着怎么办 发型睡觉压乱了怎么办 通宵一夜第二天怎么办 夏天了腿脚还凉怎么办 咖啡色三天了月经还是下不来怎么办 睡觉姿势不对腰疼怎么办 来月经吃了香瓜怎么办 减肥期间晚上有饭局怎么办 减肥期间遇到晚上聚餐怎么办 婴儿脸不向上睡怎么办 睡觉压奶了疼怎么办 堵奶了挤不下来怎么办 孕36周胎儿腿短怎么办