Post
IOCP 채팅 서버 소스 예제
윤성우의 TCP/IP 에코 채팅 서버에서
다른사람들에게 채팅 내용이 전송되도록 수정한 소스이다.
#include#include #include #include #include #include #include #pragma comment(lib, "ws2_32.lib") #define BUF_SIZE 100 #define READ 3 #define WRITE 5 int const NAME_SIZE = 10; typedef struct // socket intfo { SOCKET hClntSock; SOCKADDR_IN clntAdr; }PER_HANDLE_DATA, *LPPER_HANDLE_DATA; typedef struct // buffer info { OVERLAPPED overlapped; WSABUF wsaBuf; char buffer[BUF_SIZE]; int rwMode; // READ or WRITE }PER_IO_DATA, *LPPER_IO_DATA; typedef struct { char id[NAME_SIZE]; unsigned int level; }USER_INFO, *PUSER_INFO; // 나중에 PER_HANDLE_DATA 상속하자 std::list
UserList; // 다른사람에게 채팅 전달해야하므로 user list 추가 DWORD WINAPI EchoThreadMain(LPVOID CompletionPortIO); CRITICAL_SECTION cs; // UserList 동기화를 위한 Critical Section int main() { WSADATA wsaData; HANDLE hComPort; SYSTEM_INFO sysInfo; LPPER_IO_DATA ioInfo; LPPER_HANDLE_DATA handleInfo; SOCKET hServSock; SOCKADDR_IN servAdr; int recvBytes, i, flags = 0; if (0 != WSAStartup(MAKEWORD(2, 2), &wsaData)) { printf("WSAStartup() error!"); exit(1); } hComPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0); GetSystemInfo(&sysInfo); for (i = 0; i < (int)sysInfo.dwNumberOfProcessors; ++i) { // Thread 핸들 정리 추가할 것 _beginthreadex(NULL, 0, (_beginthreadex_proc_type)EchoThreadMain, (LPVOID)hComPort, 0, NULL); } hServSock = WSASocketW(AF_INET, SOCK_STREAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED); memset(&servAdr, 0, sizeof(servAdr)); servAdr.sin_family = AF_INET; inet_pton(AF_INET, "127.0.0.1", &(servAdr.sin_addr)); servAdr.sin_port = htons(atoi("7777")); bind(hServSock, (SOCKADDR*)&servAdr, sizeof(servAdr)); listen(hServSock, 5); InitializeCriticalSection(&cs); while (1) { SOCKET hClntSock; SOCKADDR_IN clntAdr; int addrLen = sizeof(clntAdr); hClntSock = accept(hServSock, (SOCKADDR*)&clntAdr, &addrLen); handleInfo = (LPPER_HANDLE_DATA)malloc(sizeof(PER_HANDLE_DATA)); // 누군가 접속할 때 마다 소켓 정보를 생성하고 handleInfo->hClntSock = hClntSock; // hClntSock은 지역변수이므로 저장한다 memcpy(&(handleInfo->clntAdr), &clntAdr, addrLen); EnterCriticalSection(&cs); UserList.push_back(handleInfo); LeaveCriticalSection(&cs); CreateIoCompletionPort((HANDLE)hClntSock, hComPort, (DWORD)handleInfo, 0); // 접속한 클라 소켓을 IOCP에 등록함 ioInfo = (LPPER_IO_DATA)malloc(sizeof(PER_IO_DATA)); // 신규 유저 접속 시 overlapped도 생성한다. memset(&(ioInfo->overlapped), 0, sizeof(OVERLAPPED)); // 그리고 WSARecv 함수로 소켓 io완료 시 반환되는 wasBuf, overlapped 주소를 등록한다 ioInfo->wsaBuf.len = BUF_SIZE; ioInfo->wsaBuf.buf = ioInfo->buffer; ioInfo->rwMode = READ; WSARecv(handleInfo->hClntSock, &(ioInfo->wsaBuf), 1, (LPDWORD)&recvBytes, (LPDWORD)&flags, &(ioInfo->overlapped), NULL); // recv가 완료되었을 때 GetQueue...에서 반환됨 } DeleteCriticalSection(&cs); return 0; } DWORD WINAPI EchoThreadMain(LPVOID pComPort) { HANDLE hComPort = (HANDLE)pComPort; SOCKET sock; DWORD bytesTrans; LPPER_HANDLE_DATA handleInfo; LPPER_IO_DATA ioInfo; DWORD flags = 0; char msg[BUF_SIZE]; while (1) { GetQueuedCompletionStatus(hComPort, &bytesTrans, (LPDWORD)&handleInfo, (LPOVERLAPPED*)&ioInfo, INFINITE); sock = handleInfo->hClntSock; if (READ == ioInfo->rwMode) { puts("message received!"); if (0 == bytesTrans) { EnterCriticalSection(&cs); std::list ::iterator iter; for (iter = UserList.begin(); iter != UserList.end(); ++iter) { if (sock == (*iter)->hClntSock) { UserList.erase(iter); break; } } LeaveCriticalSection(&cs); closesocket(sock); free(handleInfo); free(ioInfo); continue; } memset(msg, 0, BUF_SIZE); memcpy(msg, ioInfo->buffer, BUF_SIZE); free(ioInfo); EnterCriticalSection(&cs); // UserList는 여러 스레드가 접근하는 공유자원이므로 접근 시 cs 필수 std::list ::iterator iter; for (iter = UserList.begin(); iter != UserList.end(); ++iter) { // 소켓마다 overlapped를 새로 만들어야 한다. 왜냐하면 하나의 overlapped를 여러 소켓이 쓸 수 없기 때문이다. ioInfo = (LPPER_IO_DATA)malloc(sizeof(PER_IO_DATA)); memset(&(ioInfo->overlapped), 0, sizeof(OVERLAPPED)); ioInfo->wsaBuf.buf = msg; // msg는 스레드마다 가지고 있는 지역변수이므로 공유자원이 아니므로 cs 사용을 하지 않는다 ioInfo->wsaBuf.len = bytesTrans; ioInfo->rwMode = WRITE; WSASend((*iter)->hClntSock, &(ioInfo->wsaBuf), 1, NULL, 0, &(ioInfo->overlapped), NULL); } LeaveCriticalSection(&cs); ioInfo = (LPPER_IO_DATA)malloc(sizeof(PER_IO_DATA)); memset(&(ioInfo->overlapped), 0, sizeof(OVERLAPPED)); ioInfo->wsaBuf.len = BUF_SIZE; ioInfo->wsaBuf.buf = ioInfo->buffer; ioInfo->rwMode = READ; WSARecv(sock, &(ioInfo->wsaBuf), 1, NULL, &flags, &(ioInfo->overlapped), NULL); } else { puts("message sent!"); free(ioInfo); } } return 0; }
실행하면 아래처럼 여러사람들에게 메시지가 전송된다.
내가 개인적으로 저장할 용도로 올리는거라서 수정, 추가할 부분이 많다:Q...
( 메시지큐를 이용해서 데이터 받는 부분, 처리부분 분리하기, 패킷 덜 왔을 때 합치기, acceptEx , 메모리풀 등..)
'이전게시판 > Server' 카테고리의 다른 글
IOCP window 채팅 클라이언트 소스 (0) | 2018.10.04 |
---|---|
C4996 에러 (0) | 2018.09.28 |
LNK2019: __imp__closesocket@4 외부 기호(참조 위치: _main 함수)에서 확인하지 못했습니다. (0) | 2018.09.28 |
구글 프로토콜 버퍼 C++ Window 예제 (Google Protocol buffer) (0) | 2018.07.27 |
기본 서버 예제의 문제점 (0) | 2018.07.05 |
C4996 inet_addr Error Solution (0) | 2018.06.30 |
ip주소 추적 (0) | 2018.04.21 |
hosts파일, DNS (0) | 2018.04.21 |