// // Function: DerefSocketContext // // Description: // This routine holds the LSP critical section and decrements the ref count // by one. It also checks if the socket has been closed while holding the // ref count. This can happen if two threads are accessing a socket simultaneously // and one calls closesocket. We don't want to remove the context from under // the second thread so it is marked as closing instead. // void DerefSocketContext( SOCK_INFO *context, int *lpErrno ) { LONG newval; int ret = NO_ERROR; EnterCriticalSection(&GlobalVars::gCriticalSection); // Decrement the ref count and see if someone closed this socket (from another thread) newval = InterlockedDecrement(&context->RefCount); if ( ( 0 == newval ) && ( 0 == context->dwOutstandingAsync ) && ( TRUE == context->bClosing ) ) { ASSERT( GlobalVars::gMainUpCallTable.lpWPUCloseSocketHandle ); // Socket has been closed so close the handle and free associated resources ret = GlobalVars::gMainUpCallTable.lpWPUCloseSocketHandle(context->LayeredSocket, lpErrno); if ( SOCKET_ERROR == ret ) { dbgprint("DerefSocketContext: WPUCloseSocketHandle() failed: %d", *lpErrno); } context->LayeredSocket = INVALID_SOCKET; RemoveSocketInfo(context->Provider, context); dbgprint("Closing socket %d Bytes Sent [%lu] Bytes Recv [%lu]", context->LayeredSocket, context->BytesSent, context->BytesRecv); FreeSockInfo( context ); context = NULL; } LeaveCriticalSection( &GlobalVars::gCriticalSection ); }
int main(int argc, char *argv[]) { int retval; InitializeCriticalSection(&cs); // 윈속 초기화 WSADATA wsa; if(WSAStartup(MAKEWORD(2,2), &wsa) != 0) return 1; // socket() SOCKET listen_sock = socket(AF_INET, SOCK_STREAM, 0); if(listen_sock == INVALID_SOCKET) err_quit("socket()"); // bind() SOCKADDR_IN serveraddr; ZeroMemory(&serveraddr, sizeof(serveraddr)); serveraddr.sin_family = AF_INET; serveraddr.sin_addr.s_addr = htonl(INADDR_ANY); serveraddr.sin_port = htons(SERVERPORT); retval = bind(listen_sock, (SOCKADDR *)&serveraddr, sizeof(serveraddr)); if(retval == SOCKET_ERROR) err_quit("bind()"); // listen() retval = listen(listen_sock, SOMAXCONN); if(retval == SOCKET_ERROR) err_quit("listen()"); // 더미(dummy) 이벤트 객체 생성 WSAEVENT hEvent = WSACreateEvent(); if(hEvent == WSA_INVALID_EVENT) err_quit("WSACreateEvent()"); EventArray[nTotalSockets++] = hEvent; // 스레드 생성 HANDLE hThread = CreateThread(NULL, 0, WorkerThread, NULL, 0, NULL); if(hThread == NULL) return 1; CloseHandle(hThread); // 데이터 통신에 사용할 변수 SOCKET client_sock; SOCKADDR_IN clientaddr; int addrlen; DWORD recvbytes, flags; while(1){ // accept() addrlen = sizeof(clientaddr); client_sock = accept(listen_sock, (SOCKADDR *)&clientaddr, &addrlen); if(client_sock == INVALID_SOCKET){ err_display("accept()"); break; } printf("\n[TCP 서버] 클라이언트 접속: IP 주소=%s, 포트 번호=%d\n", inet_ntoa(clientaddr.sin_addr), ntohs(clientaddr.sin_port)); // 소켓 정보 추가 if(AddSocketInfo(client_sock) == FALSE){ closesocket(client_sock); printf("[TCP 서버] 클라이언트 종료: IP 주소=%s, 포트 번호=%d\n", inet_ntoa(clientaddr.sin_addr), ntohs(clientaddr.sin_port)); continue; } // 비동기 입출력 시작 SOCKETINFO *ptr = SocketInfoArray[nTotalSockets-1]; flags = 0; retval = WSARecv(ptr->sock, &ptr->wsabuf, 1, &recvbytes, &flags, &ptr->overlapped, NULL); if(retval == SOCKET_ERROR){ if(WSAGetLastError() != WSA_IO_PENDING){ err_display("WSARecv()"); RemoveSocketInfo(nTotalSockets-1); continue; } } // 소켓의 개수(nTotalSockets) 변화를 알림 WSASetEvent(EventArray[0]); } // 윈속 종료 WSACleanup(); DeleteCriticalSection(&cs); return 0; }
// 비동기 입출력 처리 함수 DWORD WINAPI WorkerThread(LPVOID arg) { int retval; while(1){ // 이벤트 객체 관찰 DWORD index = WSAWaitForMultipleEvents(nTotalSockets, EventArray, FALSE, WSA_INFINITE, FALSE); if(index == WSA_WAIT_FAILED) continue; index -= WSA_WAIT_EVENT_0; WSAResetEvent(EventArray[index]); if(index == 0) continue; // 클라이언트 정보 얻기 SOCKETINFO *ptr = SocketInfoArray[index]; SOCKADDR_IN clientaddr; int addrlen = sizeof(clientaddr); getpeername(ptr->sock, (SOCKADDR *)&clientaddr, &addrlen); // 비동기 입출력 결과 확인 DWORD cbTransferred, flags; retval = WSAGetOverlappedResult(ptr->sock, &ptr->overlapped, &cbTransferred, FALSE, &flags); if(retval == FALSE || cbTransferred == 0){ RemoveSocketInfo(index); printf("[TCP 서버] 클라이언트 종료: IP 주소=%s, 포트 번호=%d\n", inet_ntoa(clientaddr.sin_addr), ntohs(clientaddr.sin_port)); continue; } // 데이터 전송량 갱신 if(ptr->recvbytes == 0){ ptr->recvbytes = cbTransferred; ptr->sendbytes = 0; // 받은 데이터 출력 ptr->buf[ptr->recvbytes] = '\0'; printf("[TCP/%s:%d] %s\n", inet_ntoa(clientaddr.sin_addr), ntohs(clientaddr.sin_port), ptr->buf); } else{ ptr->sendbytes += cbTransferred; } if(ptr->recvbytes > ptr->sendbytes){ // 데이터 보내기 ZeroMemory(&ptr->overlapped, sizeof(ptr->overlapped)); ptr->overlapped.hEvent = EventArray[index]; ptr->wsabuf.buf = ptr->buf + ptr->sendbytes; ptr->wsabuf.len = ptr->recvbytes - ptr->sendbytes; DWORD sendbytes; retval = WSASend(ptr->sock, &ptr->wsabuf, 1, &sendbytes, 0, &ptr->overlapped, NULL); if(retval == SOCKET_ERROR){ if(WSAGetLastError() != WSA_IO_PENDING){ err_display("WSASend()"); } continue; } } else{ ptr->recvbytes = 0; // 데이터 받기 ZeroMemory(&ptr->overlapped, sizeof(ptr->overlapped)); ptr->overlapped.hEvent = EventArray[index]; ptr->wsabuf.buf = ptr->buf; ptr->wsabuf.len = BUFSIZE; DWORD recvbytes; flags = 0; retval = WSARecv(ptr->sock, &ptr->wsabuf, 1, &recvbytes, &flags, &ptr->overlapped, NULL); if(retval == SOCKET_ERROR){ if(WSAGetLastError() != WSA_IO_PENDING){ err_display("WSARecv()"); } continue; } } } }
int main(int argc, char *argv[]) { int retval; // 윈속 초기화 WSADATA wsa; if(WSAStartup(MAKEWORD(2,2), &wsa) != 0) return 1; // socket() SOCKET listen_sock = socket(AF_INET, SOCK_STREAM, 0); if(listen_sock == INVALID_SOCKET) err_quit("socket()"); // bind() SOCKADDR_IN serveraddr; ZeroMemory(&serveraddr, sizeof(serveraddr)); serveraddr.sin_family = AF_INET; serveraddr.sin_addr.s_addr = htonl(INADDR_ANY); serveraddr.sin_port = htons(SERVERPORT); retval = bind(listen_sock, (SOCKADDR *)&serveraddr, sizeof(serveraddr)); if(retval == SOCKET_ERROR) err_quit("bind()"); // listen() retval = listen(listen_sock, SOMAXCONN); if(retval == SOCKET_ERROR) err_quit("listen()"); // 넌블로킹 소켓으로 전환 u_long on = 1; retval = ioctlsocket(listen_sock, FIONBIO, &on); if(retval == SOCKET_ERROR) err_display("ioctlsocket()"); // 데이터 통신에 사용할 변수 FD_SET rset, wset; SOCKET client_sock; SOCKADDR_IN clientaddr; int addrlen, i; while(1){ // 소켓 셋 초기화 FD_ZERO(&rset); FD_ZERO(&wset); FD_SET(listen_sock, &rset); for(i=0; i<nTotalSockets; i++){ if(SocketInfoArray[i]->recvbytes > SocketInfoArray[i]->sendbytes) FD_SET(SocketInfoArray[i]->sock, &wset); else FD_SET(SocketInfoArray[i]->sock, &rset); } // select() retval = select(0, &rset, &wset, NULL, NULL); if(retval == SOCKET_ERROR) err_quit("select()"); // 소켓 셋 검사(1): 클라이언트 접속 수용 if(FD_ISSET(listen_sock, &rset)){ addrlen = sizeof(clientaddr); client_sock = accept(listen_sock, (SOCKADDR *)&clientaddr, &addrlen); if(client_sock == INVALID_SOCKET){ err_display("accept()"); } else{ printf("\n[TCP 서버] 클라이언트 접속: IP 주소=%s, 포트 번호=%d\n", inet_ntoa(clientaddr.sin_addr), ntohs(clientaddr.sin_port)); // 소켓 정보 추가 AddSocketInfo(client_sock); } } // 소켓 셋 검사(2): 데이터 통신 for(i=0; i<nTotalSockets; i++){ SOCKETINFO *ptr = SocketInfoArray[i]; if(FD_ISSET(ptr->sock, &rset)){ // 데이터 받기 retval = recv(ptr->sock, ptr->buf, BUFSIZE, 0); if(retval == SOCKET_ERROR){ err_display("recv()"); RemoveSocketInfo(i); continue; } else if(retval == 0){ RemoveSocketInfo(i); continue; } ptr->recvbytes = retval; // 받은 데이터 출력 addrlen = sizeof(clientaddr); getpeername(ptr->sock, (SOCKADDR *)&clientaddr, &addrlen); ptr->buf[retval] = '\0'; printf("[TCP/%s:%d] %s\n", inet_ntoa(clientaddr.sin_addr), ntohs(clientaddr.sin_port), ptr->buf); } if(FD_ISSET(ptr->sock, &wset)){ // 데이터 보내기 retval = send(ptr->sock, ptr->buf + ptr->sendbytes, ptr->recvbytes - ptr->sendbytes, 0); if(retval == SOCKET_ERROR){ err_display("send()"); RemoveSocketInfo(i); continue; } ptr->sendbytes += retval; if(ptr->recvbytes == ptr->sendbytes){ ptr->recvbytes = ptr->sendbytes = 0; } } } } // 윈속 종료 WSACleanup(); return 0; }