Windows网络编程笔记6 --- WinSock I/O 控制方法
Windows提供了两种方式“套接字模式”和“套接字I/O模型”,可对一个套接字上的I/O行为加以控制。套接字模式用于决定在随一个套接字调用时,那些 Winsock函数的行为。其中的模型包括括select(选择)、WSAAsyncSelect(异步选择)、WSAEventSelect(事件选择)、OverlappedI/O(重叠式I/O)以及Completionport(完成端口)等等。
所有Windows平台都支持套接字以锁定或非锁定方式工作。在锁定模式下,在I/O操作完成前,执行操作的Winsock函数(比如send和recv)会一直等候下去,不会立即返回程序(将控制权交还给程序)。而在非锁定模式下,Winsock函数无论如何都会立即返回。
锁定模式使用线程同步机制完成数据的I/O,扩展性不好,尤其是有多个套接字时。
非锁定模式的使用需要函数 ioctlsocket(),使用如下:
SOCKET s;
unsigned long ub =;
int nRet;
s = socketAF_INET,SOCK_STREAM,);
nRet = ioctlsocket(s,FIOBIO,(unsigned long *)&ub);//设置非锁定模式
if(nRet == SOCKET_ERROR)
{
//进入非锁定模式失败
}
套接字I/O模型
1、select模型
//函数原型
int select(
int nfds, //与早期程序兼容,可忽略
fd_set FAR * readfds,// 可读性
fd_set FAR * writefds,// 可写性
fd_set FAR * exceptfds,// 例外数据
const struct timeval FAR * timeout//超时时间
);
参数readfds 表示以下几种情况
有数据可读入、连接已经关闭(重设或者终止)、如果已经listen,并且正在建立连接,那么accept函数会返回成功。
参数writefds表示以下几种情况
有数据可发出、如果已完成对一个非锁定连接调用的处理,连接就会成功。
参数exceptfds表示如下
如果已完成对一个非锁定连接调用的处理,连接尝试就会失败。有带外(Out-of-band,OOB)数据可供读取。
其中fd_set结构如下:
typedef struct fd_set {
u_int fd_count; /* how many are SET? */
SOCKET fd_array[FD_SETSIZE]; /* an array of SOCKETs */
} fd_set;
超时时间
struct timeval {
long tv_sec; /* seconds */秒
long tv_usec; /* and microseconds */毫秒
};
用select对套接字进行监视之前,在自己的应用程序中,必须将套接字句柄分配给一个集合,设置好一个或全部读、写以及例外fd_set结构。将一个套接字分配给任何一个集合后,再来调用select,便可知道一个套接字上是否正在发生上述的I/O活动。
下面是对fd_set进行操作的一些宏
FD_CLR(s,*set):从set中删除套接字s。
FD_ISSET(s,*set):检查s是否set集合的一名成员;如答案是肯定的是,则返回TRUE。
FD_SET(s,*set):将套接字s加入集合set。
FD_ZERO(*set):将set初始化成空集合。
用select操作一个或多个套接字句柄的全过程:
1)使用FD_ZERO宏,初始化自己感兴趣的每一个fd_set。
2)使用FD_SET宏,将套接字句柄分配给自己感兴趣的每个fd_set。
3)调用select函数,然后等待在指定的fd_set集合中,I/O活动设置好一个或多个套接字句柄。select完成后,会返回在所有fd_set集合中设置的套接字句柄总数,并对每个集合进行相应的更新。
4)根据select的返回值,我们的应用程序便可判断出哪些套接字存在着尚未完成(待决)的I/O操作—具体的方法是使用FD_ISSET宏,对每个fd_set集合进行检查。
5)知道了每个集合中“待决”的I/O操作之后,对I/O进行处理,然后返回步骤1),继续进行select处理。
简单过程如下
SOCKET s;
fd_set fdread;
int ret;
//创建
//bind()
//accept()
//开始
while ()
{
FD_ZERO(&fdread);//初始化
FD_SET(s,&fdread);//添加
if ((ret = select(,&fdread,NULL,NULL,NULL)) == SOCKET_ERROR)
{
//添加失败
}
if (ret > )
{
if (FD_ISSET(s,&fdread))
{
//已经是集合的一部分,正在读取数据
}
}
//其他操作
}
2、WSAAsyncSelect模型
WSAAsyncSelect 的使用必须要在窗口应用程序中使用,在回调函数中实现处理。
int WSAAsyncSelect(
_In_ SOCKET s,//关心的socket
_In_ HWND hWnd,//窗口句柄
_In_ unsigned int wMsg,//消息
_In_ long lEvent//事件类型
);
事件类型很多,如图
WSAAsyncSelect模式实现,这个实现起来挺简单的
// WSAAsyncSelect模式实现,这个实现起来挺简单的。
#include <winsock2.h>
#include <tchar.h>
#define PORT 7890
#define MSGSIZE 1024
#define WM_SOCKET WM_USER+1
#pragma comment(lib, "ws2_32.lib") LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM);
int WINAPI WinMain( HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nShowCmd
)
{ static TCHAR szAppName[] = TEXT ("WSAAsyncSelect Test") ;
HWND hwnd ;
MSG msg ;
WNDCLASS wndclass ; wndclass.style = CS_HREDRAW | CS_VREDRAW ;
wndclass.lpfnWndProc = WndProc;
wndclass.cbClsExtra = ;
wndclass.cbWndExtra = ;
wndclass.hInstance = hInstance ;
wndclass.hIcon = LoadIcon (NULL, IDI_APPLICATION) ;
wndclass.hCursor = LoadCursor (NULL, IDC_ARROW) ;
wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ;
wndclass.lpszMenuName = NULL ;
wndclass.lpszClassName = szAppName ; if (!RegisterClass (&wndclass))
{
MessageBox (NULL, TEXT ("Program requires Windows NT!"),
szAppName, MB_ICONERROR) ;
return ;
} hwnd = CreateWindow (szAppName, TEXT ("WSAAsyncSelect"),
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT,
NULL, NULL, hInstance, NULL) ; ShowWindow (hwnd, nShowCmd) ;
UpdateWindow (hwnd) ; while (GetMessage (&msg, NULL, , ))
{
TranslateMessage (&msg) ;
DispatchMessage (&msg) ;
}
return msg.wParam ;
} LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
WSADATA wsd;
static SOCKET sListen;
SOCKET sClient;
SOCKADDR_IN local, client;
int ret, iAddrSize = sizeof(client);
char szMessage[MSGSIZE] ;
switch (message)
{
case WM_CREATE:
// 初始化
WSAStartup(0x0202, &wsd); // 创建socket
sListen = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); // 绑定
local.sin_addr.S_un.S_addr = inet_addr("192.168.0.87");
local.sin_family = AF_INET;
local.sin_port = htons(PORT);
bind(sListen, (struct sockaddr *)&local, sizeof(local)); // 监听
listen(sListen, );
// 设置WSAAsyncSelect模式
WSAAsyncSelect(sListen, hwnd, WM_SOCKET, FD_ACCEPT);
return ;
case WM_DESTROY:
closesocket(sListen);
WSACleanup();
PostQuitMessage();
return ; case WM_SOCKET:
if (WSAGETSELECTERROR(lParam))//使用宏 WSAGETSELECTERROR 判断lParam高字节是否有错误
{
closesocket(wParam);
break;
} switch (WSAGETSELECTEVENT(lParam))//使用宏WSAGETSELECTEVENT判断lParam低字节是什么操作
{
case FD_ACCEPT:
// 从客户端接收连接
sClient = accept(wParam, (struct sockaddr *)&client, &iAddrSize); // 设置事件监听
WSAAsyncSelect(sClient, hwnd, WM_SOCKET, FD_READ | FD_WRITE | FD_CLOSE);
break;
case FD_READ: //读
ret = recv(wParam, szMessage, MSGSIZE, );
if (ret == || ret == SOCKET_ERROR && WSAGetLastError() == WSAECONNRESET)
{
closesocket(wParam);
}
else
{
szMessage[ret] = '\0';
MessageBox(hwnd,"接收成功",NULL,MB_OK);
send(wParam, szMessage, strlen(szMessage), );
}
break;
case FD_WRITE://写
send(wParam, szMessage, strlen(szMessage), );
break;
case FD_CLOSE: //关闭
closesocket(wParam);
break;
}
return ;
} return DefWindowProc(hwnd, message, wParam, lParam);
}
3、WSAEventSelect 模型
这个模型和WSAAsyncSelect的区别是,它能将网络事件投递给一个事件对象句柄,而不是投递到一个窗口程序。
因此使用的时候首先就要生成一个事件对象,函数如下
WSAEVENT WSACreateEvent(void);//创建一个事件对象句柄
//创建WSAEventSelect模型
int WSAEventSelect(
_In_ SOCKET s,//关心的套接字
_In_ WSAEVENT hEventObject,//事件对象
_In_ long lNetworkEvents//感兴趣的网络事件类型,FD_READ,FD_WRITE一类的
);
为WSAEventSelect创建的事件拥有两种工作状态,以及两种工作模式。其中,两种工作状态分别是“已传信”(signaled)和“未传信”(nonsignaled)。工作模式则包括“人工重设”(manual reset)和“自动重设”(auto reset)。WSACreateEvent最开始在一种未传信的工作状态中,并用一种人工重设模式,来创建事件句柄。随着网络事件触发了与一个套接字关联在一起的事件对象,工作状态便会从“未传信”转变成“已传信”。由于事件对象是在一种人工重设模式中创建的,所以在完成了一个I/O请求的处理之后,我们的应用程序需要负责将工作状态从已传信更改为未传信。
几个函数
//重置使用WSAResetEvent函数。
BOOL WSAResetEvent(
_In_ WSAEVENT hEvent
);
//如果不再使用,就是放资源
BOOL WSACloseEvent(
_In_ WSAEVENT hEvent
); //创建过之后要监听事件,这时使用WSAWaitForMultipleEvents函数等待
DWORD WSAWaitForMultipleEvents(
DWORD cEvents,//事件对象数量
const WSAEVENT *lphEvents,//所有的套接字事件,最多64个
BOOL fWaitAll,//是否等待全部完成才返回,还是大于一个时就返回
DWORD dwTimeout,//超时返回
BOOL fAlertable//在这个模型中忽略,为false,在重叠I/O时使用
);
知道网络事件引起的套接字后,可以查询发生了什么类型的网络事件
//知道网络事件引起的套接字后,可以查询发生了什么类型的网络事件
int WSAEnumNetworkEvents(
_In_ SOCKET s,
_In_ WSAEVENT hEventObject,//需要重设的事件对象,
_Out_ LPWSANETWORKEVENTS lpNetworkEvents//网络事件
);
//参数三结构
typedef struct _WSANETWORKEVENTS {
long lNetworkEvents;//网络事件类型
int iErrorCode[FD_MAX_EVENTS];//错误代码
} WSANETWORKEVENTS, FAR * LPWSANETWORKEVENTS;
WSAWaitForMultipleEvents 使用举例
//WSAWaitForMultipleEvents 使用举例
#include <winsock2.h>
#include <Ws2tcpip.h>
#include <stdio.h> #pragma comment(lib, "Ws2_32.lib")
#define DATA_BUFSIZE 4096 int main()
{
//-----------------------------------------
WSADATA wsaData = { };
int iResult = ;
BOOL bResult = TRUE; WSABUF DataBuf;
char buffer[DATA_BUFSIZE]; DWORD EventTotal = ;
DWORD RecvBytes = ;
DWORD Flags = ;
DWORD BytesTransferred = ; WSAEVENT EventArray[WSA_MAXIMUM_WAIT_EVENTS];
WSAOVERLAPPED AcceptOverlapped;
SOCKET ListenSocket = INVALID_SOCKET;
SOCKET AcceptSocket = INVALID_SOCKET; DWORD Index; //-----------------------------------------
// 初始化
iResult = WSAStartup(MAKEWORD(, ), &wsaData);
if (iResult != ) {
wprintf(L"WSAStartup failed: %d\n", iResult);
return ;
}
//-----------------------------------------
//创建监听套接字
ListenSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (ListenSocket == INVALID_SOCKET) {
wprintf(L"socket failed with error = %d\n", WSAGetLastError());
WSACleanup();
return ;
} u_short port = ;
char *ip;
sockaddr_in service;
service.sin_family = AF_INET;
service.sin_port = htons(port);
hostent *thisHost; thisHost = gethostbyname("");
if (thisHost == NULL) {
wprintf(L"gethostbyname failed with error = %d\n", WSAGetLastError());
closesocket(ListenSocket);
WSACleanup();
return ;
} ip = inet_ntoa(*(struct in_addr *) *thisHost->h_addr_list); service.sin_addr.s_addr = inet_addr(ip); //-----------------------------------------
// 绑定
iResult = bind(ListenSocket, (SOCKADDR *) & service, sizeof (SOCKADDR));
if (iResult != ) {
wprintf(L"bind failed with error = %d\n", WSAGetLastError());
closesocket(ListenSocket);
WSACleanup();
return ;
}
//-----------------------------------------
// 监听
iResult = listen(ListenSocket, );
if (iResult != ) {
wprintf(L"listen failed with error = %d\n", WSAGetLastError());
closesocket(ListenSocket);
WSACleanup();
return ;
}
wprintf(L"Listening...\n"); //-----------------------------------------
// 接受连接
AcceptSocket = accept(ListenSocket, NULL, NULL);
if (AcceptSocket == INVALID_SOCKET) {
wprintf(L"accept failed with error = %d\n", WSAGetLastError());
closesocket(ListenSocket);
WSACleanup();
return ;
}
wprintf(L"Client Accepted...\n"); //-----------------------------------------
// 创建事件,并初始化Overlapped结构.
EventArray[EventTotal] = WSACreateEvent();
if (EventArray[EventTotal] == WSA_INVALID_EVENT) {
wprintf(L"WSACreateEvent failed with error = %d\n", WSAGetLastError());
closesocket(AcceptSocket);
closesocket(ListenSocket);
WSACleanup();
return ;
} ZeroMemory(&AcceptOverlapped, sizeof (WSAOVERLAPPED));
AcceptOverlapped.hEvent = EventArray[EventTotal]; DataBuf.len = DATA_BUFSIZE;
DataBuf.buf = buffer; EventTotal++; //-----------------------------------------
// 接受数据
if (WSARecv(AcceptSocket, &DataBuf, , &RecvBytes, &Flags, &AcceptOverlapped, NULL) ==
SOCKET_ERROR) {
iResult = WSAGetLastError();
if (iResult != WSA_IO_PENDING)
wprintf(L"WSARecv failed with error = %d\n", iResult);
}
//-----------------------------------------
// 开始在Socket上操作
while () { //-----------------------------------------
// 等待重叠I/O完成
Index = WSAWaitForMultipleEvents(EventTotal, EventArray, FALSE, WSA_INFINITE, FALSE); //-----------------------------------------
// 重置事件
bResult = WSAResetEvent(EventArray[Index - WSA_WAIT_EVENT_0]);
if (bResult == FALSE) {
wprintf(L"WSAResetEvent failed with error = %d\n", WSAGetLastError());
}
//-----------------------------------------
// 确定重叠I/O事件标志
bResult =
WSAGetOverlappedResult(AcceptSocket, &AcceptOverlapped, &BytesTransferred, FALSE,
&Flags);
if (bResult == FALSE) {
wprintf(L"WSAGetOverlappedResult failed with error = %d\n", WSAGetLastError());
}
//-----------------------------------------
// 如果连接关闭,就关闭Socket
if (BytesTransferred == ) {
wprintf(L"Closing accept Socket %d\n", AcceptSocket);
closesocket(ListenSocket);
closesocket(AcceptSocket);
WSACloseEvent(EventArray[Index - WSA_WAIT_EVENT_0]);
WSACleanup();
return ;
}
//-----------------------------------------
// 如果接收到数据就在数据发送到客户端
iResult =
WSASend(AcceptSocket, &DataBuf, , &RecvBytes, Flags, &AcceptOverlapped, NULL);
if (iResult != ) {
wprintf(L"WSASend failed with error = %d\n", WSAGetLastError());
}
//-----------------------------------------
//重置Overlapped结构
Flags = ;
ZeroMemory(&AcceptOverlapped, sizeof (WSAOVERLAPPED)); AcceptOverlapped.hEvent = EventArray[Index - WSA_WAIT_EVENT_0]; //-----------------------------------------
// 重置缓冲区
DataBuf.len = DATA_BUFSIZE;
DataBuf.buf = buffer;
} closesocket(ListenSocket);
closesocket(AcceptSocket);
WSACleanup();
return ;
}
WSAEnumNetworkEvent 使用举例
//WSAEnumNetworkEvent 使用举例
#include <windows.h>
#include <winsock2.h>
#include <Ws2tcpip.h>
#include <stdio.h>
#pragma comment(lib, "Ws2_32.lib")
int main()
{
//-------------------------
WSADATA wsaData;
int iResult; SOCKET SocketArray[WSA_MAXIMUM_WAIT_EVENTS], ListenSocket;
WSAEVENT EventArray[WSA_MAXIMUM_WAIT_EVENTS];
WSANETWORKEVENTS NetworkEvents;
sockaddr_in InetAddr;
DWORD EventTotal = ;
DWORD Index;
DWORD i; HANDLE NewEvent = NULL; // 初始化
iResult = WSAStartup(MAKEWORD(, ), &wsaData);
if (iResult != )
{
wprintf(L"WSAStartup failed with error: %d\n", iResult);
return ;
} //创建
ListenSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (ListenSocket == INVALID_SOCKET)
{
wprintf(L"socket function failed with error: %d\n", WSAGetLastError() );
return ;
} InetAddr.sin_family = AF_INET;
InetAddr.sin_addr.s_addr = htonl(INADDR_ANY);
InetAddr.sin_port = htons(); //绑定
iResult = bind(ListenSocket, (SOCKADDR *) & InetAddr, sizeof (InetAddr));
if (iResult != )
{
wprintf(L"bind failed with error: %d\n", WSAGetLastError() );
return ;
} // 创建事件
NewEvent = WSACreateEvent();
if (NewEvent == NULL)
{
wprintf(L"WSACreateEvent failed with error: %d\n", GetLastError() );
return ;
}
// 将事件类型绑定到套接字上
iResult = WSAEventSelect(ListenSocket, NewEvent, FD_ACCEPT | FD_CLOSE);
if (iResult != )
{
wprintf(L"WSAEventSelect failed with error: %d\n", WSAGetLastError() );
return ;
} // 监听
iResult = listen(ListenSocket, );
if (iResult != )
{
wprintf(L"listen failed with error: %d\n", WSAGetLastError() );
return ;
}
// 添加事件
SocketArray[EventTotal] = ListenSocket;
EventArray[EventTotal] = NewEvent;
EventTotal++; // 等待所有套接字上的事件发生
Index = WSAWaitForMultipleEvents(EventTotal, EventArray, FALSE, WSA_INFINITE, FALSE);
Index = Index - WSA_WAIT_EVENT_0; //枚举事件类型,直到失败
for (i = Index; i < EventTotal; i++)
{
Index = WSAWaitForMultipleEvents(, &EventArray[i], TRUE, , FALSE);
if ((Index != WSA_WAIT_FAILED) && (Index != WSA_WAIT_TIMEOUT))
{
WSAEnumNetworkEvents(SocketArray[i], EventArray[i], &NetworkEvents);
}
} //...
return ;
}
4、重叠I/O模式
重叠I./O模式只支持Winsock2 ,可以使用WSARecv和WSASend实现
关键字 WSA_FLAG_OVERLAPPED
函数WSASocket在创建时可以手动指定模式
SOCKET s=WSASocket(AF_INET,SOCK_STREAM,0,NULL,0,WSA_FLAG_OVERLAPPED);
而函数socket默认就是重叠I/O模式。
typedef struct _WSAOVERLAPPED {
ULONG_PTR Internal;
ULONG_PTR InternalHigh;
union {
struct {
DWORD Offset;
DWORD OffsetHigh;
};
PVOID Pointer;
};
HANDLE hEvent;//
} WSAOVERLAPPED, *LPWSAOVERLAPPED;
前几个参数有系统维护,只有hEvent字段有点儿特殊,它允许应用程序将一个事件对象句柄同一个套接字关联起来。
//结构Overlapped
BOOL WSAAPI WSAGetOverlappedResult(
_In_ SOCKET s,
_In_ LPWSAOVERLAPPED lpOverlapped,
_Out_ LPDWORD lpcbTransfer,//一次收发的实际字节数
_In_ BOOL fWait,//在重叠I/O模式中无效
_Out_ LPDWORD lpdwFlags//接收结果标志
);
对重叠I/O操作进行管理过程
1)创建一个套接字,开始在指定的端口上监听连接请求。
2)接受一个进入的连接请求。
3)为接受的套接字新建一个WSAOVERLAPPED结构,并为该结构分配一个事件对象句柄。也将事件对象句柄分配给一个事件数组,以便稍后由WSAWaitForMultipleEvents函数使用。
4)在套接字上投递一个异步WSARecv请求,指定参数为WSAOVERLAPPED结构。注意函数通常会以失败告终,返回SOCKET_ERROR错误状态WSA_IO_PENDING(I/O操作尚未完成)。
5)使用步骤3)的事件数组,调用WSAWaitForMultipleEvents函数,并等待与重叠调用关联在一起的事件进入“已传信”状态(换言之,等待那个事件的“触发”)。
6)WSAWaitForMultipleEvents函数完成后,针对事件数组,调用WSAResetEvent(重设事件)函数,从而重设事件对象,并对完成的重叠请求进行处理。
7)使用WSAGetOverlappedResult函数,判断重叠调用的返回状态是什么。
8)在套接字上投递另一个重叠WSARecv请求。
9)重复步骤5-8)。
5、完成端口模型
适合在管理成千上万的套接字时使用。
使用这种模型之前,首先要创建一个I/O完成端口对象,用它面向任意数量的套接字句柄,管理多个I/O请求。要做到这一点,需要调用CreateCompletionPort函数。函数有两种功能:
1. 用于创建一个完成端口对象。
2. 将一个句柄同完成端口关联到一起。
//创建一个完成端口对象
HANDLE CreateIoCompletionPort(
HANDLE FileHandle, //关联的文件句柄,可能为套接字句柄
HANDLE ExistingCompletionPort,//已经存在的完成端口
LONG_PTR CompletionKey,// 传送给处理函数的参数
DWORD NumberOfConcurrentThreads//有多少个线程在访问这个消息队列
);
参数ExistingCompletionPort表示已经存在的完成端口。如果为NULL,则为新建一个IOCP(I/O完成端口)。
创建过程
HANDLE completionPort=CreateIoCompletionPort(INVALID_HANDLE_VALUE,NULL,0,0);
//获取排队完成的状态
BOOL GetQueuedCompletionStatus(
__in HANDLE CompletionPort,//完成端口
__out LPDWORD lpNumberOfBytesTransferred,//实际接受的数据
__out PULONG_PTR lpCompletionKey,//“单句柄数据”
__out LPOVERLAPPED *lpOverlapped,//重叠I/O结构
__in DWORD dwMilliseconds//超时时间
);
一旦所有套接字句柄都已关闭,便需在完成端口上,终止所有工作者线程的运行,此时调用函数PostQueuedCompletionStatus向所有线程发送终止命令。
BOOL PostQueuedCompletionStatus(
__in HANDLE CompletionPort,//完成端口句柄
__in DWORD dwNumberOfBytesTransferred,//实际接受数据
__in ULONG_PTR dwCompletionKey,//完成端口关键字“单句柄数据”
__in_opt LPOVERLAPPED lpOverlapped//重叠I/O结构
);
使用过程
1、创建一个完成端口,第四个参数保持为0,指定在完成端口上,每个处理器一次只允许执行一个工作者线程
2、判断系统内到底安装了几个处理器
3、创建工作者线程,根据步骤2得到的处理器信息,在完成端口上,为已完成的I/O请求提供服务
4、准备好一个监听套接字
5、使用accept函数,接受进入的请求
6、创建一个数据结构,用于容纳“单句柄数据”,同时在结构中存入接受的套接字句柄。
7、调用CreateCompletionPort,将之accept返回的新套接字同完成端口关联到一起
8、开始在已接受的连接上进行I/O操作
9、重复5到8,直到服务器终止。
这些操作对套接字I/O至关重要,需要理解
Windows网络编程笔记6 --- WinSock I/O 控制方法的更多相关文章
- Windows网络编程笔记4 -- Winsock 协议相关知识
Win32平台上的Winsock编程,Winsock是一个与协议无关的接口.以下协议是我们需要了解的: 网络协议的特征包括: 1. 面向消息 2. 面向连接和无线接 3. 可靠性和次序性 4. ...
- Windows网络编程笔记5 -- 其他套接字
包括红外线套接字(IrSock).IPX/SPX 套接字.NetBIOS 套接字.AppleTalk 套接字.ATM 套接字等.对这些套接字进行简单介绍. 第一.红外线套接字(I r S o c k) ...
- Windows网络编程笔记1
第一部分 传统网络API 传统的网络接口NetBIOS.重定向器.邮槽.命名管道等.第一,NetBIOS(Network Basic Input/Output System, NetBIOS)“网络基 ...
- Windows网络编程笔记3 ---- 邮槽和命名管道
邮槽和命名管道的使用方法也很简单,只需几个有限的函数就可以实现双方的通信. 第三.邮槽 邮槽----进程间通信机制. 通过邮槽客户进程可以将消息通过广播给一个或多个服务进程.这是一个单向通信机制,缺点 ...
- Windows网络编程笔记2
这一次看看重定向器和如何使用Netbios函数获取本机mac地址 5.获取Mac地址 利用NCBASTAT命令实现,适配器状态命令会返回一个 ADAPTER_STATUS结构,紧接着是大量 NAME_ ...
- Winsock网络编程笔记(1)----入门
今天第一次接触winsock网络编程,看的资料是Windows网络编程第二版.通过博客记住自己的看书笔记.. 在这里贴出第一个程序,虽然程序什么都没做,但以此作为入门,熟悉其网络编程风格.. #inc ...
- Windows网络编程 2 【转】
Windows网络编程使用winsock.Winsock是一个基于Socket模型的API,在Windows系统中广泛使用.使用Winsock进行网络编程需要包含头文件Winsock2.h,需要使用库 ...
- Linux网络编程笔记(修订版)
我的网络编程笔记, 因为最近又要做Linux下的网络编程,故重新修订, 其中一些内容参考了文末的链接及文章 1. 基本概念 2. 基本接口 2.1. 打开一个socket 2.2. 将 ...
- storysnail的Windows串口编程笔记
storysnail的Windows串口编程笔记 作者 He YiJun – storysnail<at>gmail.com 团队 ls 版权 转载请保留本声明! 本文档包含的原创代码根据 ...
随机推荐
- fish 与oh-my-fish 的安装
fish 相对于 自带的shell优势很大,最近在研究使用中. 安装教程如下: sudo apt-get install fish oh-my-fish是github上开源项目,使得fish的使用更加 ...
- SQL优化 · 经典案例 · 索引篇
Introduction 在这些年的工作之中,由于SQL问题导致的数据库故障层出不穷,下面将过去六年工作中遇到的SQL问题总结归类,还原问题原貌,给出分析问题思路和解决问题的方法,帮助用户在使用数据库 ...
- 新版graylog2安装过程
Graylog是一个开源的 log 收容器,背后的储存是搭配 mongodb,而搜寻引擎则由 elasticsearch 提供.以前版本主要有两个部分集合而成 server 与 web interfa ...
- COGS 750. 栅格网络流
★★☆ 输入文件:flowa.in 输出文件:flowa.out 简单对比时间限制:1 s 内存限制:128 MB [问题描述] Bob 觉得一般图的最大流问题太难了,他不知道如何解决 ...
- 汇编:jmp系列跳转指令总结
助记方法: J:跳转C: 进位位置位N: 否S: 符号位置位o: 溢出位置位Z: 零标志位置位E: 等于P:奇偶位置位A: AboveB: BelowL: Less (Little的比较级)G: Gr ...
- Kibana功能一览
Overview标签 总共32个请求,最大响应时间:4.7秒 Usage标签 可以看到HTTP请求的发起时间分布 Performance and Quality 6个请求里,响应时间在100毫秒以下的 ...
- hdu-2256 Problem of Precision---矩阵快速幂+数学技巧
题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=2256 题目大意: 题目要求的是(sqrt(2)+sqrt(3))^2n %1024向下取整的值 解题 ...
- Android(java)学习笔记92:Android线程形态之 AsyncTask (异步任务)
1. AsyncTask和Handler的优缺点比较: 1)AsyncTask实现的原理和适用的优缺点 AsyncTask是Android提供的轻量级的异步类,可以直接继承AsyncTa ...
- scikit-learn 中 OneHotEncoder 解析
概要 在 sklearn 包中,OneHotEncoder 函数非常实用,它可以实现将分类特征的每个元素转化为一个可以用来计算的值.本篇详细讲解该函数的用法,也可以参考官网 sklearn.prepr ...
- 第26题:LeetCode572:Subtree of Another Tree另一个树的子树
题目描述 给定两个非空二叉树 s 和 t,检验 s 中是否包含和 t 具有相同结构和节点值的子树.s 的一个子树包括 s 的一个节点和这个节点的所有子孙.s 也可以看做它自身的一棵子树. 示例 1: ...