找回密码
 立即注册→加入我们

QQ登录

只需一步,快速开始

搜索
热搜: 下载 VB C 实现 编写
查看: 4320|回复: 0

我注意到的UDP协议的一些值得注意的细节

[复制链接]

1109

主题

1649

回帖

7万

积分

用户组: 管理员

一只技术宅

UID
1
精华
244
威望
743 点
宅币
24180 个
贡献
46222 次
宅之契约
0 份
在线时间
2294 小时
注册时间
2014-1-26
发表于 2014-1-27 21:45:35 | 显示全部楼层 |阅读模式

欢迎访问技术宅的结界,请注册或者登录吧。

您需要 登录 才可以下载或查看,没有账号?立即注册→加入我们

×
通常我们在Windows下是这么包含socket头文件的:
  1. #include<winsock2.h>
复制代码


但是在GCC就不能这么写,GCC的socket是这么包含的:
  1. #include<sys/socket.h>
复制代码


好了,然后就是初始化,Windows是需要调用WSAStartup来初始化的,但是GCC不需要。同样Windows在用完socket之后还要调用WSACleanup来结束对套接字的使用,GCC可不必这样。
出错的时候,Windows会设置WSAGetLastError,GCC则设置errno(我不太清楚Windows会不会设置errno)。

来说点重要的信息吧。
首先就是UDP一般怎么发送数据包。和TCP/IP不同,UDP不需要连接。看下面的代码:

  1. SOCKET                 sSender;                                        //套接字
  2. SOCKADDR_IN        addr;                                                //要发送的端口号和地址
  3. char                szBuf[0x2000];                                //缓冲区,用来存储要发送的数据。我们把它定义为8K大小
  4. sSender=socket(AF_INET,SOCK_DGRAM,0);        //初始化套接字
  5. if(INVALID_SOCKET==sSender)                                //如果初始化失败则打印信息并返回。
  6. {
  7.         printf("调用socket函数失败。\n");
  8.         return;
  9. }

  10. //到这里是初始化成功的状态
  11. //现在要设置发送目标的IP和端口。
  12. //注意端口是unsigned short类型的整数,IP地址是无符号32位整数
  13. //IP地址可以看做一个有4个元素的unsigned char类型的数组,
  14. //假定我们输入的IP地址是192.168.1.100,那么这个数组的元素从0到3依次是192,168,1,100
  15. //也就是按照这样的顺序排列过来的。
  16. //inet_addr函数起到了这样的翻译作用,自动把给定的IP地址分成4字节,组合成32位整数一并返回。
  17. addr.sin_family=AF_INET;                                //必须为AF_INET
  18. addr.sin_port=9527;                                                //端口为9527,不错的数字
  19. addr.sin_addr.s_addr=inet_addr("127.0.0.1");//127.0.0.1这个地址表示本机,也就是自己发送给自己。

  20. strcpy(szBuf,"我靠这是数据包!");                //设置数据包数据

  21. //现在直接就可以调用sendto发送数据!
  22. //这里将会发送缓冲区szBuf的内容,并且发送的长度就是这个字符串的长度(包括结束符'\0')
  23. //sendto发送失败会返回SOCKET_ERROR,这个时候如果是Windows可以通过获取WSAGetLastError取得错误码。
  24. if(sendto(sSender,szBuf,strlen(szBuf)+1,0,(SOCKADDR*)&addr,sizeof(addr))==SOCKET_ERROR)
  25. {
  26.         printf("调用sendto函数失败。\n");
  27.         closesocket(sSender);
  28.         return;
  29. }
复制代码


可以看出,UDP只要初始化好socket就能直接进行发送,也就是调用完socket之后就能直接sendto,根本不需要连接的过程。很方便。
然后如何让套接字接收数据呢?
首先,我们刚才声明的套接字sSender经过sendto函数之后,它已经绑定了IP地址127.0.0.1,端口9527,那么这个套接字是“知道自己对应的IP地址的”。那么这个时候可以直接调用recvfrom来从127.0.0.1,端口9527接收数据。
而如果我们重新声明了一个套接字,SOCKET sReceiver=socket(AD_INET,SOCK_DGRAM,0);
这个新的套接字sReceiver并没有绑定一个IP地址,怎么办呢?很简单,手动绑定。


  1. //设置接收者接收的来源
  2. addr_recv.sin_port=9527;
  3. addr_recv.sin_family=AF_INET;
  4. addr_recv.sin_addr.s_addr=inet_addr("127.0.0.1");
  5. if(bind(sReceiver,(SOCKADDR*)&addr_recv,sizeof(addr_recv))==SOCKET_ERROR)//绑定IP地址和端口
  6. {
  7.         printf("调用bind函数失败。\n",);
  8.         closesocket(sReceiver);
  9.         return;
  10. }
复制代码



调用完bind之后就能直接recvfrom接收数据了!

  1. //从addr_recv指定的IP和端口接收数据。
  2. //和sendto不同,sendto会在发送后立即返回。而recvfrom则会卡住一直不返回,直到接收到了数据。
  3. //不管发过来的数据包有多大,总之只要收到了数据包recvfrom就会返回。
  4. //不同于TCP/IP的recv,UDP的recvfrom并不会像recv那样等待直到所有的数据包都接收到。而是一有数据包过来就会立即接收并返回。
  5. if(recvfrom(sReceiver,szBuf,sizeof(szBuf),0,NULL,NULL)==SOCKET_ERROR)
  6. {
  7.         printf("调用recvfrom函数失败。\n");
  8.         closesocket(sReceiver);
  9.         return;
  10. }
复制代码




看来虽然UDP并不能保证可靠性,但是它还是相当方便的。至少不用进行繁琐的连接、侦听、接受连接这种TCP的必须走的过程。

不过呢,虽然如此,但是UDP有一点,当一端发送数据的时候,另一端必须是正在调用recvfrom的状态,也就是说,如果我发送数据,但是你却要等一会儿才能调用recvfrom的话,你就无法接收到数据。你必须先调用recvfrom,然后对方再sendto,你才能顺利接收到数据,否则数据将丢失。
这一点和TCP/IP不一样,TCP/IP能保证数据是一定能传达的,也就是说,我先send,你晚点再recv,也能接收到数据。但是如果是UDP,我sendto之前你就必须先调用recvfrom,否则你就接收不到数据。

                        
回复

使用道具 举报

QQ|Archiver|小黑屋|技术宅的结界 ( 滇ICP备16008837号 )|网站地图

GMT+8, 2024-3-29 15:46 , Processed in 0.051635 second(s), 31 queries , Gzip On.

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表