我注意到的UDP协议的一些值得注意的细节
通常我们在Windows下是这么包含socket头文件的:#include<winsock2.h>
但是在GCC就不能这么写,GCC的socket是这么包含的:
#include<sys/socket.h>
好了,然后就是初始化,Windows是需要调用WSAStartup来初始化的,但是GCC不需要。同样Windows在用完socket之后还要调用WSACleanup来结束对套接字的使用,GCC可不必这样。
出错的时候,Windows会设置WSAGetLastError,GCC则设置errno(我不太清楚Windows会不会设置errno)。
来说点重要的信息吧。
首先就是UDP一般怎么发送数据包。和TCP/IP不同,UDP不需要连接。看下面的代码:
SOCKET sSender; //套接字
SOCKADDR_IN addr; //要发送的端口号和地址
char szBuf; //缓冲区,用来存储要发送的数据。我们把它定义为8K大小
sSender=socket(AF_INET,SOCK_DGRAM,0); //初始化套接字
if(INVALID_SOCKET==sSender) //如果初始化失败则打印信息并返回。
{
printf("调用socket函数失败。\n");
return;
}
//到这里是初始化成功的状态
//现在要设置发送目标的IP和端口。
//注意端口是unsigned short类型的整数,IP地址是无符号32位整数
//IP地址可以看做一个有4个元素的unsigned char类型的数组,
//假定我们输入的IP地址是192.168.1.100,那么这个数组的元素从0到3依次是192,168,1,100
//也就是按照这样的顺序排列过来的。
//inet_addr函数起到了这样的翻译作用,自动把给定的IP地址分成4字节,组合成32位整数一并返回。
addr.sin_family=AF_INET; //必须为AF_INET
addr.sin_port=9527; //端口为9527,不错的数字
addr.sin_addr.s_addr=inet_addr("127.0.0.1");//127.0.0.1这个地址表示本机,也就是自己发送给自己。
strcpy(szBuf,"我靠这是数据包!"); //设置数据包数据
//现在直接就可以调用sendto发送数据!
//这里将会发送缓冲区szBuf的内容,并且发送的长度就是这个字符串的长度(包括结束符'\0')
//sendto发送失败会返回SOCKET_ERROR,这个时候如果是Windows可以通过获取WSAGetLastError取得错误码。
if(sendto(sSender,szBuf,strlen(szBuf)+1,0,(SOCKADDR*)&addr,sizeof(addr))==SOCKET_ERROR)
{
printf("调用sendto函数失败。\n");
closesocket(sSender);
return;
}
可以看出,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地址,怎么办呢?很简单,手动绑定。
//设置接收者接收的来源
addr_recv.sin_port=9527;
addr_recv.sin_family=AF_INET;
addr_recv.sin_addr.s_addr=inet_addr("127.0.0.1");
if(bind(sReceiver,(SOCKADDR*)&addr_recv,sizeof(addr_recv))==SOCKET_ERROR)//绑定IP地址和端口
{
printf("调用bind函数失败。\n",);
closesocket(sReceiver);
return;
}
调用完bind之后就能直接recvfrom接收数据了!
//从addr_recv指定的IP和端口接收数据。
//和sendto不同,sendto会在发送后立即返回。而recvfrom则会卡住一直不返回,直到接收到了数据。
//不管发过来的数据包有多大,总之只要收到了数据包recvfrom就会返回。
//不同于TCP/IP的recv,UDP的recvfrom并不会像recv那样等待直到所有的数据包都接收到。而是一有数据包过来就会立即接收并返回。
if(recvfrom(sReceiver,szBuf,sizeof(szBuf),0,NULL,NULL)==SOCKET_ERROR)
{
printf("调用recvfrom函数失败。\n");
closesocket(sReceiver);
return;
}
看来虽然UDP并不能保证可靠性,但是它还是相当方便的。至少不用进行繁琐的连接、侦听、接受连接这种TCP的必须走的过程。
不过呢,虽然如此,但是UDP有一点,当一端发送数据的时候,另一端必须是正在调用recvfrom的状态,也就是说,如果我发送数据,但是你却要等一会儿才能调用recvfrom的话,你就无法接收到数据。你必须先调用recvfrom,然后对方再sendto,你才能顺利接收到数据,否则数据将丢失。
这一点和TCP/IP不一样,TCP/IP能保证数据是一定能传达的,也就是说,我先send,你晚点再recv,也能接收到数据。但是如果是UDP,我sendto之前你就必须先调用recvfrom,否则你就接收不到数据。
页:
[1]