您的位置:首页 > 理论基础 > 计算机网络

网络编程之路---10

2014-03-12 20:30 260 查看
开始之前先狠狠的鄙视自己一下,居然这么久都木有继续

select模型:防止在阻塞模式的套接字被锁死,避免在非阻塞套接字里重复检查WSAWOULDBLOCK错误.

流程:1:用FD_ZERO宏来初始化我们感兴趣的fd_set,也就是select函数的第二三四个参数

2:用FD_SET宏来将套接字句柄分配给相应的fd_set。3:调用select函数。

4:用FD_ISSET对套接字句柄进行检查,如果我们所关注的那个套接字句柄仍然在开始分配的那个fd_set里,那么说明马上可以进行相应的IO操作。比如一个分配给select第一个参数的套接字句柄在select返回后仍然在select第一个参数的fd_set里,那么说明当前数据已经来了,马上可以读取成功而不会被阻塞。

下面是select模型服务端代码:

#include <WinSock2.h>
#include <iostream>

#pragma comment(lib, "WS2_32.lib")

#define BUF_SIZE 64
#define DATA_BUFSIZE 1024
#define PORT 9990

typedef struct _SOCKET_INFORMATION
{
CHAR Buffer[DATA_BUFSIZE]; //收发数据的缓冲区
WSABUF DataBuf; //定义发送和接收数据的结构体
SOCKET socket; //与客户进行通信的socket
DWORD ByteSEND; //socket发送的字节数
DWORD ByteRECV; //socket接收的字节数
}SOCKET_INFORMATION , *LPSOCKET_INFORMATION;

DWORD TotalSockets; //正在使用的socket总数
LPSOCKET_INFORMATION SocketArray[FD_SETSIZE];

BOOL CreateSocketInformation(SOCKET s) //创建socket信息
{
LPSOCKET_INFORMATION SI;

if ( (SI = (LPSOCKET_INFORMATION) GlobalAlloc(GPTR, sizeof(SOCKET_INFORMATION))) == NULL )
{
printf("GlobalAlloc failed with error %d\n", GetLastError());
return FALSE;
}

SI->socket = s;
SI->ByteSEND = 0;
SI->ByteRECV = 0;
SocketArray[TotalSockets] = SI;
TotalSockets++;

return TRUE;
}

void FreeSocketInformation(DWORD index) //释放socket信息
{
LPSOCKET_INFORMATION SI = SocketArray[index];

DWORD i;

closesocket(SI->socket);
GlobalFree(SI);

for (i = index; i < TotalSockets; i++)
{
SocketArray[i] = SocketArray[i + 1];
}
TotalSockets--;
}

int main(int argc, char *argv[])
{
SOCKET ListenSocket;
SOCKET AcceptSocket;
SOCKADDR_IN InternetAddr;
WSADATA wsaData;
INT ret; //用来检查各种返回值结果的
FD_SET WriteSet;
FD_SET ReadSet;
DWORD Total = 0;
DWORD SendBytes = 0;
DWORD RecvBytes = 0;

if ((ret = WSAStartup(0X0202, &wsaData)) != 0)
{
printf("WSAstarup() failed\n");
WSACleanup();
return -1;
}

if ((ListenSocket = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED)) == INVALID_SOCKET)
{
printf("WSAsocket() failed\n");
closesocket(ListenSocket);
WSACleanup();
return -1;
}

InternetAddr.sin_family = AF_INET;
InternetAddr.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
InternetAddr.sin_port = htons(PORT);

if (bind(ListenSocket, (PSOCKADDR)&InternetAddr, sizeof(InternetAddr)) == SOCKET_ERROR)
{
printf("bind error\n");
closesocket(ListenSocket);
WSACleanup();
return -1;
}

if (listen(ListenSocket, 5))
{
printf("listen failed\n");
return -1;
}

ULONG noBlock = 1; //设置为非阻塞模式
if (ioctlsocket(ListenSocket, FIONBIO, &noBlock) == SOCKET_ERROR)
{
printf("ioctlsocket failed");
return -1;
}

CreateSocketInformation(ListenSocket);

while(TRUE)
{
//初始化我们感兴趣的fe_set
FD_ZERO(&ReadSet);
FD_ZERO(&WriteSet);

//用FD_SET宏来将套接字句柄分配给相应的fd_set
//如果想要检查一个套接字是否有数据需要接收,可以用FD_SET宏把套接字句柄加入可读性检查队列中

FD_SET(ListenSocket, &ReadSet);

//循环把所有的套接字都放进对应的集合?
for (DWORD i = 0; i < TotalSockets; ++i)
{
LPSOCKET_INFORMATION socket_info = SocketArray[i];
FD_SET(socket_info->socket, &ReadSet);
FD_SET(socket_info->socket, &WriteSet);
}

//调用select函数,如果该套接字没有数据要接收,select函数会把该套接字从可读性列表中删除
if ( (Total = select(0, &ReadSet, &WriteSet, NULL, NULL)) == SOCKET_ERROR)
{
printf("select failed\n");
return -1;
}

//用FD_ISSET对套接字句柄进行检查
for (DWORD i = 0; i < TotalSockets; ++i)
{
LPSOCKET_INFORMATION socket_info = SocketArray[i];

if (FD_ISSET(socket_info->socket, &ReadSet))
{
if (socket_info->socket == ListenSocket)
{
Total--; //就绪的socket-1

//参数addr所指向的套接字地址结构中将存放客户机的相关信息,addrLen指针将描述套接字地址结构长度
//通常情况下服务器对这些信息不是很感兴趣,故可以设置为NULL
if ( (AcceptSocket = accept(ListenSocket, NULL, NULL)) != INVALID_SOCKET)
{
noBlock = 1;
if (ioctlsocket(AcceptSocket, FIONBIO, &noBlock) == SOCKET_ERROR)
{
printf("ioctlsocket failed\n");
return -1;
}
//把accept放入数组中
if (CreateSocketInformation(AcceptSocket) == FALSE)
return -1;
}
else
{
if (GetLastError() != WSAEWOULDBLOCK)
{
printf("accept failed\n");
return -1;
}
}
}

else //有可读的数据
{
if (FD_ISSET(socket_info->socket, &ReadSet))
{
Total--; //就绪状态的socket--

//初始化缓冲区
memset(socket_info->Buffer, 0 , DATA_BUFSIZE);
socket_info->DataBuf.buf = socket_info->Buffer;
socket_info->DataBuf.len = DATA_BUFSIZE;

DWORD Flags = 0;

//重叠模型 WSARecv接收数据
if (WSARecv(socket_info->socket, &(socket_info->DataBuf), 1, &RecvBytes, &Flags, NULL, NULL))
{
if (WSAGetLastError() != WSAEWOULDBLOCK)
{
printf("wsarecv failed\n");
return -1;
}
continue;
}
else
{
socket_info->ByteRECV = RecvBytes; //记录接收数据的字节数
if (RecvBytes == 0)
{
FreeSocketInformation(i);
continue;
}
else
{
printf(socket_info->DataBuf.buf);
printf("\n");
}
}

}
}
}
else
{
//表明有数据可以发送
if (FD_ISSET(socket_info->socket, &WriteSet))
{
//初始化缓冲区位置;
socket_info->DataBuf.buf = socket_info->Buffer + socket_info->ByteSEND;
//初始化缓冲区长度
socket_info->DataBuf.len = socket_info->ByteRECV - socket_info->ByteSEND;

if (socket_info->DataBuf.len > 0)
{
if (WSASend(socket_info->socket, &(socket_info->DataBuf), 1, &SendBytes, 0, NULL, NULL))
{
if (WSAGetLastError() != WSAEWOULDBLOCK)
{
printf("WSASend failed\n");
FreeSocketInformation(i);
}
continue;
}
else
{
socket_info->ByteSEND += SendBytes;
if (socket_info->ByteSEND == socket_info->ByteRECV) //如果从客户端收到的数据都已经发送到客户端
{
socket_info->ByteRECV = 0;
socket_info->ByteSEND = 0;
}
}
}
}
}
}
}
closesocket(ListenSocket);
closesocket(AcceptSocket);
WSACleanup();
return 0;
}

表示根据前面的几部流程还是蛮轻松的。。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: