下面范例是一个关于非阻塞模式下的SOCKET设定处理---select模式。
使用的是UDP协议。
Client02首先启动,将本机的1207端口进行SOCKET绑定,并将该SOCKET模式设定为非阻塞模式, 此模式下不可直接调用recvfrom。
理由: 阻塞模式下,如果直接调用recvfrom从指定的SOCKET读取数据,如果还没有接受到来自Client01的数据,函数recvfrom会一直等待,直到有数据可以读出为止。当然,也可以创建一个独立的线程来专门调用recvfrom,这样可以不影响主线程的处理。
非阻塞模式下,recvfrom函数会立即返回,如果此时没有数据到达,该函数调用必然失败,返回值为WSAEWOULDBLOCK,表明当前状态是非阻塞模式的调用,且没有数据到达。这时,通常的做法是,创建一个独立的线程调用recvfrom,判断返回值(如果是WSAEWOULDBLOCK)进行循环调用recvfrom,直到有数据到达为止,这样可以,但是开销大,不好。如果,用MSG_PEEK来查询,其实开销也很大,也不推荐。
或者用select模式进行处理。(select应该是专门用来对付非阻塞模式的一种办法)
select函数察看要求进行处理的SOCKET, 同时设有超时Value, 这个函数本身会等待(也就是本身其实是个阻塞的函数),直到有可用的SOCKET为止,然后,进行处理,再进行select等待,如此反复执行。
//
// UDP Client 02
// IP: 127.0.0.1
// PORT: 1207
//
#define CONNECT_IP "127.0.0.1"
#define CONNECT_PORT 1207
#define DEFAULT_BUFFER_LEN 256
char* getBlockMode(int nMode)
{
return nMode==0? "阻塞模式设定" : "非阻塞模式设定" ;
}
int _tmain(int argc, _TCHAR* argv[])
{
printf(">>>>>UDP Client启动<<<<<<<\n");
WSAData wsData;
SOCKET sClient;
SOCKADDR_IN addrSender;
SOCKADDR_IN addrRecviver;
int addrSenderLen = sizeof(addrSender);
char sendBuff[DEFAULT_BUFFER_LEN] = {0};
char recvBuff[DEFAULT_BUFFER_LEN] = {0};
DWORD nMode = 0;
int nError = -1;
printf("-启动SOCKET库\n");
WSAStartup( MAKEWORD(2,2), &wsData );
addrRecviver.sin_family = AF_INET;
addrRecviver.sin_addr.S_un.S_addr = inet_addr( CONNECT_IP );
addrRecviver.sin_port = htons( CONNECT_PORT );
printf("-设定地址等信息完了: %s:%d\n", inet_ntoa(addrRecviver.sin_addr), ntohs(addrRecviver.sin_port));
printf("-创建SOCKET\n");
sClient = socket( AF_INET, SOCK_DGRAM , IPPROTO_UDP );
if( sClient == INVALID_SOCKET )
{
printf("!!! socket failed: %d\n", WSAGetLastError());
WSACleanup();
return -1;
}
//阻塞/非阻塞模式设定
nMode = 1; //阻塞模式/非阻塞模式
nError = ioctlsocket( sClient, FIONBIO, &nMode );
if( nError!=0 )
{
printf("!!! ioctlsocket failed: %d\n", WSAGetLastError());
WSACleanup();
return -1;
}
printf("-设定SOCKET模式: %s\n", getBlockMode(nMode));
printf("-绑定SOCKET用于接受数据\n");
nError = bind( sClient, (const sockaddr *)&addrRecviver, sizeof(addrRecviver) );
if( nError == SOCKET_ERROR )
{
printf("!!! bind failed: %d\n", WSAGetLastError());
WSACleanup();
return -1;
}
//
// select 模型用于监视制定socket 非阻塞模式设定中
//
fd_set fdRead;
struct timeval tv = {5,0}; //{second, milisec}
while( true )
{
FD_ZERO( &fdRead ); //首先清空fd集
FD_SET( sClient, &fdRead ); //加入要查看的SOCKET
printf("-select 监视中\n");
nError = select( 0, &fdRead, NULL, NULL, &tv ); //调用select进行SOCKET查看
;;;;;if( nError == 0 )
{
printf("-!!! select 超时: %d sec\n", tv.tv_sec);
continue;
}
else if( nError < 0 )
{
printf("!!! select failed: %d\n", WSAGetLastError());
WSACleanup();
return -1;
}
printf("-select 查找可读的SOCKET\n");
if( FD_ISSET(sClient, &fdRead) ) //查找可用的SOCKET,不可用的SOCKET会从fd集里删除掉
{//可读入数据了
printf("-找到一个可以读入的SOCKET\n");
nError = recvfrom( sClient, recvBuff, sizeof(recvBuff), 0, (sockaddr *)&addrSender, &addrSenderLen );
if( nError == SOCKET_ERROR )
{
printf("!!! recvfrom failed: %d\n", WSAGetLastError());
closesocket( sClient );
WSACleanup();
return -1;
}
printf("-收到数据: %s [%s:%d]\n", recvBuff, inet_ntoa(addrSender.sin_addr), ntohs(addrSender.sin_port));
}
else{
printf("-没有找到一个可以读入的SOCKET\n");
}
}//while(true)
printf("-Socket关闭\n");
closesocket( sClient );
WSACleanup();
return 0;
}
//
// UDP Client 01
// IP: 127.0.0.1
// PORT: 1207
//
#define CONNECT_IP "127.0.0.1"
#define CONNECT_PORT 1207
#define DEFAULT_BUFFER_LEN 256
char* getBlockMode(int nMode)
{
return nMode==0? "阻塞模式设定" : "非阻塞模式设定" ;
}
int _tmain(int argc, _TCHAR* argv[])
{
printf(">>>>>UDP Client启动<<<<<<<\n");
WSAData wsData;
SOCKET sClient;
SOCKADDR_IN addrSender;
SOCKADDR_IN addrRecvier;
int addrRecvierLen = sizeof(addrRecvier);
char sendBuff[DEFAULT_BUFFER_LEN] = {0};
char recvBuff[DEFAULT_BUFFER_LEN] = {0};
DWORD nMode = 0;
int nError = -1;
printf("-启动SOCKET库\n");
WSAStartup( MAKEWORD(2,2), &wsData );
addrSender.sin_family = AF_INET;
addrSender.sin_addr.S_un.S_addr = inet_addr( CONNECT_IP );
addrSender.sin_port = htons( CONNECT_PORT );
printf("-设定地址等信息完了: %s:%d\n", inet_ntoa(addrSender.sin_addr), ntohs(addrSender.sin_port));
printf("-创建SOCKET\n");
sClient = socket( AF_INET, SOCK_DGRAM , IPPROTO_UDP );
if( sClient == INVALID_SOCKET )
{
printf("!!! socket failed: %d\n", WSAGetLastError());
WSACleanup();
return -1;
}
//阻塞/非阻塞模式设定
nMode = 0; //阻塞模式/非阻塞模式
nError = ioctlsocket( sClient, FIONBIO, &nMode );
if( nError!=0 )
{
printf("!!! ioctlsocket failed: %d\n", WSAGetLastError());
WSACleanup();
return -1;
}
printf("-设定SOCKET模式: %s\n", getBlockMode(nMode));
strcpy( sendBuff, "message from Client01" );
printf("-送信设定: %s [%s:%d]\n", sendBuff, inet_ntoa(addrSender.sin_addr), ntohs(addrSender.sin_port));
nError = sendto( sClient, sendBuff, sizeof(sendBuff), 0, (const sockaddr *)&addrSender, sizeof(addrSender) );
if( nError == SOCKET_ERROR )
{
printf("!!! sendto failed: %d\n", WSAGetLastError());
WSACleanup();
return -1;
}
printf("-Socket关闭\n");
closesocket( sClient );
WSACleanup();
return 0;
}
|