#include "pch.h"
#include <thread>
#include <atomic>
#include <mutex>
#include <future>
#include "CorePch.h"
#include "ThreadManager.h"
#include "RefCounting.h"
#include "Memory.h"
#include "TypeCast.h"
#include <WinSock2.h>
#include <MSWSock.h>
#include <WS2tcpip.h>
#pragma comment(lib, "ws2_32.lib")
void HandleError(const char* str) {
int32 errCode = ::WSAGetLastError();
cout << str << " erroCode: " << errCode << endl;
}
const int BUFSIZE = 1000;
struct Session {
WSAOVERLAPPED overlapped = {};
SOCKET socket = INVALID_SOCKET;
char recvBuffer[BUFSIZE] = {};
int32 recvBytes = 0;
};
void CALLBACK RecvCallBack(DWORD error, DWORD recvLen, LPWSAOVERLAPPED overlapped, DWORD flags) {
Session* s = (Session*)overlapped;
cout << "Data Recv Len CallBack = " << recvLen << endl;
cout << "Data Recv = " << s->recvBuffer << endl;
//TODO 에코 서버시 WSASend()
}
int main() {
//윈도우 소켓 초기화
// 관련 정보가 wsaData에 채워짐
WSADATA wsaData;
if (::WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
return 0;
SOCKET serverSocket = ::socket(AF_INET, SOCK_STREAM, 0);
if (serverSocket == INVALID_SOCKET) {
return 0;
}
u_long on = 1;
if (::ioctlsocket(serverSocket, FIONBIO, &on) == INVALID_SOCKET) // non-block
return 0;
SOCKADDR_IN serverAddr;
serverAddr.sin_family = AF_INET;
serverAddr.sin_addr.s_addr = ::htonl(INADDR_ANY);
serverAddr.sin_port = ::htons(7777);
if (::bind(serverSocket, (SOCKADDR*)&serverAddr, sizeof(serverAddr)) == SOCKET_ERROR)
return 0;
if (::listen(serverSocket, 10) == SOCKET_ERROR)
return 0;
cout << "Accept" << endl;
// Overlapped IO (비동기 + 논블록)
// - Overlapped 함수를 건다 (WSARecv, WSASend)
// - overlapped 함수가 성공하였는지 확인 후,
// -> 성공하였으면 결과 얻어서 처리
// -> 실패하였으면 사유를 확인
// 1) 비동기 입출력 소켓
// 2) WSABUF 배열의 시작 주소 + 개수
// 3) 보내고/받은 비트 수
// 4) 상세 옵션 0
// 5) WSAOVERLLAPED 구조체의 주소값
// 6) 입출력이 완료되면 OS가 호출할 콜백 함수 (이번에는 안씀)
//WSASend
//WSARecv
// Overlapped 모델 (completion Routine 콜백 기반)
// -비동기 입출력 지원하는 소켓 생성
// -비동기 입출력 함수 호출(완료 루틴의 시작 주소를 넘겨준다)
// -비동기 작업이 바로 완료되지 않으면, WSA_IO_PENDING 오류 코드
// -비동기 입출력 함수 호출한 쓰레드를 Alertable Wait 상태로 만든다.
// ex) WaitForSingleObjectEx, waitForMultipleObjectEx, SleepEx, WSAWaitForMultipleEvents
// -비동기 IO 완료되면, 운영체제는 완료 루틴 호출
// -완료 루틴 호출이 모두 끝나면, Alertable Wait 상태를 빠져나온다.
// 1)오류 발생시 0 아닌 값
// 2) 전송 바이트 수
// 3) 비동기 입출력 함수 호출 시 넘겨준 WSAOverlapped의 구조체를 주소값 반환
// 4) 0
// void CompletionRoutine()
// Overlapped( 콜백 기반)
// 장점) 성능
// 단점) 모든 비동기 소켓 함수에서 사용 가능하지 않음 (accept), 빈번한 alertable Wait 인한 성능 저하
while (true) {
SOCKET clientSocket;
SOCKADDR_IN clientAddr;
int32 addrLen = sizeof(clientAddr);
while (true) {
clientSocket = ::accept(serverSocket, (SOCKADDR*)&clientAddr, &addrLen);
if (clientSocket != INVALID_SOCKET)
break;
if (::WSAGetLastError() == WSAEWOULDBLOCK)
continue;
// 문제 있는 상황
return 0;
}
Session s;
s.socket = clientSocket;
WSAEVENT wsaEvent = ::WSACreateEvent();
//s.overlapped.hEvent = wsaEvent;
cout << "client connected!" << endl;
while (true) {
WSABUF wsabuf;
wsabuf.buf = s.recvBuffer;
wsabuf.len = BUFSIZE;
DWORD recvLen = 0;
DWORD flags = 0;
if (::WSARecv(s.socket, &wsabuf, 1, &recvLen, &flags, &s.overlapped, RecvCallBack) == SOCKET_ERROR) {
if (WSAGetLastError() == WSA_IO_PENDING) {
// Pending
// Alertable Wait
//::WSAWaitForMultipleEvents(1, &wsaEvent, FALSE, WSA_INFINITE, TRUE);
::SleepEx(INFINITE, TRUE);
}
else {
break;
// error
}
}
else {
cout << "Data Received: " << recvLen << endl;
cout << "Data detailed:" << s.recvBuffer << endl;
}
}
::closesocket(s.socket);
::WSACloseEvent(wsaEvent);
}
::closesocket(serverSocket); // 소켓 리소스 반환
::WSACleanup(); // 윈속 종료
return 0;
}
'MMOServer' 카테고리의 다른 글
IOCP(C++) (0) | 2025.04.07 |
---|---|
Overlapped 모델[이벤트 기반](C++) (0) | 2025.04.04 |
WSA(window socket api)EventSelect (c++) (0) | 2025.04.03 |
Socket_select방식(C++) (0) | 2025.04.03 |
Non-Blocking Socket(C++) (0) | 2025.04.02 |