|
OVERLAPPED IO |
重叠模式 之 完成例程
完成例程是另外一种管理重叠I/O请求的方法,完成例程其实就是一些函数,在开始时有我们传递给一个重叠I/O请求,当一个重叠I/O请求完成后,系统会调用这些完成例程。完成例程必须有以下的函数原型:
void CALLBACK CompletionROUTINE(
DWORD dwError,
DWORD cbTransferred,
LPWSAOVERLAPPED lpOverlapped,
DWORD dwFlags
);
dwError,表示一个重叠I/O请求后的完成状态。
cbTransferred,实际传输的字节量。
dwFlags,尚未使用,设为0。
重叠模式 之 基于事件通知的重叠模型
重叠模式是的总体设计以WIN32重叠I/O机制为基础,要想在一个套接字上使用重叠I/O模型,必须使用WSA_FLAG_OVERLAPPED这个标志,如下:
s=WSASocket(AF_INET,SOCK_STREAM,0,NULL,0,WSA_FLAG_OVERLAPPED);
如果使用的是socket函数,则默认设置为WSA_FLAG_OVERLAPPED。
使用重叠模式,就不得不熟悉WSAOVERLAPPED结构,下面是这个结构的定义:
typedef struct WSAOVERLAPPED
{
DWORD Internal;
DWORD InternalHigh;
DWORD Offset;
DWORD OffsetHigh;
WSAEVENT hEvent;
} WSAOVERLAPPED, FAR * LPWSAOVERLAPPED;
其中前4个参数都是系统内部调用的,不关我们的事。
WSAOVERLAPPED结构中的hEvent即可和一个事件对象进行关联:用WSACreateEvent创建一个事件对象,将重叠结构的hEvent字段分配给新创建的事件对象。然后调用下列winsock函数(带重叠结构类型的参数),
WSASend 、WSASendTo、 WSARecv、 WSARecvFrom、 WSAIoctl、AcceptEx、 TransimitFile
在对重叠I/O请求完成后,Winsock会更改重叠结构中的事件对象的事件传信状态,从“未传信”-->“已传信”,由于WSACreate最开始在一种未传信的工作状态中,并用一种人工重设模式创建句柄的,所以在I/O请求完成后我们的程序有责任将工作状态从“已传信”转到“未传信”,即调用WSAResetEvent函数。
对重叠I/O请求完成后,我们可以调用WSAGetOverlappedResult函数来判断重叠调用是否成功,定义如下:
BOOL WSAGetOverlappedResult(
SOCKET s,
LPWSAOVERLAPPED lpOverlapped,
LPDWORD lpcbTransfer,
BOOL fWait,
LPDWORD lpdwFlags
);
其中,lpcbTransfer对应一个双字变量,负责接收一次重叠发送或接收操作实际传输的字节数。
fWait,用于决定是否应该等待一次重叠操作完成。
lpdwFlags,负责接收结果标志。
下面是一个完整的过程:
1.创建一个套接字,开始在指定的端口上监听连接请求。
2.接收进入的连接请求。
3.为接受的套接字新建一个WSAOVERLAPPED结构,并为该结构分配一个事件对象句柄,同时把该对象句柄分配给一个事件数组,以便由WSAWaitForMultipleEvent函数使用。
4.在套接字上投递一个异步WSARecv请求,指定参数为WSAOVERLAPPED结构。
5.使用步骤3中的事件数组,调用WSAWaitForMultipleEvent,等待与重叠调用关联的事件对像进入“已传信”状态(即,等待事件触发)
6.WSAWaitForMultipleEvent函数完成后,针对事件数组,调用WSAResetEvent重设事件对象,并对完成的重叠请求进行处理。
7.使用WSAGetOverlappedResult函数,判断重叠调用的返回状态。
8.在套接字上投递另一个重叠WSARecv请求。
9.重复5--8步骤。
代码如下:
#include #include #include
#define PORT 5150#define DATA_BUFSIZE 8192
typedef struct _SOCKET_INFORMATION { CHAR Buffer[DATA_BUFSIZE]; WSABUF DataBuf; SOCKET Socket; WSAOVERLAPPED Overlapped; DWORD BytesSEND; DWORD BytesRECV;} SOCKET_INFORMATION, * LPSOCKET_INFORMATION;
DWORD WINAPI ProcessIO(LPVOID lpParameter);
DWORD EventTotal = 0;WSAEVENT EventArray[WSA_MAXIMUM_WAIT_EVENTS];LPSOCKET_INFORMATION SocketArray[WSA_MAXIMUM_WAIT_EVENTS];CRITICAL_SECTION CriticalSection;
void main(void){ WSADATA wsaData; SOCKET ListenSocket, AcceptSocket; SOCKADDR_IN InternetAddr; DWORD Flags; DWORD ThreadId; DWORD RecvBytes; INT Ret;
InitializeCriticalSection(&CriticalSection);
if ((Ret = WSAStartup(0x0202,&wsaData)) != 0) { printf("WSAStartup failed with error %d\n", Ret); WSACleanup(); return; }
if ((ListenSocket = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED)) == INVALID_SOCKET) { printf("Failed to get a socket %d\n", WSAGetLastError()); return; }
InternetAddr.sin_family = AF_INET; InternetAddr.sin_addr.s_addr = htonl(INADDR_ANY); InternetAddr.sin_port = htons(PORT);
if (bind(ListenSocket, (PSOCKADDR) &InternetAddr, sizeof(InternetAddr)) == SOCKET_ERROR) { printf("bind() failed with error %d\n", WSAGetLastError()); return; }
if (listen(ListenSocket, 5)) { printf("listen() failed with error %d\n", WSAGetLastError()); return; }
// Setup the listening socket for connections.
if ((AcceptSocket = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED)) == INVALID_SOCKET) { printf("Failed to get a socket %d\n", WSAGetLastError()); return; }
if ((EventArray[0] = WSACreateEvent()) == WSA_INVALID_EVENT) { printf("WSACreateEvent failed with error %d\n", WSAGetLastError()); return; }
// Create a thread to service overlapped requests
if (CreateThread(NULL, 0, ProcessIO, NULL, 0, &ThreadId) == NULL) { printf("CreateThread failed with error %d\n", GetLastError()); return; }
EventTotal = 1;
while(TRUE) { // Accept inbound connections
if ((AcceptSocket = accept(ListenSocket, NULL, NULL)) == INVALID_SOCKET) { printf("accept failed with error %d\n", WSAGetLastError()); return; }
EnterCriticalSection(&CriticalSection);
// Create a socket information structure to associate with the accepted socket.
if ((SocketArray[EventTotal] = (LPSOCKET_INFORMATION) GlobalAlloc(GPTR, sizeof(SOCKET_INFORMATION))) == NULL) { printf("GlobalAlloc() failed with error %d\n", GetLastError()); return; }
// Fill in the details of our accepted socket.
SocketArray[EventTotal]->Socket = AcceptSocket; ZeroMemory(&(SocketArray[EventTotal]->Overlapped), sizeof(OVERLAPPED)); SocketArray[EventTotal]->BytesSEND = 0; SocketArray[EventTotal]->BytesRECV = 0; SocketArray[EventTotal]->DataBuf.len = DATA_BUFSIZE; SocketArray[EventTotal]->DataBuf.buf = SocketArray[EventTotal]->Buffer;
if ((SocketArray[EventTotal]->Overlapped.hEvent = EventArray[EventTotal] = WSACreateEvent()) == WSA_INVALID_EVENT) { printf("WSACreateEvent() failed with error %d\n", WSAGetLastError()); return; }
// Post a WSARecv request to to begin receiving data on the socket
Flags = 0; if (WSARecv(SocketArray[EventTotal]->Socket, &(SocketArray[EventTotal]->DataBuf), 1, &RecvBytes, &Flags, &(SocketArray[EventTotal]->Overlapped), NULL) == SOCKET_ERROR) { if (WSAGetLastError() != ERROR_IO_PENDING) { printf("WSARecv() failed with error %d\n", WSAGetLastError()); return; } }
EventTotal++;
LeaveCriticalSection(&CriticalSection);
// // Signal the first event in the event array to tell the worker thread to // service an additional event in the event array // if (WSASetEvent(EventArray[0]) == FALSE) { printf("WSASetEvent failed with error %d\n", WSAGetLastError()); return; } }
}
DWORD WINAPI ProcessIO(LPVOID lpParameter){ DWORD Index; DWORD Flags; LPSOCKET_INFORMATION SI; DWORD BytesTransferred; DWORD i; DWORD RecvBytes, SendBytes;
// Process asynchronous WSASend, WSARecv requests.
while(TRUE) {
if ((Index = WSAWaitForMultipleEvents(EventTotal, EventArray, FALSE, WSA_INFINITE, FALSE)) == WSA_WAIT_FAILED) { printf("WSAWaitForMultipleEvents failed %d\n", WSAGetLastError()); return 0; }
// If the event triggered was zero then a connection attempt was made // on our listening socket. if ((Index - WSA_WAIT_EVENT_0) == 0) { WSAResetEvent(EventArray[0]); continue; }
SI = SocketArray[Index - WSA_WAIT_EVENT_0]; WSAResetEvent(EventArray[Index - WSA_WAIT_EVENT_0]);
if (WSAGetOverlappedResult(SI->Socket, &(SI->Overlapped), &BytesTransferred, FALSE, &Flags) == FALSE || BytesTransferred == 0) { printf("Closing socket %d\n", SI->Socket);
if (closesocket(SI->Socket) == SOCKET_ERROR) { printf("closesocket() failed with error %d\n", WSAGetLastError()); }
GlobalFree(SI); WSACloseEvent(EventArray[Index - WSA_WAIT_EVENT_0]);
// Cleanup SocketArray and EventArray by removing the socket event handle // and socket information structure if they are not at the end of the // arrays.
EnterCriticalSection(&CriticalSection);
if ((Index - WSA_WAIT_EVENT_0) + 1 != EventTotal) for (i = Index - WSA_WAIT_EVENT_0; i < EventTotal; i++) { EventArray[i] = EventArray[i + 1]; SocketArray[i] = SocketArray[i + 1]; }
EventTotal--;
LeaveCriticalSection(&CriticalSection);
continue; }
// Check to see if the BytesRECV field equals zero. If this is so, then // this means a WSARecv call just completed so update the BytesRECV field // with the BytesTransferred value from the completed WSARecv() call.
if (SI->BytesRECV == 0) { SI->BytesRECV = BytesTransferred; SI->BytesSEND = 0; } else { SI->BytesSEND += BytesTransferred; }
if (SI->BytesRECV > SI->BytesSEND) {
// Post another WSASend() request. // Since WSASend() is not gauranteed to send all of the bytes requested, // continue posting WSASend() calls until all received bytes are sent.
ZeroMemory(&(SI->Overlapped), sizeof(WSAOVERLAPPED)); SI->Overlapped.hEvent = EventArray[Index - WSA_WAIT_EVENT_0];
SI->DataBuf.buf = SI->Buffer + SI->BytesSEND; SI->DataBuf.len = SI->BytesRECV - SI->BytesSEND;
if (WSASend(SI->Socket, &(SI->DataBuf), 1, &SendBytes, 0, &(SI->Overlapped), NULL) == SOCKET_ERROR) { if (WSAGetLastError() != ERROR_IO_PENDING) { printf("WSASend() failed with error %d\n", WSAGetLastError()); return 0; } } } else { SI->BytesRECV = 0;
// Now that there are no more bytes to send post another WSARecv() request.
Flags = 0; ZeroMemory(&(SI->Overlapped), sizeof(WSAOVERLAPPED)); SI->Overlapped.hEvent = EventArray[Index - WSA_WAIT_EVENT_0];
SI->DataBuf.len = DATA_BUFSIZE; SI->DataBuf.buf = SI->Buffer;
if (WSARecv(SI->Socket, &(SI->DataBuf), 1, &RecvBytes, &Flags, &(SI->Overlapped), NULL) == SOCKET_ERROR) { if (WSAGetLastError() != ERROR_IO_PENDING) { printf("WSARecv() failed with error %d\n", WSAGetLastError()); return 0; } } } }}
select模型:
select函数原型
int select(
int nfds,
fd_set FAR * readfds,
fd_set FAR * writefds,
fd_set FAR * exceptfds,
const struct timeval FAR * timeout
);
nfds为了保持与早期兼容,可忽略。
fd_set,代表一系列特定的套接字的集合,readset用于检查可读性,writeset用于检查可写性,exceptset用于检查异常数据,具体用法如下:
假定我们想测试一个套接字是否可读,必须将该套接字增加到readset中,再等待select函数完成,完成后判断该套接字是否仍然是readset集合的一部分,是,则该套接字可读。三个参数至少有一个不为null。
其中的timeval的结构如下:
struct timeval
{
long tv_sec; (秒)
long tv_usec; (毫秒)
}
如select函数调用失败,返回SOCKET_ERROR.
对fd_set集合进行操作,可用宏:
FD_CLR(s,*set):从set中删除套接字s;
FD_ISSET(s,*set):检查s是否是set集合的一员,返回true或false
FD_SET(s,*set):将套接字s加入集合set
FD_ZERO(*set):将set初始化为空集合。
WSAAsyncSelect 模型
利用这个模型,应用程序可在一个套接字上接收以Windows消息为基础的网络时间通知。使用这种模型,必须提供一个“窗口”,用于接收消息。函数原型如下:
int WSAAsyncSelect(
SOCKET s,
HWND hwnd,
unsigned int wMsg,
Long lEvent
);
s:对应套接字
hwnd:接收消息的窗体句柄
wMsg:发送的消息,应该为一个比WM_USER大的值。
IEvent:对应为一系列网络事件的组合,包括:FD_READ,FD_WRITE,FD_ACCEPT,FD_CONNECT,FD_CLOSE.
如:WSAAsyncSelect(s,hwnd,WM_SOCKET,FD_CONNECT|FD_READ|FD_WRITE|FD_CLOSE)
在调用WSAAsyncSelect后,即在hwnd对应的窗体回调函数中对网络事件(消息)进行处理:
LRESULT CALLBACK WindowProc(
HWND hWnd,
UINT uMsg,
WPARAM wParam,
LPARAM lParam
);
其中wParam指定在其上面发生了一个网络事件的套接字。
lParam低字节指定了已经发生的网络事件,而lParam的高字节包含了可能出现的错误代码。在网络事件消息到达的时候,先调用WSAGETSELECTERROR宏返回高字节包含的错误信息。若没有错误,则调用WSAGETSELECTEVENT宏返回lParam低字节部分。
WSAEventSelect 模型
和WSAAsyncSelect模型不同的是,这种模型中网络事件会投递至一个事件对象句柄,而非窗口句柄。所以要求我们针对每一个套接字都要建立一个事件对象。用WSACreateEvent函数,定义如下:
WSAEVENT WSACreateEvent(void);
然后调用WSAEventSelect函数,定义如下:
int WSAEventSelect(
SOCKET s;
WSAEVENT hEventObject,
long lNetworkEvents
);
鉴于这个模型的复杂性,还是用代码说话吧:
#include #include #include
#define PORT 5150#define DATA_BUFSIZE 8192
typedef struct _SOCKET_INFORMATION { CHAR Buffer[DATA_BUFSIZE]; WSABUF DataBuf; SOCKET Socket; DWORD BytesSEND; DWORD BytesRECV;} SOCKET_INFORMATION, * LPSOCKET_INFORMATION;
BOOL CreateSocketInformation(SOCKET s);void FreeSocketInformation(DWORD Event);
DWORD EventTotal = 0;WSAEVENT EventArray[WSA_MAXIMUM_WAIT_EVENTS];LPSOCKET_INFORMATION SocketArray[WSA_MAXIMUM_WAIT_EVENTS];
void main(void){ SOCKET Listen; SOCKET Accept; SOCKADDR_IN InternetAddr; DWORD Event; WSANETWORKEVENTS NetworkEvents; WSADATA wsaData; DWORD Ret; DWORD Flags; DWORD RecvBytes; DWORD SendBytes;
if ((Ret = WSAStartup(0x0202, &wsaData)) != 0) //初始化winsock { printf("WSAStartup() failed with error %d\n", Ret); return; }
if ((Listen = socket (AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) //创建Listen套接字 { printf("socket() failed with error %d\n", WSAGetLastError()); return; }
CreateSocketInformation(Listen); //创建一个_SOCKET_INFORMATION结构实体
if (WSAEventSelect(Listen, EventArray[EventTotal - 1], FD_ACCEPT|FD_CLOSE) == SOCKET_ERROR) { printf("WSAEventSelect() failed with error %d\n", WSAGetLastError()); return; }
InternetAddr.sin_family = AF_INET; InternetAddr.sin_addr.s_addr = htonl(INADDR_ANY); InternetAddr.sin_port = htons(PORT);
if (bind(Listen, (PSOCKADDR) &InternetAddr, sizeof(InternetAddr)) == SOCKET_ERROR) { printf("bind() failed with error %d\n", WSAGetLastError()); return; }
if (listen(Listen, 5)) { printf("listen() failed with error %d\n", WSAGetLastError()); return; }
while(TRUE) { // Wait for one of the sockets to receive I/O notification and if ((Event = WSAWaitForMultipleEvents(EventTotal, EventArray, FALSE, WSA_INFINITE, FALSE)) == WSA_WAIT_FAILED) { printf("WSAWaitForMultipleEvents failed with error %d\n", WSAGetLastError()); return; }
//通过SocketArray[Event - WSA_WAIT_EVENT_0]获得事件是在哪个socket上发生
//通过WSAEnumNetworkEvents获得发生了什么类型的网络事件 if (WSAEnumNetworkEvents(SocketArray[Event - WSA_WAIT_EVENT_0]->Socket, EventArray[Event - WSA_WAIT_EVENT_0], &NetworkEvents) == SOCKET_ERROR) //必须减去预定义值,才能得到实际的索引位置 { printf("WSAEnumNetworkEvents failed with error %d\n", WSAGetLastError()); return; }
if (NetworkEvents.lNetworkEvents & FD_ACCEPT) { if (NetworkEvents.iErrorCode[FD_ACCEPT_BIT] != 0) { printf("FD_ACCEPT failed with error %d\n", NetworkEvents.iErrorCode[FD_ACCEPT_BIT]); break; }
if ((Accept = accept(SocketArray[Event - WSA_WAIT_EVENT_0]->Socket, NULL, NULL)) == INVALID_SOCKET) { printf("accept() failed with error %d\n", WSAGetLastError()); break; }
if (EventTotal > WSA_MAXIMUM_WAIT_EVENTS) { printf("Too many connections - closing socket.\n"); closesocket(Accept); break; }
CreateSocketInformation(Accept);//一旦有套接字accepted了,就创建事件对象和LPSOCKET_INFORMATION 结构
if (WSAEventSelect(Accept, EventArray[EventTotal - 1], FD_READ|FD_WRITE|FD_CLOSE) == SOCKET_ERROR) //继续对accepted 的 套接字调用WSAEventSelect { printf("WSAEventSelect() failed with error %d\n", WSAGetLastError()); return; }
printf("Socket %d connected\n", Accept); }
// Try to read and write data to and from the data buffer if read and write events occur.
if (NetworkEvents.lNetworkEvents & FD_READ || NetworkEvents.lNetworkEvents & FD_WRITE) { if (NetworkEvents.lNetworkEvents & FD_READ && NetworkEvents.iErrorCode[FD_READ_BIT] != 0) { printf("FD_READ failed with error %d\n", NetworkEvents.iErrorCode[FD_READ_BIT]); break; }
if (NetworkEvents.lNetworkEvents & FD_WRITE && NetworkEvents.iErrorCode[FD_WRITE_BIT] != 0) { printf("FD_WRITE failed with error %d\n", NetworkEvents.iErrorCode[FD_WRITE_BIT]); break; }
LPSOCKET_INFORMATION SocketInfo = SocketArray[Event - WSA_WAIT_EVENT_0];
// Read data only if the receive buffer is empty.
if (SocketInfo->BytesRECV == 0) { SocketInfo->DataBuf.buf = SocketInfo->Buffer; SocketInfo->DataBuf.len = DATA_BUFSIZE;
Flags = 0; if (WSARecv(SocketInfo->Socket, &(SocketInfo->DataBuf), 1, &RecvBytes, &Flags, NULL, NULL) == SOCKET_ERROR) { if (WSAGetLastError() != WSAEWOULDBLOCK) { printf("WSARecv() failed with error %d\n", WSAGetLastError()); FreeSocketInformation(Event - WSA_WAIT_EVENT_0); return; } } else { SocketInfo->BytesRECV = RecvBytes; } }
// Write buffer data if it is available.
if (SocketInfo->BytesRECV > SocketInfo->BytesSEND) { SocketInfo->DataBuf.buf = SocketInfo->Buffer + SocketInfo->BytesSEND; SocketInfo->DataBuf.len = SocketInfo->BytesRECV - SocketInfo->BytesSEND;
if (WSASend(SocketInfo->Socket, &(SocketInfo->DataBuf), 1, &SendBytes, 0, NULL, NULL) == SOCKET_ERROR) { if (WSAGetLastError() != WSAEWOULDBLOCK) { printf("WSASend() failed with error %d\n", WSAGetLastError()); FreeSocketInformation(Event - WSA_WAIT_EVENT_0); return; }
// A WSAEWOULDBLOCK error has occured. An FD_WRITE event will be posted // when more buffer space becomes available } else { SocketInfo->BytesSEND += SendBytes;
if (SocketInfo->BytesSEND == SocketInfo->BytesRECV) { SocketInfo->BytesSEND = 0; SocketInfo->BytesRECV = 0; } } } }
if (NetworkEvents.lNetworkEvents & FD_CLOSE) { if (NetworkEvents.iErrorCode[FD_CLOSE_BIT] != 0) { printf("FD_CLOSE failed with error %d\n", NetworkEvents.iErrorCode[FD_CLOSE_BIT]); break; }
printf("Closing socket information %d\n", SocketArray[Event - WSA_WAIT_EVENT_0]->Socket);
FreeSocketInformation(Event - WSA_WAIT_EVENT_0); } } return;}
BOOL CreateSocketInformation(SOCKET s){ LPSOCKET_INFORMATION SI; if ((EventArray[EventTotal] = WSACreateEvent()) == WSA_INVALID_EVENT) { printf("WSACreateEvent() failed with error %d\n", WSAGetLastError()); return FALSE; }
if ((SI = (LPSOCKET_INFORMATION) GlobalAlloc(GPTR, sizeof(SOCKET_INFORMATION))) == NULL) { printf("GlobalAlloc() failed with error %d\n", GetLastError()); return FALSE; }
// Prepare SocketInfo structure for use.
SI->Socket = s; SI->BytesSEND = 0; SI->BytesRECV = 0;
SocketArray[EventTotal] = SI;
EventTotal++;
return(TRUE);}
void FreeSocketInformation(DWORD Event){ LPSOCKET_INFORMATION SI = SocketArray[Event]; DWORD i;
closesocket(SI->Socket);
GlobalFree(SI);
WSACloseEvent(EventArray[Event]);
// Squash the socket and event arrays
for (i = Event; i < EventTotal; i++) { EventArray[i] = EventArray[i + 1]; SocketArray[i] = SocketArray[i + 1]; }
EventTotal--;}
有了源代码,一切都是那么清晰,呵呵。
终于可以进入重叠模型了。。。
《windows网络编程技术》之 Winsock基础
Tag:
前段时间根据客服的反映,老翁的前置机程序存在不工作的情况,初步表现为GPRS登录失败,我查看了报文(强烈要求老板发奖金,有什么问题我总是冲锋在前)发现基本出现在网络频繁断开的情况后(网络每隔10分钟被断开一次,socket错误10053,什么原因还不得而知)。忘了说了,前置机是通过TCP连接到省局的GPRS代理服务器(是由小赖开发的)然后和现场的终端进行通信。前置机程序中是通过delphi的clientsocket进行连接的。一下子还真不知道是什么原因。对于socket这块我绝对不是专家,知其然,不知其所以然。于是我决定先从清理基本概念开始:
鸟瞰TCP/IP体系结构
首先从TCP/IP体系结构开始(这也是不少公司面试时的必备良题啊),相信下图已经表达得非常清除了。
500)this.width=500'>
其次是winsocket与tcp/ip(其实,不止TCP/IP协议族,这里只讨论TCP/IP)
TCP/IP协议核心与应用程序关系图。500)this.width=500'>
最后是常用协议特性:
500)this.width=500'>
关于定址
Winsock中,通过SOCKADDR_IN结构来描述IP地址和服务端口:
struct sockaddr_in
{
short sin_family;
u_short sin_port;
struct in_addr sin_addr;
char sin_zero[8];
};
哦,我只关心IP协议,所以sin_family = AF_INET;
关于端口要注意哦,0-1023为固定服务保留的(别打他们的注意了);1024-49151供普通用户的普通用户进程使用;49152-65535是动态和私有端口。
几个特殊地址:
INADDR_ANY:允许服务器应用监听主机上每个网络接口上的客户机活动;
INADDR_BROADCAST用于在一个IP网络中发送广播UDP数据报。
字节排序:
从主机字节顺序---> 网络字节顺序
返回四字节,用于IP地址
u_long htonl(u_long hostlong)
int WSAHtonl(
SOCKET s,
u_long hostlong,
u_long FAR * lpnetlong
);
返回两字节,用于端口号
u_short htons(u_short hostshort);
int WSAHtons(
SOCKET s,
u_short hostshort,
u_short FAR * lpnetshort
);
对应的反向函数:
u_long ntohl(u_long netong)
int WSANtohl(
SOCKETs,
u_long netong,
u_long FAR * lphostlong
);
u_short htons(u_short netshort);
int WSANtons(
SOCKET s,
u_short netshort,
u_short FAR * lphostshort
);
进入winsocket
下面开始整理winsocket 的一些细节:
所有的winsocket应用其实都是调用winsock dll 中的方法,所以通过WSAstartup加载是第一步。否则就会出错:WSANOTINITIALISED(10093)。
下面先来看看面向连接的协议:
从服务器端来看:
1.bind,将套接字和一个已知的地址进行绑定。
500)this.width=500'>
这样就创建了一个流套接字,这个步骤最常见的错误是WSAEADDRINUSE (10048) ,表示另外一个进程已经和本地IP和端口进行了绑定,或者那个IP地址和端口号处于TIME_WAIT状态。
2.Listen,将套接字置于监听状态。
........................ | |
|
|
|
|