winsock最主要的功能基于socket套接字,现在学得还不是特明白,有个初步的大概认识,通过同步异步IO方式,适用于不同的网络,现在主要提出几个winsock的关键点。
在定义sockaddr_in地址结构时,有个地址家族主要注意下,sin_family这个地址家族可以用两个宏来定义,一个AF_INET和PF_INET意思上af_inet为IP协议,pf_inet为TCP协议,一般socket用pf_inet但其实两个值是一样的,也可以通用。
利用ICOP socket通信建立服务端流程大概为
1、调用WSAStartup(MAKEWORD(2,2),wsadata);初始化,载入winsock的动态链接库,然后是动态链接库的结构信息WSADATA, iMaxSockets为并发的socket的最大数,iMaxUdpDg收发的数据包的最大长度其他的参数无非就是版本信息之类的了。
2、这里我不想去用简单堵塞似recv和select来进行IO操作了,直接用实用性强一点的iocp也就是完成端口的方式进行通信,而且完成端口的IO是用的重叠IO,可以理解为异步的。
所以SOCKET socket=WSASocket(PF_INET,SOCK_STREAM,0,NULL,0,WSA_FLAG_OVERLAPPED);
3、然后初始化地址结构,SOCKADDR_IN,把socket绑定到这个地址上监听,期间把完成端口句柄初始化一下
HANDLE completePort=CreateIoCompletionPort(INVALID_HANDLE_VALUE,NULL,0,0);
后面会把这个完成句柄与socket关联起来
4、创建服务处理线程,把线程和完成端口句柄关联起来,即可以接受和发送了。
CreateThread(NULL,0,ServerWorkerThread,completionPort,0,NULL);
5、然后监听IP端口
listen(socket,num);num为监听的线程数
6、然后是等接收客户端的连入的消息了
SOCKET socketHandle=accpet(socket,(SOCKADDR*)addr,num);接收这个地址的socket客户端信息
7、把socket与完成端口句柄关联起来
在这里要特殊注意一下,客户端与服务器交互时,我们可以互传一个单IO数据指针和一个单句柄数据结构(保存有客户端socket信息)进行通信,所以这里我们声明两个数据结构
typedef struct tagPER_IO_DATA
{
OVERLAPPED Overlapped;
WSABUF DataBuf;
char buffer[IODATASIZE];
DWORD BufferLen;
int OperationType; // 可以作为读写的标志,为简单,我忽略了
}PER_IO_DATA, *LPPER_IO_DATA;单IO操作指针 这个数据结构只要保证首位是个OVERLAPPED类型就可以通过首地址去的结构体的指针地址来进行操作。
typedef struct tagPER_HANDLE_DATA
{
SOCKET Socket;
xxxxxxxxxx扩展变量
// 将和这个句柄关联的其他有用信息,尽管放在这里面吧
}PER_HANDLE_DATA, *LPPER_HANDLE_DATA;单句柄数据
CreateIoCompletionPort((HANDLE)socketHandle,completionPort,(DWORD)LPPER_HANDLE_DATA,NULL);LPPER_HANDLE_DATA为一个完成端口虚拟键,用来和套接字关联的单句柄数据信息结构,用来获得接受客户端socket的信息的结构。
8、设立接收的接口
WSARecv(socket,&PER_IO_DATA.DataBuf,num,recvbytenum,,&PER_IO_DATA.Overlapped);WSABUF为接收缓冲区结构,num为结构数量,recvbytenum为接收的字节数,overlapped为接受客户端的重叠信息结构。
9、建立的工作者线程serverworkthread
循环里while(true)
通过GetQueuedCompletionStatus(completionPort,&bytenum,(LPDWORD)&PER_HANDLE_DATA.socket,(LPOVERLAPPED*)&PER_IO_DATA,INFINITE)取得完成端口的结果
这里最好通过线程互斥锁来锁定线程,先createMutex一个互斥锁,第二个参数为false为创建的句柄为有信号的,然后用WaitForSingleObject()检查互斥锁的信号,然后用ReleaseMutex()释放占有权不然其它线程中的WaitForSingleObject()将阻塞。
返回成功之后取得IO数据然后重置IO
ZeroMemory清空PER_IO_DATA数据
再赋值PER_IO_DATA.databuf.buf=PER_IO_DATA.buffer
PER_IO_DATA.databuf.len=SIZE
然后再继续接收WSARecv();