int MightyTCPEventSelectServer::acceptSocketObject() { while(TRUE) { int nRet = ::WaitForSingleObject(m_ListenEvent,5*1000); if(nRet == WAIT_FAILED) { return -1; } else if(nRet == WSA_WAIT_TIMEOUT) // 定时显式状态信息 { continue; } else // 有新的连接未决 { ::ResetEvent(m_ListenEvent); // 循环处理所有未决的连接请求 while(TRUE) { sockaddr_in si; int nLen = sizeof(si); SOCKET sNew = ::accept(m_ListenSock, (sockaddr*)&si, &nLen); if(sNew == SOCKET_ERROR) break; PSOCKET_OBJ pSocket = GetSocketObj(sNew); pSocket->addrRemote = si; ::WSAEventSelect(pSocket->s, pSocket->event, FD_READ|FD_CLOSE|FD_WRITE); AssignToFreeThread(pSocket); } } } }
int main() { USHORT nPort = 4567; // 此服务器监听的端口号 // 创建监听套节字 SOCKET sListen = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); sockaddr_in sin; sin.sin_family = AF_INET; sin.sin_port = htons(nPort); sin.sin_addr.S_un.S_addr = INADDR_ANY; if(::bind(sListen, (sockaddr*)&sin, sizeof(sin)) == SOCKET_ERROR) { printf(" Failed bind() \n"); return -1; } ::listen(sListen, 200); // 创建事件对象,并关联到监听的套节字 WSAEVENT event = ::WSACreateEvent(); ::WSAEventSelect(sListen, event, FD_ACCEPT|FD_CLOSE); ::InitializeCriticalSection(&g_cs); // 处理客户连接请求,打印状态信息 while(TRUE) { int nRet = ::WaitForSingleObject(event, 5*1000); if(nRet == WAIT_FAILED) { printf(" Failed WaitForSingleObject() \n"); break; } else if(nRet == WSA_WAIT_TIMEOUT) // 定时显式状态信息 { printf(" \n"); printf(" TatolConnections: %d \n", g_nTatolConnections); printf(" CurrentConnections: %d \n", g_nCurrentConnections); continue; } else // 有新的连接未决 { ::ResetEvent(event); // 循环处理所有未决的连接请求 while(TRUE) { sockaddr_in si; int nLen = sizeof(si); SOCKET sNew = ::accept(sListen, (sockaddr*)&si, &nLen); if(sNew == SOCKET_ERROR) break; PSOCKET_OBJ pSocket = GetSocketObj(sNew); pSocket->addrRemote = si; ::WSAEventSelect(pSocket->s, pSocket->event, FD_READ|FD_CLOSE|FD_WRITE); AssignToFreeThread(pSocket); } } } ::DeleteCriticalSection(&g_cs); return 0; }
void MightyTCPCompletionPortServer::HandleIO(DWORD dwKey, PCompletionPort_BufObj pBuffer, DWORD dwTrans, int nError) { PCompletionPortIOObject pContext = (PCompletionPortIOObject)dwKey; //首先减少套节字上的未决I/O计数 if(pContext != NULL) { if(pBuffer->nOperation == OP_READ) pContext->nOutstandingRecv --; else if(pBuffer->nOperation == OP_WRITE) pContext->nOutstandingSend --; // 检查套节字是否已经被我们关闭 if(pContext->nCondition) { if(pContext->nOutstandingRecv == 0 && pContext->nOutstandingSend == 0) { RemoveSocketObj(pContext); } // 释放已关闭套节字的未决I/O ReleaseBuffer(pBuffer); return; } } else { RemovePendingAccept(pBuffer); } //检查套节字上发生的错误,如果有的话,通知用户,然后关闭套节字 if(nError != NO_ERROR) { if(pBuffer->nOperation != OP_ACCEPT) { //if(nError==ERROR_NETNAME_DELETED) RemoveSocketObj(pContext); if(pContext->nOutstandingRecv == 0 && pContext->nOutstandingSend == 0) { //ReleaseContext(pContext); } } else // 在监听套节字上发生错误,也就是监听套节字处理的客户出错了 { // 客户端出错,释放I/O缓冲区 if(pBuffer->sAccept!= INVALID_SOCKET) { ::closesocket(pBuffer->sAccept); pBuffer->sAccept= INVALID_SOCKET; } } ReleaseBuffer(pBuffer); return; } // 开始处理 if(pBuffer->nOperation == OP_ACCEPT) { if(dwTrans == 0) { if(pBuffer->sAccept!= INVALID_SOCKET) { ::closesocket(pBuffer->sAccept); pBuffer->sAccept= INVALID_SOCKET; } } else { // 为新接受的连接申请客户上下文对象 PCompletionPortIOObject pClient =GetSocketObj(pBuffer->sAccept); if(pClient != NULL) { if(InsertSocketObj(pClient)==0) { // 取得客户地址 int nLocalLen, nRmoteLen; LPSOCKADDR pLocalAddr, pRemoteAddr; m_lpfnGetAcceptExSockaddrs( pBuffer->buff, pBuffer->nLen - ((sizeof(sockaddr_in) + 16) * 2), sizeof(sockaddr_in) + 16, sizeof(sockaddr_in) + 16, (SOCKADDR **)&pLocalAddr, &nLocalLen, (SOCKADDR **)&pRemoteAddr, &nRmoteLen); memcpy(&pClient->addrLocal, pLocalAddr, nLocalLen); memcpy(&pClient->addrRemote, pRemoteAddr, nRmoteLen); // 关联新连接到完成端口对象 ::CreateIoCompletionPort((HANDLE)pClient->s, m_hCompletionPort, (DWORD)pClient, 0); // 通知用户 pBuffer->nLen = dwTrans; m_pNotifyCallBack->OnRecvData(pBuffer->buff,dwTrans,pClient->identityIDKey); // 向新连接投递几个Read请求,这些空间在套节字关闭或出错时释放 for(int i=0; i<5; i++) { PCompletionPort_BufObj p = AllocateBuffer(pClient,2048); if(p != NULL) { if(!PostRecv(p)) { RemoveSocketObj(pClient); break; } } } } else // 连接数量已满,关闭连接 { RemoveSocketObj(pClient); } } else { // 资源不足,关闭与客户的连接即可 ::closesocket(pBuffer->sAccept); pBuffer->sAccept= INVALID_SOCKET; } } // Accept请求完成,释放I/O缓冲区 ReleaseBuffer(pBuffer); // 通知监听线程继续再投递一个Accept请求 ::InterlockedIncrement(&m_nRepostCount); ::SetEvent(m_hRepostEvent); } else if(pBuffer->nOperation == OP_READ) { if(dwTrans == 0) // 对方关闭套节字 { // 先通知用户 pBuffer->nLen = 0; RemoveSocketObj(pContext); // 释放客户上下文和缓冲区对象 if(pContext->nOutstandingRecv == 0 && pContext->nOutstandingSend == 0) { //ReleaseContext(pContext); } //ReleaseBuffer(pBuffer); } else { pBuffer->nLen = dwTrans; // 按照I/O投递的顺序读取接收到的数据 PCompletionPort_BufObj pNextIO = GetNextReadBuffer(pContext, pBuffer); while(pNextIO != NULL) { m_pNotifyCallBack->OnRecvData(pNextIO->buff,dwTrans,pContext->identityIDKey); // 增加要读的序列号的值 ::InterlockedIncrement((LONG*)&pContext->nCurrentReadSequence); // 释放这个已完成的I/O ReleaseBuffer(pNextIO); pNextIO= GetNextReadBuffer(pContext, NULL); } // 继续投递一个新的接收请求 pBuffer = AllocateBuffer(pContext,4096); if(pBuffer == NULL || !PostRecv(pBuffer)) { //closeAConnection(pContext); } } } else if(pBuffer->nOperation == OP_WRITE) { if(dwTrans == 0) // 对方关闭套节字 { // 先通知用户 pBuffer->nLen = 0; RemoveSocketObj(pContext); // 再关闭连接 //CloseAConnection(pContext); // 释放客户上下文和缓冲区对象 if(pContext->nOutstandingRecv == 0 && pContext->nOutstandingSend == 0) { //ReleaseContext(pContext); } //ReleaseBuffer(pBuffer); } else { // 写操作完成,通知用户 pBuffer->nLen = dwTrans; //OnWriteCompleted(pContext, pBuffer); // 释放SendText函数申请的缓冲区 ReleaseBuffer(pBuffer); } } }
bool MightyTCPCompletionPortServer::Create(const unsigned short & usPort) { /*if (!(P2PUtilTools::is2KOS())) { return false; }*/ m_ListenPort=usPort; m_ListenSock = ::WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, WSA_FLAG_OVERLAPPED); SOCKADDR_IN si; si.sin_family = AF_INET; si.sin_port = ::ntohs(usPort); si.sin_addr.S_un.S_addr = INADDR_ANY; if(::bind(m_ListenSock, (sockaddr*)&si, sizeof(si)) == SOCKET_ERROR) { return false; } ::listen(m_ListenSock, 200); m_pListenObj= GetSocketObj(m_ListenSock); // 创建完成端口对象 m_hCompletionPort = ::CreateIoCompletionPort(INVALID_HANDLE_VALUE, 0, 0, 0); // 加载扩展函数AcceptEx GUID GuidAcceptEx = WSAID_ACCEPTEX; DWORD dwBytes; ::WSAIoctl(m_ListenSock, SIO_GET_EXTENSION_FUNCTION_POINTER, &GuidAcceptEx, sizeof(GuidAcceptEx), &m_lpfnAcceptEx, sizeof(m_lpfnAcceptEx), &dwBytes, NULL, NULL); // 加载扩展函数GetAcceptExSockaddrs GUID GuidGetAcceptExSockaddrs = WSAID_GETACCEPTEXSOCKADDRS; ::WSAIoctl(m_ListenSock, SIO_GET_EXTENSION_FUNCTION_POINTER, &GuidGetAcceptExSockaddrs, sizeof(GuidGetAcceptExSockaddrs), &m_lpfnGetAcceptExSockaddrs, sizeof(m_lpfnGetAcceptExSockaddrs), &dwBytes, NULL, NULL ); // 将监听套节字关联到完成端口,注意,这里为它传递的CompletionKey为0 ::CreateIoCompletionPort((HANDLE)m_ListenSock, m_hCompletionPort, (DWORD)0, 0); // 注册FD_ACCEPT事件。 // 如果投递的AcceptEx I/O不够,线程会接收到FD_ACCEPT网络事件,说明应该投递更多的AcceptEx I/O WSAEventSelect(m_ListenSock, m_hAcceptEvent, FD_ACCEPT); startWork(); return 0; }
//--------------------------------------------------------------------------------------------------------------------------- //CLConnect() //NULL - connect failed . //--------------------------------------------------------------------------------------------------------------------------- SOCKET_OBJ * CIocpServer::CLConnect(const char * ip,const char *port,int flag) { SOCKET_OBJ *sockobj = NULL; BUFFER_OBJ *recvobj = NULL; struct addrinfo *res=NULL; bool bFlag = false; res = ResolveAddress(ip, port, AF_INET,SOCK_STREAM, IPPROTO_TCP); if (res == NULL) { fprintf(stderr, "CLConnect ResolveAddress failed to return any addresses!\n"); return NULL; } sockobj = GetSocketObj(INVALID_SOCKET); // create the socket sockobj->s = socket(res->ai_family, res->ai_socktype, res->ai_protocol); if (sockobj->s == INVALID_SOCKET) { fprintf(stderr,"CLConnect socket failed: %d\n", WSAGetLastError()); FreeSocketObj(sockobj); return NULL; } #ifdef ENABLE_UNLIMIT_PORT SOCKADDR_IN local_addr={0}; local_addr.sin_family = AF_INET; local_addr.sin_addr.s_addr = ADDR_ANY; BOOL optvar = true; for(int i=0;i<40;i++) { local_addr.sin_port = htons(m_UnlimitPort); setsockopt(sockobj->s,SOL_SOCKET,SO_REUSEADDR,(char*)&optvar,sizeof(optvar)); if(bind(sockobj->s,(SOCKADDR *)&local_addr,sizeof(SOCKADDR)) == -1) { if(m_UnlimitPort < UNLIMIT_PORT_END) m_UnlimitPort++; else m_UnlimitPort = UNLIMIT_PORT_START; if(WSAGetLastError() != WSAEADDRINUSE ) { //bind出错了 fprintf(stderr,"bind() failed: %d\n", WSAGetLastError()); closesocket(sockobj->s); sockobj->s = INVALID_SOCKET; FreeSocketObj(sockobj); return NULL; } } else break; } if(m_UnlimitPort < UNLIMIT_PORT_END) m_UnlimitPort++; else m_UnlimitPort = UNLIMIT_PORT_START; #endif if( connect(sockobj->s,res->ai_addr,(int)res->ai_addrlen)!=SOCKET_ERROR ) { if( CreateIoCompletionPort((HANDLE)sockobj->s, CLCompletionPort, (ULONG_PTR)sockobj, 0) == NULL) fprintf(stderr, "CLConnect : CLCreateIoCompletionPort failed: %d\n", GetLastError()); else bFlag = true; } #ifdef LOG_LEVEL2 if(!bFlag) Log_Server.Write("CLConnect失败: %d\n",WSAGetLastError()); #endif if(bFlag) { recvobj = GetBufferObj(); #ifdef DEBUG_IOCP recvobj->sclient = sockobj->s; #endif LOCK(&sockobj->cs); if (PostRecv(sockobj, recvobj) != NO_ERROR) { UNLOCK(&sockobj->cs); FreeBufferObj(recvobj); } else { sockobj->flag = flag; sockobj->flag_accept = 1; sockobj->flag_close = 1; sockobj->recvobj = recvobj; #ifdef ENABLE_KEEPALIVE AddAlive(sockobj,GetTickCount()); #endif #ifdef DEBUG_IOCP sockobj->freeed = 0; sockobj->onclosed = 0; #endif UNLOCK(&sockobj->cs); CLOnConnect(sockobj,sockobj->flag); return sockobj; } } closesocket(sockobj->s); sockobj->s = INVALID_SOCKET; FreeSocketObj(sockobj); return NULL; }
//--------------------------------------------------------------------------------------------------------------------------- //StartServer() //start listen //--------------------------------------------------------------------------------------------------------------------------- int CIocpServer::StartServer(const char *local_ip,const char *local_port) { if(!m_Inited) { fprintf(stderr, "Run Init() first !\n"); return -1; } SOCKET_OBJ *sockobj=NULL; HANDLE hrc; int endpointcount=0, rc, i; HANDLE event_accept; struct addrinfo *res=NULL, *ptr=NULL; #ifdef LOG_LEVEL2 printf("LOG_LEVEL2"); #else // #ifdef LOG_LEVEL1 printf("LOG_LEVEL1"); #else printf("LOG_NONE"); #endif // #endif printf("\n\n连接线程:%d: 监听线程:%d; 平均连接数:%d ; ", m_CLThreads,m_Threads,AVERAGE_CONNECTIONS); printf("IOCP版本: %s;\n",IOCP_VERSION); #ifdef ENABLE_KEEPALIVE printf("心跳检测:开启; "); #else printf("心跳检测:关闭; "); #endif printf("心跳超时:%d; 关闭延时:%d\n",KEEPALIVE_TIME,CLOSE_DELAY); res = ResolveAddress(local_ip, local_port, AF_INET, SOCK_STREAM, IPPROTO_TCP); if (res == NULL) { fprintf(stderr, "ResolveAddress failed to return any addresses!\n"); return -1; } ptr = res; if (ptr) { sockobj = GetSocketObj(INVALID_SOCKET); // create the socket sockobj->s = socket(ptr->ai_family, ptr->ai_socktype, ptr->ai_protocol); if (sockobj->s == INVALID_SOCKET) { fprintf(stderr,"socket failed: %d\n", WSAGetLastError()); return -1; } // Associate the socket and its SOCKET_OBJ to the completion port hrc = CreateIoCompletionPort((HANDLE)sockobj->s, CompletionPort, (ULONG_PTR)sockobj, 0); if (hrc == NULL) { fprintf(stderr, "CreateIoCompletionPort failed: %d\n", GetLastError()); return -1; } // bind the socket to a local address and port rc = bind(sockobj->s, ptr->ai_addr, (int)ptr->ai_addrlen); if (rc == SOCKET_ERROR) { fprintf(stderr, "bind failed: %d\n", WSAGetLastError()); return -1; } BUFFER_OBJ *acceptobj=NULL; GUID guidAcceptEx = WSAID_ACCEPTEX; GUID guidGetAcceptExSockaddrs = WSAID_GETACCEPTEXSOCKADDRS; DWORD bytes; // Need to load the Winsock extension functions from each provider // -- e.g. AF_INET and AF_INET6. rc = WSAIoctl( sockobj->s, SIO_GET_EXTENSION_FUNCTION_POINTER, &guidAcceptEx, sizeof(guidAcceptEx), &lpfnAcceptEx, sizeof(lpfnAcceptEx), &bytes, NULL, NULL ); if (rc == SOCKET_ERROR) { fprintf(stderr, "WSAIoctl: SIO_GET_EXTENSION_FUNCTION_POINTER failed: %d\n", WSAGetLastError()); return -1; } rc = WSAIoctl( sockobj->s, SIO_GET_EXTENSION_FUNCTION_POINTER, &guidGetAcceptExSockaddrs, sizeof(guidGetAcceptExSockaddrs), &lpfnGetAcceptExSockaddrs, sizeof(lpfnGetAcceptExSockaddrs), &bytes, NULL, NULL ); if (rc == SOCKET_ERROR) { fprintf(stderr, "WSAIoctl: SIO_GET_EXTENSION_FUNCTION_POINTER faled: %d\n", WSAGetLastError()); return -1; } // For TCP sockets, we need to "listen" on them rc = _(sockobj->s, 32); if (rc == SOCKET_ERROR) { fprintf(stderr, "listen failed: %d\n", WSAGetLastError()); return -1; } event_accept = CreateEvent(NULL, TRUE, FALSE, NULL); if(event_accept == NULL) { fprintf(stderr,"event_accept CreateEvent failed: %d\n",GetLastError()); return -1; } rc = WSAEventSelect(sockobj->s,event_accept,FD_ACCEPT); if (rc == SOCKET_ERROR) { fprintf(stderr, "WSAEventSelect failed: %d\n", WSAGetLastError()); return -1; } // Post the AcceptEx(s) for(i=0; i < DEFAULT_ACCEPTEX_COUNT ;i++) { acceptobj = GetBufferObj(); sockobj->recvobj = acceptobj; if(PostAccept(sockobj, acceptobj) != 0) { FreeBufferObj(acceptobj); fprintf(stderr, "PostAccept failed: %d\n", WSAGetLastError()); return -1; } } endpointcount++; ptr = ptr->ai_next; } // free the addrinfo structure for the 'bind' address freeaddrinfo(res); DWORD sleeptime = CLOSE_DELAY / 2; //每次sleep的时间 #ifdef LOG_STATUS long sock=0; long buf=0; #endif if(sleeptime < 50) //限制在50ms以外 sleeptime = 50; DWORD time_ontimer=GetTickCount(); DWORD time_close = time_ontimer; DWORD time_alive = time_ontimer; DWORD newtime; BUFFER_OBJ *acceptobj; printf("\n>>> 服务器地址: %s: 端口号: %s <<<\n\n", local_ip, local_port); while (1) { rc = WaitForSingleObject(event_accept,sleeptime); if(rc == WAIT_FAILED) { fprintf(stderr,"WaitForSingleObject Failed:%d\n",GetLastError()); break; } else if(rc != WAIT_TIMEOUT) //GOT FD_ACCEPT { // acceptobj = GetBufferObj(); // PostAccept(sockobj,acceptobj); if(m_accept_count < DEFAULT_ACCEPTEX_COUNT /2) { for(int i=0;i<DEFAULT_ACCEPTEX_COUNT/2;i++) { acceptobj = GetBufferObj(); PostAccept(sockobj,acceptobj); } } ResetEvent(event_accept); } newtime = GetTickCount(); if(newtime - time_ontimer > 3000) // 3秒 { OnTimer(); //call virtual timer(); time_ontimer = newtime; #ifdef LOG_STATUS //显示服务器状态 if(sock != gTotalSock || buf != gTotalBuf) { Log_Server.Write("服务器状态 : sock : %d ; buf : %d ; IoCount : %d ;",gTotalSock,gTotalBuf,gIoCount); sock = gTotalSock; buf = gTotalBuf; } #endif } //Check Close if(newtime - time_close > sleeptime) { time_close = newtime; CheckClose(); } if(newtime - time_alive > KEEPALIVE_TIME / 3) { CheckAlive(); time_alive = newtime; } } WSACleanup(); return 0; }
//--------------------------------------------------------------------------------------------------------------------------- //HanelIO() //--------------------------------------------------------------------------------------------------------------------------- void CIocpServer::HandleIo(SOCKET_OBJ *sock, BUFFER_OBJ *buf, HANDLE CompPort, DWORD BytesTransfered, DWORD error, ADOConn *pAdo,bool isClient) { #ifdef LOG_STATUS InterlockedDecrement(&gIoCount); #endif switch(buf->operation) { case OP_ACCEPT: //处理AcceptEx的返回 LOCK(&sock->cs);//加锁 { if(error == 0) { SOCKET_OBJ *clientobj = GetSocketObj(buf->sclient);//创建一个客户端sockobj HANDLE hrc = CreateIoCompletionPort( //将sockobj添加到完成端口处理列表 (HANDLE)buf->sclient, CompPort, (ULONG_PTR)clientobj, 0 ); if (hrc != NULL) { //读取ip地址 SOCKADDR *local=NULL; SOCKADDR *remote=NULL; int local_len; int remote_len; lpfnGetAcceptExSockaddrs(buf->buf,0,sizeof(SOCKADDR_STORAGE) + 16 ,sizeof(SOCKADDR_STORAGE) + 16, &local,&local_len,&remote,&remote_len); clientobj->addr = ((SOCKADDR_IN *)remote)->sin_addr; #ifdef LOG_LEVEL1 Log_Server.Write("--新连接 ip = %s , sock = %d ",GetAddress(clientobj), clientobj->s); #endif BUFFER_OBJ *recvobj = GetBufferObj(); //禁用发送缓冲 #ifdef SEND_BUF_DISABLE BOOL sndbuf=0; setsockopt(buf->sclient,SOL_SOCKET,SO_SNDBUF,(char *)&sndbuf,sizeof(sndbuf)); #endif LOCK(&clientobj->cs); if(PostRecv(clientobj,recvobj) == 0) { #ifdef DEBUG_IOCP clientobj->sockfd = clientobj->s; recvobj->sclient = clientobj->s; #endif clientobj->recvobj = recvobj; AddAlive(clientobj,GetTickCount()); //把他添加到心跳列表,如果一个客户端连上来以后不发数据,会被踢掉 } else { FreeBufferObj(recvobj); closesocket(clientobj->s); //注意关闭句柄,clientobj->s的句柄在PostAccept时候创建 clientobj->s = INVALID_SOCKET; FreeSocketObj(clientobj); } UNLOCK(&clientobj->cs); } else { //创建完成端口失败,像new失败一样,基本上不会出现 #ifdef LOG_LEVEL2 Log_Server.Write("CreateIoCompletionPort 错误:%d",GetLastError()); #endif closesocket(clientobj->s); clientobj->s = INVALID_SOCKET; FreeSocketObj(clientobj); } } else //如果AcceptEx返回出错,说明有一个客户端连了一半就退了,需要把他的句柄关了. closesocket(buf->sclient); InterlockedDecrement(&m_accept_count); // 一般情况下重新PostAccept if(m_accept_count < DEFAULT_ACCEPTEX_COUNT *2) { if(PostAccept(sock,buf)!=0) FreeBufferObj(buf); } } UNLOCK(&sock->cs); break; case OP_READ: //收到数据了 { LOCK(&sock->cs); //锁一下 bool bflag = false; _ASSERTE(buf == sock->recvobj); if(error == 0 && BytesTransfered > 0 ) { #ifdef LOG_LEVEL1 char head[256]; sprintf(head,"(%d) :接收",sock->s); if(isClient) strcat(head,"CL"); Log_Server.WriteHex(buf->buf+buf->buflen,BytesTransfered,head,(int)strlen(head)); #endif buf->buflen += BytesTransfered; int nret = 0; if(isClient) { //调用可以重载的虚函数 //开锁,否则在On系列函数中调用Send(),CloseSock()时候会死锁! UNLOCK(&sock->cs); nret = CLOnRead(sock,buf->buf,buf->buflen,sock->flag,pAdo); LOCK(&sock->cs); } else if(sock->flag_accept == 1) { UNLOCK(&sock->cs); nret = OnRead(sock,buf->buf,buf->buflen,pAdo); LOCK(&sock->cs); } else { sock->flag_close = 1; //设置允许标志,否则调用Send将会失败 sock->flag_accept = 1; //设置accept标志 #ifndef ENABLE_KEEPALIVE //没有启用KeepAlive DeleteAlive(sock); #endif #ifdef DEBUG_IOCP sock->freeed = 0; sock->onclosed = 0; #endif UNLOCK(&sock->cs); nret = OnAccept(sock,buf->buf,buf->buflen,pAdo); LOCK(&sock->cs); } _ASSERTE(nret >= 0 && nret <= buf->buflen); if(nret < 0 || nret > buf->buflen) { #ifdef LOG_LEVEL2 Log_Server.Write("指令处理出错啦\n"); nret = buf->buflen; //强制设置为完全处理 #endif } #ifdef ENABLE_KEEPALIVE sock->timeAlive = GetTickCount(); //设置新的心跳时间 #endif buf->buflen -= nret; if(nret > 0 && buf->buflen > 0) memmove(buf->buf,buf->buf+nret,buf->buflen); if(PostRecv(sock,buf) == 0) //重新递交一个接收操作 bflag = true; } if(!bflag) { DeleteClose(sock); //看看是否存在于延时关闭列表中 DeleteAlive(sock); //看看是否存在于心跳列表中 if(sock->s != INVALID_SOCKET) { closesocket(sock->s); sock->s = INVALID_SOCKET; } sock->flag_close = 0; //设置关闭标志 FreeBufferObj(buf); if( sock->flag_accept == 1) { UNLOCK(&sock->cs); if(!isClient) OnClose(sock,pAdo); //调用一次OnClose(),告诉上层的程序,不要再去用这个sock了 else CLOnClose(sock,sock->flag,pAdo); LOCK(&sock->cs); #ifdef DEBUG_IOCP sock->onclosed = 1; //调试时设置关闭标志,用于检测是否存在逻辑问题 #endif } sock->recvobj = NULL; if(sock->recvobj == NULL && sock->sendobj == NULL) { UNLOCK(&sock->cs); FreeSocketObj(sock); //释放该客户端对应的sockobj对象 return; } } UNLOCK(&sock->cs); } break; case OP_WRITE: LOCK(&sock->cs); { _ASSERTE(buf == sock->sendobj); bool bflag = false; BUFFER_OBJ *tmpobj = sock->sendobj; sock->sendobj = sock->sendobj->next; if(error == 0 && BytesTransfered > 0) //前一个发送已经完成 { #ifdef LOG_LEVEL1 char head[256]; sprintf(head,"(%d) :发送",sock->s); if(isClient) strcat(head,"CL"); Log_Server.WriteHex(buf->buf,BytesTransfered,head,(int)strlen(head)); #endif //检查发送队列 if(sock->sendobj == NULL) bflag = true; else if(PostSend(sock,sock->sendobj) == 0) bflag = true; } FreeBufferObj(tmpobj); if(!bflag) { sock->flag_close = 0; //设置关闭标志 while(sock->sendobj) { tmpobj = sock->sendobj; sock->sendobj = sock->sendobj->next; FreeBufferObj(tmpobj); } if(sock->recvobj == NULL && sock->sendobj == NULL) { UNLOCK(&sock->cs); FreeSocketObj(sock); //如果OP_READ时,sock->sendobj!=NULL,那么需要在这里释放客户端的SocketOBJ return; } } } UNLOCK(&sock->cs); break; } }
// // Function: main // // Description: // This is the main program. It parses the command line and creates // the main socket. For UDP this socket is used to receive datagrams. // For TCP the socket is used to accept incoming client connections. // Each client TCP connection is handed off to a worker thread which // will receive any data on that connection until the connection is // closed. // int __cdecl main(int argc, char **argv) { WSADATA wsd; THREAD_OBJ *thread=NULL; SOCKET_OBJ *sockobj=NULL, *newsock=NULL; int index, rc; struct addrinfo *res=NULL, *ptr=NULL; // Validate the command line ValidateArgs(argc, argv); // Load Winsock if (WSAStartup(MAKEWORD(2,2), &wsd) != 0) { fprintf(stderr, "unable to load Winsock!\n"); return -1; } printf("Local address: %s; Port: %s; Family: %d\n", gBindAddr, gBindPort, gAddressFamily); res = ResolveAddress(gBindAddr, gBindPort, gAddressFamily, gSocketType, gProtocol); if (res == NULL) { fprintf(stderr, "ResolveAddress failed to return any addresses!\n"); return -1; } thread = GetThreadObj(); // For each local address returned, create a listening/receiving socket ptr = res; while (ptr) { PrintAddress(ptr->ai_addr, ptr->ai_addrlen); printf("\n"); sockobj = GetSocketObj(INVALID_SOCKET, (gProtocol == IPPROTO_TCP) ? TRUE : FALSE); // create the socket sockobj->s = socket(ptr->ai_family, ptr->ai_socktype, ptr->ai_protocol); if (sockobj->s == INVALID_SOCKET) { fprintf(stderr,"socket failed: %d\n", WSAGetLastError()); return -1; } InsertSocketObj(thread, sockobj); // bind the socket to a local address and port rc = bind(sockobj->s, ptr->ai_addr, ptr->ai_addrlen); if (rc == SOCKET_ERROR) { fprintf(stderr, "bind failed: %d\n", WSAGetLastError()); return -1; } if (gProtocol == IPPROTO_TCP) { rc = listen(sockobj->s, 200); if (rc == SOCKET_ERROR) { fprintf(stderr, "listen failed: %d\n", WSAGetLastError()); return -1; } // Register events on the socket rc = WSAEventSelect( sockobj->s, sockobj->event, FD_ACCEPT | FD_CLOSE ); if (rc == SOCKET_ERROR) { fprintf(stderr, "WSAEventSelect failed: %d\n", WSAGetLastError()); return -1; } } else { // Register events on the socket rc = WSAEventSelect( sockobj->s, sockobj->event, FD_READ | FD_WRITE | FD_CLOSE ); if (rc == SOCKET_ERROR) { fprintf(stderr, "WSAEventSelect failed: %d\n", WSAGetLastError()); return -1; } } ptr = ptr->ai_next; } // free the addrinfo structure for the 'bind' address freeaddrinfo(res); gStartTime = gStartTimeLast = GetTickCount(); while (1) { rc = WaitForMultipleObjects( thread->SocketCount + 1, thread->Handles, FALSE, 5000 ); if (rc == WAIT_FAILED) { fprintf(stderr, "WaitForMultipleObjects failed: %d\n", GetLastError()); break; } else if (rc == WAIT_TIMEOUT) { PrintStatistics(); } else { index = rc - WAIT_OBJECT_0; sockobj = FindSocketObj(thread, index-1); if (gProtocol == IPPROTO_TCP) { SOCKADDR_STORAGE sa; WSANETWORKEVENTS ne; SOCKET sc; int salen; rc = WSAEnumNetworkEvents( sockobj->s, thread->Handles[index], &ne ); if (rc == SOCKET_ERROR) { fprintf(stderr, "WSAEnumNetworkEvents failed: %d\n", WSAGetLastError()); break; } while (1) { sc = INVALID_SOCKET; salen = sizeof(sa); // // For TCP, accept the connection and hand off the client socket // to a worker thread // sc = accept( sockobj->s, (SOCKADDR *)&sa, &salen ); if ((sc == INVALID_SOCKET) && (WSAGetLastError() != WSAEWOULDBLOCK)) { fprintf(stderr, "accept failed: %d\n", WSAGetLastError()); break; } else if (sc != INVALID_SOCKET) { newsock = GetSocketObj(INVALID_SOCKET, FALSE); // Copy address information memcpy(&newsock->addr, &sa, salen); newsock->addrlen = salen; newsock->s = sc; InterlockedIncrement(&gTotalConnections); InterlockedIncrement(&gCurrentConnections); /* printf("Accepted connection from: "); PrintAddress((SOCKADDR *)&newsock->addr, newsock->addrlen); printf("\n"); */ // Register for read, write and close on the client socket rc = WSAEventSelect( newsock->s, newsock->event, FD_READ | FD_WRITE | FD_CLOSE ); if (rc == SOCKET_ERROR) { fprintf(stderr, "WSAEventSelect failed: %d\n", WSAGetLastError()); break; } AssignToFreeThread(newsock); } else { // Failed with WSAEWOULDBLOCK -- just continue break; } } } else { // For UDP all we have to do is handle events on the main // threads. if (HandleIo(thread, sockobj) == SOCKET_ERROR) { RenumberThreadArray(thread); } } } } WSACleanup(); return 0; }
// // Function: main // // Description: // This is the main program. It parses the command line and creates // the main socket. For UDP this socket is used to receive datagrams. // For TCP the socket is used to accept incoming client connections. // Each client TCP connection is handed off to a worker thread which // will receive any data on that connection until the connection is // closed. // int __cdecl main(int argc, char **argv) { WSADATA wsd; SOCKET_OBJ *sockobj=NULL, *sptr=NULL, *tmp=NULL; HANDLE hThread; ULONG lastprint=0; MSG msg; int rc; struct addrinfo *res=NULL, *ptr=NULL; ValidateArgs(argc, argv); InitializeCriticalSection(&gSocketCritSec); // Load winsock if (WSAStartup(MAKEWORD(2,2), &wsd) != 0) { fprintf(stderr, "unable to load Winsock!\n"); return -1; } gWorkerWindow = MakeWorkerWindow(); printf("Local address: %s; Port: %s; Family: %d\n", gBindAddr, gBindPort, gAddressFamily); res = ResolveAddress(gBindAddr, gBindPort, gAddressFamily, gSocketType, gProtocol); if (res == NULL) { fprintf(stderr, "ResolveAddress failed to return any addresses!\n"); return -1; } // For each local address returned, create a listening/receiving socket ptr = res; while (ptr) { PrintAddress(ptr->ai_addr, ptr->ai_addrlen); printf("\n"); sockobj = GetSocketObj(INVALID_SOCKET); // create the socket sockobj->s = socket(ptr->ai_family, ptr->ai_socktype, ptr->ai_protocol); if (sockobj->s == INVALID_SOCKET) { fprintf(stderr,"socket failed: %d\n", WSAGetLastError()); return -1; } InsertSocketObj(sockobj); // bind the socket to a local address and port rc = bind(sockobj->s, ptr->ai_addr, ptr->ai_addrlen); if (rc == SOCKET_ERROR) { fprintf(stderr, "bind failed: %d\n", WSAGetLastError()); return -1; } if (gProtocol == IPPROTO_TCP) { rc = listen(sockobj->s, 200); if (rc == SOCKET_ERROR) { fprintf(stderr, "listen failed: %d\n", WSAGetLastError()); return -1; } // Register for notification rc = WSAAsyncSelect( sockobj->s, gWorkerWindow, WM_SOCKET, FD_ACCEPT | FD_CLOSE ); if (rc == SOCKET_ERROR) { fprintf(stderr, "WSAAsyncSelect failed: %d\n", WSAGetLastError()); return -1; } } else { // Register for notification rc = WSAAsyncSelect( sockobj->s, gWorkerWindow, WM_SOCKET, FD_READ | FD_WRITE | FD_CLOSE ); if (rc == SOCKET_ERROR) { fprintf(stderr, "WSAAsyncSelect failed: %d\n", WSAGetLastError()); return -1; } } ptr = ptr->ai_next; } // free the addrinfo structure for the 'bind' address freeaddrinfo(res); gStartTime = gStartTimeLast = lastprint = GetTickCount(); // Start a thread to print statistics hThread = CreateThread(NULL, 0, StatisticsThread, NULL, 0, NULL); if (hThread == NULL) { fprintf(stderr, "CreateThread failed: %d\n", WSAGetLastError()); return -1; } CloseHandle(hThread); while(rc = GetMessage(&msg, NULL, 0, 0)) { if (rc == -1) { fprintf(stderr, "GetMessage() failed with error %d\n", GetLastError()); return -1; } TranslateMessage(&msg); DispatchMessage(&msg); } WSACleanup(); DeleteCriticalSection(&gSocketCritSec); return 0; }
// // Function: WindowProc // // Description: // This is the window procedure which handles the window messages for // our hidden window. It handles all the WM_SOCKET messages and performs // the correct actions for each message type (FD_READ, FD_WRITE, etc.). // LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { SOCKET_OBJ *sockobj=NULL, *newsock=NULL; int rc; if (uMsg == WM_SOCKET) { // Check for an error on the socket if (WSAGETSELECTERROR(lParam)) { // An error occured on the socket, close it down fprintf(stderr, "Socket failed with error %d\n", WSAGETSELECTERROR(lParam)); closesocket(wParam); RemoveSocketObjByHandle(wParam); } else { // Find the socket object for this event sockobj = FindSocketObj(wParam); if (sockobj == NULL) return 0; switch (WSAGETSELECTEVENT(lParam)) { case FD_ACCEPT: // Get a new object for the client socket newsock = GetSocketObj(INVALID_SOCKET); newsock->s = accept( wParam, (SOCKADDR *)&newsock->addr, &newsock->addrlen ); if (newsock->s == INVALID_SOCKET) { fprintf(stderr, "accept failed: %d\n", WSAGetLastError()); break; } InterlockedIncrement(&gCurrentConnections); // Create a socket information structure to associate with the // socket for processing I/O. InsertSocketObj(newsock); /* printf("Accepted connection from: "); PrintAddress((SOCKADDR *)&newsock->addr, newsock->addrlen); printf("\n"); */ rc = WSAAsyncSelect( newsock->s, hwnd, WM_SOCKET, FD_READ | FD_WRITE | FD_CLOSE ); if (rc == SOCKET_ERROR) { fprintf(stderr, "WSAAsyncSelect failed: %d\n", WSAGetLastError()); return -1; } break; case FD_READ: rc = ReceivePendingData(sockobj); if (rc == -1) { RemoveSocketObj(sockobj); break; } else if (rc != WSAEWOULDBLOCK) { PostMessage(hwnd, WM_SOCKET, wParam, FD_READ); } // // Don't break fall through and attempt to send data // case FD_WRITE: // // Send routine automatically tries to send all queued buffers. // rc = SendPendingData(sockobj); if (rc == -1) { RemoveSocketObj(sockobj); } break; case FD_CLOSE: sockobj->closing = TRUE; // // Post an FD_READ message to force another receive // This is to ensure we recv() until 0 is returned. // PostMessage(hwnd, WM_SOCKET, wParam, FD_READ); break; default: printf("Unknown message received: %d\n", WSAGETSELECTEVENT(lParam)); break; } } return 0; } return DefWindowProc(hwnd, uMsg, wParam, lParam); }
void main() { // 创建监听套节字,绑定到本地端口,进入监听模式 int nPort = 4567; SOCKET sListen = ::WSASocket( AF_INET, SOCK_STREAM, IPPROTO_TCP, //此三个参数与标准socket相同 NULL, //指定下层服务提供者,可以是NULL 0, //保留 WSA_FLAG_OVERLAPPED); //指定socket属性,要使用重叠I/O模型,必须指定WSA_FLAG_OVERLAPPED //如果使用socket则默认指定WSA_FLAG_OVERLAPPED //绑定并监听IP和端口 SOCKADDR_IN si; si.sin_family = AF_INET; si.sin_port = ::ntohs(nPort); si.sin_addr.S_un.S_addr = INADDR_ANY; ::bind(sListen, (sockaddr*)&si, sizeof(si)); ::listen(sListen, 200); // 为监听套节字创建一个SOCKET_OBJ对象 GetSocketObj函数仅仅开辟一个PSOCKET_OBJ内存并将套接字传入, PSOCKET_OBJ pListen = GetSocketObj(sListen); // 加载扩展函数AcceptEx GUID GuidAcceptEx = WSAID_ACCEPTEX; DWORD dwBytes; //套接字选项和I/O控制命令 此处用来获取AcceptEx函数指针 WSAIoctl(pListen->s, SIO_GET_EXTENSION_FUNCTION_POINTER, &GuidAcceptEx, sizeof(GuidAcceptEx), &pListen->lpfnAcceptEx, sizeof(pListen->lpfnAcceptEx), &dwBytes, NULL, NULL); g_pfnGetAcceptExSockaddrs = (LPFN_GETACCEPTEXSOCKADDRS)GetExtensionFuncPtr(pListen->s); // 创建用来重新建立g_events数组的事件对象 g_events[0] = ::WSACreateEvent(); // 在此可以投递多个接受I/O请求 投递5个接受连接请求 for(int i=0; i<5; i++) { PostAccept(GetBufferObj(pListen, BUFFER_SIZE)); } ::WSASetEvent(g_events[0]); while(TRUE) { int nIndex = ::WSAWaitForMultipleEvents(g_nBufferCount + 1, g_events, FALSE, WSA_INFINITE, FALSE); if(nIndex == WSA_WAIT_FAILED) { printf("WSAWaitForMultipleEvents() failed \n"); break; } nIndex = nIndex - WSA_WAIT_EVENT_0; for(int i=0; i<=nIndex; i++) { int nRet = ::WSAWaitForMultipleEvents(1, &g_events[i], TRUE, 0, FALSE); if(nRet == WSA_WAIT_TIMEOUT) continue; else { ::WSAResetEvent(g_events[i]); // 重新建立g_events数组 if(i == 0) { RebuildArray(); continue; } // 处理这个I/O PBUFFER_OBJ pBuffer = FindBufferObj(g_events[i]); if(pBuffer != NULL) { if(!HandleIO(pBuffer)) RebuildArray(); } } } } }
BOOL HandleIO(PBUFFER_OBJ pBuffer) { PSOCKET_OBJ pSocket = pBuffer->pSocket; // 从BUFFER_OBJ对象中提取SOCKET_OBJ对象指针,为的是方便引用 pSocket->nOutstandingOps --; DWORD dwTrans; //用于取得实际传输字节的数量 DWORD dwFlags; //用于取得完成状态 // 获取重叠操作结果 true 重叠操作成功 false 套接字上有错误发生 BOOL bRet = ::WSAGetOverlappedResult(pSocket->s, &pBuffer->ol, &dwTrans, FALSE, &dwFlags); if(!bRet) { // 在此套节字上有错误发生,因此,关闭套节字,移除此缓冲区对象。 // 如果没有其它抛出的I/O请求了,释放此缓冲区对象,否则,等待此套节字上的其它I/O也完成 if(pSocket->s != INVALID_SOCKET) { ::closesocket(pSocket->s); pSocket->s = INVALID_SOCKET; } if(pSocket->nOutstandingOps == 0) FreeSocketObj(pSocket); FreeBufferObj(pBuffer); return FALSE; } // 没有错误发生,处理已完成的I/O switch(pBuffer->nOperation) { case OP_ACCEPT: // 接收到一个新的连接,并接收到了对方发来的第一个封包 { // 为新客户创建一个SOCKET_OBJ对象 PSOCKET_OBJ pClient = GetSocketObj(pBuffer->sAccept); // 为收发数据创建一个BUFFER_OBJ对象,这个对象会在套节字出错或者关闭时释放 PBUFFER_OBJ pSend = GetBufferObj(pClient, BUFFER_SIZE); if(pSend == NULL) { printf(" Too much connections! \n"); FreeSocketObj(pClient); return FALSE; } SOCKADDR_IN LocalSockAddr; SOCKADDR_IN RemoteSockAddr; int nLocalLen, nRmoteLen; LPSOCKADDR pLocalAddr, pRemoteAddr; g_pfnGetAcceptExSockaddrs( pBuffer->buff, pBuffer->nLen - ((sizeof(sockaddr_in) + 16) * 2), sizeof(sockaddr_in) + 16, sizeof(sockaddr_in) + 16, (SOCKADDR **)&pLocalAddr, &nLocalLen, (SOCKADDR **)&pRemoteAddr, &nRmoteLen); memcpy(&LocalSockAddr, pLocalAddr, nLocalLen); memcpy(&RemoteSockAddr, pRemoteAddr, nRmoteLen); printf("Local Accepted client:%s:%d\n", inet_ntoa(LocalSockAddr.sin_addr), ntohs(LocalSockAddr.sin_port)); printf("Remote Accepted client:%s:%d\n", inet_ntoa(RemoteSockAddr.sin_addr), ntohs(RemoteSockAddr.sin_port)); RebuildArray(); // 将数据复制到发送缓冲区 pSend->nLen = dwTrans; memcpy(pSend->buff, pBuffer->buff, dwTrans); // 投递此发送I/O(将数据回显给客户) if(!PostSend(pSend)) { // 万一出错的话,释放上面刚申请的两个对象 FreeSocketObj(pSocket); FreeBufferObj(pSend); return FALSE; } // 继续投递接受I/O PostAccept(pBuffer); } break; case OP_RECV: // 接收数据完成 { if(dwTrans > 0) { // 创建一个缓冲区,以发送数据。这里就使用原来的缓冲区 PBUFFER_OBJ pSend = pBuffer; printf("收到:%s\r\n", pBuffer->buff); pSend->nLen = dwTrans; // 投递发送I/O(将数据回显给客户) PostSend(pSend); } else // 套节字关闭 { // 必须先关闭套节字,以便在此套节字上投递的其它I/O也返回 if(pSocket->s != INVALID_SOCKET) { ::closesocket(pSocket->s); pSocket->s = INVALID_SOCKET; } if(pSocket->nOutstandingOps == 0) FreeSocketObj(pSocket); FreeBufferObj(pBuffer); return FALSE; } } break; case OP_SEND: // 发送数据完成 { if(dwTrans > 0) { // 继续使用这个缓冲区投递接收数据的请求 pBuffer->nLen = BUFFER_SIZE; PostRecv(pBuffer); } else // 套节字关闭 { // 同样,要先关闭套节字 if(pSocket->s != INVALID_SOCKET) { ::closesocket(pSocket->s); pSocket->s = INVALID_SOCKET; } if(pSocket->nOutstandingOps == 0) FreeSocketObj(pSocket); FreeBufferObj(pBuffer); return FALSE; } } break; } return TRUE; }