这篇代码的实现过程中参考了:
http://www.cppblog.com/yehao/articles/153729.html
http://fpcfjf.blog.163.com/blog/static/554697932011814102913/
http://blog.csdn.net/wanjingwei/article/details/4306609
服务器端:
//把#include <winsock2.h>放到最前面
//至于原因,那是因为windows.h这个头文件已经包含了winsock.h
//winsock.h和winsock2.h貌似有冲突
#include <Winsock2.h>
#include <stdio.h>
#include <Windows.h>
//#include "winsock.h"
//#pragma comment(lib,"wsock32")
#pragma comment(lib,"Ws2_32")
int main()
{
int accept_num = 0;
int recv_num = 0;
int send_num = 0;
int close_num = 0;
WSADATA wsadata;
WORD version = MAKEWORD(2,2);
int ret = WSAStartup(version,&wsadata);
/*judge the right version of the windows socket*/
if (ret != 0)
{
printf("%s\n","socket create error!");
return 0;
}
if (LOBYTE(wsadata.wVersion)!=2 || HIBYTE(wsadata.wVersion!=2))
{
printf("%s\n","socket version is error!");
WSACleanup();
return 0;
}
WSAEVENT eventArray[WSA_MAXIMUM_WAIT_EVENTS]; /*the array to store the event object*/
SOCKET sockArray[WSA_MAXIMUM_WAIT_EVENTS]; /*the array to store the socket object*/
int nEventTotal = 0; /*the total number of the events*/
USHORT nPort = 6000;
/*create the listen socket and bind to specific port*/
SOCKET sListen = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(nPort);
addr.sin_addr.S_un.S_addr = INADDR_ANY;
ret = bind(sListen,(sockaddr *)&addr,sizeof(addr));
if (SOCKET_ERROR == ret)
{
printf("%s\n","socket bind error!");
WSACleanup();
return 0;
}
ret = listen(sListen,10);
if (SOCKET_ERROR == ret)
{
printf("%s\n","socket listen error!");
WSACleanup();
return 0;
}
//获取发送缓冲区和接受缓冲区的大小
printf("%d\n",SO_MAX_MSG_SIZE);
int recvbuf_len;
int recvlength = sizeof(recvbuf_len);
getsockopt(sListen,SOL_SOCKET,SO_RCVBUF,(char *)&recvbuf_len,&recvlength);
printf("recvbuf is: %d\n",recvbuf_len);
int sendbuf_len;
int sendlength = sizeof(sendbuf_len);
getsockopt(sListen,SOL_SOCKET,SO_SNDBUF,(char *)&sendbuf_len,&sendlength);
printf("sendbuf is: %d\n",sendbuf_len);
/*create a event object and bind it to a socket*/
WSAEVENT Event = WSACreateEvent(); //注意需要手动重置事件或者使用可以重置事件的API操作
//WSAEVENT Event = CreateEvent();
//调用这个函数后,套接字被自动设置为非阻塞模式
WSAEventSelect(sListen,Event,FD_ACCEPT|FD_CLOSE /*|FD_WRITE*/);
//这样做是为了当网络时间发生时,确定是在哪个套接字上发生的。
//之后就在这个套接字上做相应的操作。
eventArray[nEventTotal] = Event;
sockArray[nEventTotal] = sListen;
nEventTotal++;
/*deal with the network events*/
while (true)
{
//在所有事件对象上等待
int nIndex = WSAWaitForMultipleEvents(nEventTotal,eventArray,false,WSA_INFINITE,false);
//发生的事件对象的索引,一般是句柄数组中最前面的那一个,
//然后再用循环依次处理后面的事件对象
nIndex = nIndex - WSA_WAIT_EVENT_0;
//对每个事件调用WSAWaitForMultipleEvents函数,以便确定它的状态
for (int i=nIndex; i<nEventTotal; i++)
{
ret = WSAWaitForMultipleEvents(1,&eventArray[i],true,1000,false);
if (WSA_WAIT_FAILED == ret || WSA_WAIT_TIMEOUT == ret)
{
continue;
}
else
{
//获取到来的通知消息,WSAEnumNetworkEvents函数会自动重置受信事件
WSANETWORKEVENTS netEvent;
WSAEnumNetworkEvents(sockArray[i],eventArray[i],&netEvent);
if(netEvent.lNetworkEvents & FD_ACCEPT) //处理FD_ACCEPT通知消息
{
accept_num++;
printf("the accept number is: %d\n",accept_num);
if(netEvent.iErrorCode[FD_ACCEPT_BIT]==0)
{
if (nEventTotal > WSA_MAXIMUM_WAIT_EVENTS)
{
printf("too many connections \n");
continue;
}
SOCKET sNew = accept(sockArray[i],NULL,NULL);
WSAEVENT Event1 = WSACreateEvent();
WSAEventSelect(sNew,Event1,FD_READ|FD_CLOSE|FD_WRITE);
eventArray[nEventTotal]=Event1;
sockArray[nEventTotal]=sNew;
nEventTotal++;
}
}
else if (netEvent.lNetworkEvents & FD_READ) //处理FD_READ通知消息
{
recv_num++;
printf("the receive number is: %d\n",recv_num);
//注意,WSAEventselect模型是和I/O同步通信模型共同使用的
//这个模型所说的非阻塞只是相对于阻塞模型中send时候无数据
//发送或recv无数据接收等待而言的,具体当执行这两个函数的时候
//和阻塞模型的执行过程是相同的,是I/O同步的。
if(netEvent.iErrorCode[FD_READ_BIT]==0)
{
char szText[8192];
int nRecv = recv(sockArray[i],szText,sizeof(szText),0);
printf("%d\n",nRecv);
if (nRecv > 0)
{
szText[nRecv]='\0';
printf("message is : %s\n",szText);
}
}
}
else if (netEvent.lNetworkEvents & FD_CLOSE) //处理FD_CLOSE通知消息
{
close_num++;
printf("the close number is: %d\n",close_num);
accept_num--;
if (netEvent.iErrorCode[FD_CLOSE_BIT]==0)
{
closesocket(sockArray[i]);
WSACloseEvent(eventArray[i]);
for (int j=i;j<nEventTotal-1;j++)
{
sockArray[j] = sockArray[j+1];
eventArray[j] = eventArray[j+1];
//sockArray[j] = sockArray[j+1];
}
nEventTotal--;
}
}
else if (netEvent.lNetworkEvents & FD_WRITE)
{
int ret_num;
send_num++;
printf("the send number is: %d\n",send_num);
if (netEvent.iErrorCode[FD_WRITE_BIT] == 0)
{
while (true)
{
char msg[47368]="";
ret_num = send(sockArray[i],msg,sizeof(msg),0);
//ret_num += ret_num;
//printf(" %d \n",ret_num);
//这里是为了验证FD_WIRTE触发机制,在这里先填充满套接字缓冲区
//让后在另一端recv,到一定程度后系统会提示可以继续发送,触发
//该事件,具体多大空间可以是TCP协议定义的。
if (ret_num == SOCKET_ERROR)
{
if(WSAGetLastError() == WSAEWOULDBLOCK)
{
printf("the buffer is full \n");
//Sleep(2000);
break;
}
}
}
}
}
///BEGIN///
//这里是为了验证在同一套接字上的绑定的一个event,如果和这个event关联了
//多个网络事件,当这多个网络事件同时发生的时候会不会由于处理了一个网络
//事件之后重置了event导致另一个被屏蔽。这里利用在客户端send数据,在服务
//器端调整处理FD_READ和FD_WRITE的处理顺序实验,说明会屏蔽的。
/*else if (netEvent.lNetworkEvents & FD_READ) //处理FD_READ通知消息
{
recv_num++;
printf("the receive number is: %d\n",recv_num);
if(netEvent.iErrorCode[FD_READ_BIT]==0)
{
char szText[256];
int nRecv = recv(sockArray[i],szText,sizeof(szText),0);
if (nRecv > 0)
{
szText[nRecv]='\0';
printf("message is : %s\n",szText);
}
}
}
END//
*/
}
}
}
return 0;
}
客户端:
#include <iostream>
#include <stdio.h>
#include <string>
#include <Windows.h>
#include "winsock.h"
#pragma comment(lib, "wsock32")
using namespace std;
#define COMMAND 100
int main()
{
int ret = 0;
//初始化socket环境
WSADATA wsadata;
WORD version = MAKEWORD(2,0);
ret = WSAStartup(version,&wsadata);
//创建套接字并连接服务器
SOCKET m_hClientSocket;
m_hClientSocket = socket(AF_INET,SOCK_STREAM,0);
SOCKADDR_IN m_addr1;
m_addr1.sin_family = AF_INET;
m_addr1.sin_addr.S_un.S_addr = inet_addr("192.168.100.57");
m_addr1.sin_port = htons(6000);
//在此可以测试FD_ACCEPT事件,以及第一次FD_WRITE事件触发机制
ret = connect(m_hClientSocket,(LPSOCKADDR)&m_addr1,sizeof(m_addr1));
//测试FD_READ事件触发机制
char msg[1]="";
if (send(m_hClientSocket,msg,sizeof(msg),0)==SOCKET_ERROR)
{
break;
}
//测试FD_WRITE事件触发机制
char Clisend_msg[10000]="";
int recv_len;
recv_len = recv(m_hClientSocket,Clisend_msg,sizeof(Clisend_msg),0);
while (recv_len > 0)
{
cout << recv_len << endl;
recv_len = recv(m_hClientSocket,Clisend_msg,sizeof(Clisend_msg),0);
}
Sleep(10000);
//测试FD_CLOSE事件触发机制
closesocket(m_hClientSocket);
WSACleanup();
return 0;
}