- UID
- 1
- 精华
- 积分
- 76361
- 威望
- 点
- 宅币
- 个
- 贡献
- 次
- 宅之契约
- 份
- 最后登录
- 1970-1-1
- 在线时间
- 小时
|
通常我们在Windows下是这么包含socket头文件的:
但是在GCC就不能这么写,GCC的socket是这么包含的:
好了,然后就是初始化,Windows是需要调用WSAStartup来初始化的,但是GCC不需要。同样Windows在用完socket之后还要调用WSACleanup来结束对套接字的使用,GCC可不必这样。
出错的时候,Windows会设置WSAGetLastError,GCC则设置errno(我不太清楚Windows会不会设置errno)。
来说点重要的信息吧。
首先就是UDP一般怎么发送数据包。和TCP/IP不同,UDP不需要连接。看下面的代码:
- SOCKET sSender; //套接字
- SOCKADDR_IN addr; //要发送的端口号和地址
- char szBuf[0x2000]; //缓冲区,用来存储要发送的数据。我们把它定义为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,否则你就接收不到数据。
|
|