#include <winsock2.h>
#include <stdlib.h>
#include <stdio.h>

#define BUFSIZE 512

// 소켓정보를저장하기위한구조체
struct SOCKETINFO

    OVERLAPPED overlapped; 
    SOCKET sock; 
    char buf[BUFSIZE+1]; 
    int recvbytes; 
    int sendbytes; 
    WSABUF wsabuf;
};

 // 소켓입출력함수
DWORD WINAPI WorkerThread(LPVOID arg);

// 오류출력함수
void err_quit(char *msg);
void err_display(char *msg); 
 
int accept_proc(SOCKET *listen_sock, HANDLE *hcp)

    int retval; 
    // accept() 
    SOCKADDR_IN clientaddr; 
    int addrlen = sizeof(clientaddr); 
    SOCKET client_sock = accept(*listen_sock, (SOCKADDR *)&clientaddr, &addrlen); 
    if(client_sock == INVALID_SOCKET) 
    { 
        err_display("accept()"); 
        return -1; 
    } 
   printf("[TCP 서버] 클라이언트접속: IP 주소= %s, 포트번호= %d \n"
        inet_ntoa(clientaddr.sin_addr), 
        ntohs(clientaddr.sin_port)); 
 
    // 소켓과입출력완료포트연결 
    HANDLE hResult = CreateIoCompletionPort((HANDLE)client_sock, 
        *hcp, (DWORD)client_sock, 0); 
    if(hResult == NULL) 
    { 
        err_quit("create iocp"); 
    } 

     // 소켓정보구조체할당 
    SOCKETINFO *ptr = new SOCKETINFO; 
    if(ptr == NULL) 
    { 
        err_quit("[오류] 메모리부족!!\n"); 
    } 
    ZeroMemory(&(ptr->overlapped), sizeof(ptr->overlapped)); 
    ptr->sock = client_sock; 
    ptr->recvbytes = 0; 
    ptr->sendbytes = 0; 
    ptr->wsabuf.buf = ptr->buf; 
    ptr->wsabuf.len = BUFSIZE; 
 
    // 비동기입출력시작 
    DWORD recvbytes; 
    DWORD flags = 0; 
    retval = WSARecv(client_sock, &(ptr->wsabuf), 1, &recvbytes, &flags, &(ptr->
overlapped), NULL);

    if(retval == SOCKET_ERROR) 
    { 
        if(WSAGetLastError() != ERROR_IO_PENDING) 
        { 
            err_display("WSARecv()"); 
        } 
        return -1; 
    } 
 
    return 0;

 
int is_log_out(DWORD *cbTransferred, SOCKET *client_sock, SOCKETINFO *ptr, SOCKADDR_IN *clientaddr, int retval)
{         
    // 비동기입출력결과확인 
    if(retval == 0 || *cbTransferred == 0) 
    { 
        if(retval == 0) 
        { 
            DWORD temp1, temp2; 
            WSAGetOverlappedResult(ptr->sock, &(ptr->overlapped), 
                &temp1, FALSE, &temp2); 
            err_display("WSAGetOverlappedResult()"); 
        } 
        closesocket(ptr->sock); 
        printf("[TCP 서버] 클라이언트종료: IP 주소= %s, 포트번호= %d \n"
            inet_ntoa(clientaddr->sin_addr), 
            ntohs(clientaddr->sin_port)); 
 
        delete ptr; 
        return 1; 
    } 
 
    return 0;

 
int send_proc(SOCKETINFO *ptr)

    int retval; 
 
    // 데이터보내기 
    ZeroMemory(&(ptr->overlapped), sizeof(ptr->overlapped)); 
    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()"); 
        } 
        return -1; 
    } 
    return 0;


int recv_proc(SOCKETINFO *ptr)

    int retval; 
 
    ptr->recvbytes = 0; 
 
    // 데이터받기 
    ZeroMemory(&(ptr->overlapped), sizeof(ptr->overlapped)); 
    ptr->wsabuf.buf = ptr->buf; 
    ptr->wsabuf.len = BUFSIZE; 
 
    DWORD recvbytes; 
    DWORD 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()"); 
        } 
        return -1; 
    } 
    return 0;

 
int main()

    int retval; 

    // 윈속초기화 
    WSADATA wsa; 
    if(WSAStartup(MAKEWORD(2,2), &wsa) != 0) 
        return -1; 
 
    // 입출력완료포트생성 
    HANDLE hcp = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0); 
    if(hcp == NULL) 
        return -1; 
 
    // CPU 갯수확인 
    SYSTEM_INFO si; 
    GetSystemInfo(&si); 
 
    // (CPU 개수* 2)개의작업자스레드생성 
    HANDLE hThread; 
    DWORD ThreadId; 
    for(int i=0; i < (int)si.dwNumberOfProcessors*2; ++i) 
    { 
        hThread = CreateThread(NULL, 0, WorkerThread, hcp, 0, &ThreadId); 
        if(hThread == NULL) 
            return -1; 
        CloseHandle(hThread); 
    } 
 
    // 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_port = htons(9000); 
    serveraddr.sin_addr.s_addr = htonl(INADDR_ANY); 
    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()"); 
 
    while(1) 
    { 
        if(accept_proc(&listen_sock, &hcp) == -1) 
            continue
    } 
 
    // 윈속종료 
    WSACleanup(); 
    return 0;

 
DWORD WINAPI WorkerThread(LPVOID arg)

    HANDLE hcp = (HANDLE)arg; 
    int retval; 
 
    while(1) 
    { 
        // 비동기입출력완료기다리기 
        DWORD cbTransferred; 
        SOCKET client_sock; 
        SOCKETINFO *ptr; 
        retval = GetQueuedCompletionStatus(hcp, &cbTransferred, 
            (LPDWORD)&client_sock, (LPOVERLAPPED*)&ptr, INFINITE); 
 
        // 클라이언트정보얻기 
        SOCKADDR_IN clientaddr; 
        int addrlen = sizeof(clientaddr); 
        getpeername(ptr->sock, (SOCKADDR*)&clientaddr, &addrlen); 
     
        if(is_log_out(&cbTransferred, &client_sock, ptr, &clientaddr, retval) == 1) 
            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) 
        { 
            if(send_proc(ptr) == -1) 
                continue
        } 
        else 
        { 
            if(recv_proc(ptr) == -1) 
                continue
        } 
    } 
     
    return 0;

 

// 소켓함수오류출력후종료
void err_quit(char *msg)

    LPVOID lpMsgBuf; 
    FormatMessage( 
        FORMAT_MESSAGE_ALLOCATE_BUFFER| 
        FORMAT_MESSAGE_FROM_SYSTEM, 
        NULL, WSAGetLastError(), 
        MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), 
        (LPTSTR)&lpMsgBuf, 0, NULL); 
    MessageBox(NULL, (LPCTSTR)lpMsgBuf, msg, MB_ICONERROR); 
    LocalFree(lpMsgBuf); 
    exit(-1);
}

// 소켓함수오류출력
void err_display(char *msg)

    LPVOID lpMsgBuf; 
    FormatMessage( 
        FORMAT_MESSAGE_ALLOCATE_BUFFER| 
        FORMAT_MESSAGE_FROM_SYSTEM, 
        NULL, WSAGetLastError(), 
        MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), 
        (LPTSTR)&lpMsgBuf, 0, NULL); 
    printf("[%s] %s", msg, (LPCTSTR)lpMsgBuf); 
    LocalFree(lpMsgBuf);
}

 인터넷에 떠도는 IOCP 에코 서버를 나름대로 잘라 보았습니다.
(그래봤자.. 어디서 logout, accept, send, recv 하는걸 함수채로 자른 것 밖에 없습니다;; )

신고
블로그 이미지

프로그래머 지향자 RosaGigantea

바쁜 일상 생활중의 기억 장소

음.. 각각 100 줄정도의 간단한 프로그램
IP / port 는 127.0.0.1  / 9000 으로 고정되있으므로

이걸 약간만 수정하면, 리눅스용이던지, 다른 서버 관련 프로그램에서
재 사용이 가능할듯

작업은 VC 2005에서 함

신고
블로그 이미지

프로그래머 지향자 RosaGigantea

바쁜 일상 생활중의 기억 장소


상당히 정리한 버젼
Visual Studio .net 2005 로 작성되었고..
client, server 각각 한 200~250줄 정도...
단순 c언어로 되어있으니.. 뭐 상관없으려나..

#define test를 제거해야 원래 sender가 작동.
test 용도로 apiprj.alz (API정복 소스 압축파일 29MB)와, Test.avi(100여메가) 파일을 사용

이건 만일을 대비한것
이거에서 어떻게 저 위로 바꼈는지 잘 생각해 볼것..
신고
블로그 이미지

프로그래머 지향자 RosaGigantea

바쁜 일상 생활중의 기억 장소

코어쪽으로선 완벽한걸까;;;

신고
블로그 이미지

프로그래머 지향자 RosaGigantea

바쁜 일상 생활중의 기억 장소

이젠.. 지쳤어.. 아.. 내 JPT
신고
블로그 이미지

프로그래머 지향자 RosaGigantea

바쁜 일상 생활중의 기억 장소