网络编程之路---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;
}
表示根据前面的几部流程还是蛮轻松的。。
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;
}
表示根据前面的几部流程还是蛮轻松的。。
相关文章推荐
- Linux企业级项目实践之网络爬虫(10)——处理HTTP状态码
- HttpServletRequest简介\-\获取请求行\-\获取网络连接-------11-10-19
- 第一行代码总结:10网络:10.2.2使用HttpClient
- IOS学习之 网络编程(10)--简单介绍ASI框架的使用
- Linux网络管理员手册(10) 第十章 网络信息系统 NIS NYS
- Python全栈开发之10、网络编程
- Suse10 网络配置
- Vmvare 10 中安装centos7 出现 没有可用的网络设备
- 10 款免费的网络监控工具
- 神经网络深入(连载10)一个demo程序
- 黑马程序员--网络编程--java学习日记10(基础知识)
- 26-网络编程-10-网络编程(TCP协议-客户端)
- 10千兆位以太网(10 Gigabit Ethernet)--网络大典
- java 网络编程【10】 HTTP协议详解
- tensorflow 学习笔记10 网络模型的保存与提取
- 网络协议栈10:connect()函数之前之整体框图
- [网络流24题] 10 餐巾 (线性规划网络优化 ,最小费用最大流)
- 计算机网络(10)-----TCP的拥塞控制
- Solaris 10/11的网络配置
- WLAN 无线网络 10 - 控制帧和数据帧