一.概念:
UDP是传输层中面向无连接的协议,所以UDP丢包后是不会重传的,而且他在编程上服务端和客户端是没有区别的,有的只是“虚拟上”的服务端和客户端,他在编程的实现上也很简单,不像TCP那么复杂。
二.UDP终端的编程
由于UDP在服务端和客户端是一样的,所以称为UDP终端,编程步骤如下:
1.加载套接字
- WORD wVersionRequested;
- wVersionRequested=MAKEWORD(1,1);//指定Winsock库的版本:1.1
- WSADATA wsaData;
- int err;
- //使用WSAStartup加载套接字库,以及确定使用的套接字库的版本,这里使用的是1.1的版本
- err=WSAStartup(wVersionRequested,&wsaData);
- if(err!=0)
- return false;
- if(LOBYTE(wsaData.wVersion)!=1||HIBYTE(wsaData.wVersion)!=1)
- {
- //如果套接字库加载成功,但是版本不正确,那么调用WSACleanup 释放对套接字库的占用资源
- WSACleanup();
- return false;
- }
和TCP的加载套接字没区别
2.创建套接字
- SOCKET sock;
- unsigned long mode=1;//0:阻塞
- int nRet;
- sock=socket(AF_INET,SOCK_DGRAM,0);
与TCP区别再与socket的第二个参数,这里是SOCK_DGRAM(数据式套接字),TCP的是SOCK_STREAM(流式套接字)
3.绑定套接字(这部是必须的,在终端开启端口)
- //LPCTSTR在unicode环境下是const wchar *,非unicode环境下是const char *
- //INADDR_ANY表示如果一台机器上(服务器)有多块网卡,每个网卡都有各自的IP,则INADDR_ANY
- //会将套接字绑定到该机器上的所有网卡地址和端口上
- SOCKADDR_IN addrSrv;
- if(localIP==NULL)
- addrSrv.sin_addr.S_un.S_addr= htonl(INADDR_ANY);//由于这里需要网络字节序,所以将主机字节序转换成网络字节序
- else
- addrSrv.sin_addr.S_un.S_addr=inet_addr(localIP);//inet_addr将字符串ip转换成long型
- addrSrv.sin_family=AF_INET;
- addrSrv.sin_port=htons(localPort);//由于这里需要网络字节序,所以将主机字节序转换成网络字节序
- //下面的代码等效于这行代码return bind(hSocket,(SOCKADDR *)&addrSrv,sizeof(SOCKADDR))==0;
- if(bind(sock,(SOCKADDR *)&addrSrv,sizeof(SOCKADDR))==0)
- return true;
- else
- return false;
4.上面三步做完,就可以开启线程,在线程中使用recvfrom来接受数据(TCP使用的是recv)
- RLen=recvfrom(sock,(char *)RecvBuffer,RecvBufferLen,0,(SOCKADDR *)&addrFrom,addrDataLen);
- if(RLen<=0)
- {
- int error=WSAGetLastError();
- TRACE("socket api error is %d\n",error);
- }
RecvBuffer是接受到的数据,RecvBufferLen是需要一次接收的数据长度,RLen是实际接收的数据长度,addrFrom可以获取发送数据的UDP终端的地址和端口
注意: 虽然这里可以设置一次接收的数据长度RecvBufferLen,但是这个长度必须需要系统缓存的支持,系统有一个接收数据的缓存,这个缓存负责将网络传过来的数据放入其中,然后recv是从这个系统缓存取数据,系统默认的SOCKET接受缓存的大小为8688B(8.6K左右),如果sock函数recv或recvFrom从系统缓存中接受数据太慢,那么系统接受到新数据后就会将原缓存给覆盖了,这样在次调用recv函数就获取到了新数据,老数据就丢失了,这时候就要使用setsockopt方法将系统缓存开到32K最合适,如下:
- if(setsockopt(sock,SOL_SOCKET,SO_RCVBUF,(const char *)&Max_RecvFrom_Buffer_Size,sizeof(int))!=0)
- {
- //设置失败
- return INVALID_SOCKET;
- }
Max_RecvFrom_Buffer_Size=32*1024B,在创建一个客户端的套接字之后就要使用setsockopt来设置系统缓存的大小了,也就是第2部做完后就要设置系统缓存
5.使用sendto来发送数据(TCP使用的是send)
- SOCKADDR_IN addrTo;
- addrTo.sin_addr.S_un.S_addr=inet_addr(IP);//inet_addr将字符串ip转换成long型
- addrTo.sin_family=AF_INET;
- addrTo.sin_port=htons(Port);
- sendto(sock,SendBuffer,sendBufLen,0,(SOCKADDR*)&addrTo,sizeof(SOCKADDR));
SendBuffer是发送的数据,sendBufLen是一次发送数据的长度,SOCKADDR_IN这个结构填入需要发送到的目的IP和端口,这里的发送长度要考虑网络最大的传输单元MTU=1500B,发送的数据长度控制在1400B较好。