• 이 Server는, Client로 접속했을 때 입력한 값을 그대로 돌려주는 Echo Server이다.
  • vi editor를 이용해서 아래의 source code를 입력한 후 Shift + Z + Z로 저장한다.
  • gcc를 이용하여 컴파일 한 후 Client보다 먼저 실행한다.
  • 이 때 파일명 뒤에 port number를 입력해주게 되는데, 이 port number는 Client와 같아야 한다.
  •     ex) vi echo_server.c
  •           gcc echo_server.c -o echo_server
  •           ./echo_server 50000

 

아래는 echo_server.c에 입력할 souce code이다.


 

#include<sys/wait.h>
#include<signal.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#define BUF_LEN 128


void clean();


int main(int argc, char* argv[]) {
     struct sockaddr_in server_addr, client_addr;
     int master_skt, slave_skt;
     int len, len_out;
    int port;
     char buf[BUF_LEN+1];

    if(argc != 3){

          printf("사용법 : %s port\n", argv[0]);

          return -1;

     }

     port = atoi(argv[2]);

     if(master_skt = socket(PF_INET, SOCK_STREAM, 0) < 0){
          printf("서버 소켓 생성 실패\n");
          return -1;
     }

     bzero((char *)&server_addr, sizeof(server_addr));
     server_addr.sin_family = AF_INET;
     server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
     server_addr.sin_port = htons(port);

    if(bind(master_skt, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0){
          printf("서버 주소 연결 실패\n");

          return -1;
     }

     listen(master_skt, 5);
     signal(SIGCHLD, clean);

    while(1) {
          printf("Server : Waiting to accept request.\n");
          len = sizeof(client_addr);

          if(slave_skt = accept(master_skt, (struct sockaddr *)&client_addr, &len) < 0){
               printf("Fail to accept the server\n");
              return -1;
          }


          printf("Server : Client connected.\n");

          switch(fork()) {
               case 0 :
                    close(master_skt);
                    len_out = read(slave_skt, buf, sizeof(buf));
                    write(slave_skt, buf, len_out);
                    exit();

               case -1 :

                    exit();

               default :

                    close(slave_skt);
          }
     }
}

void clean(){
     union wait status;

    while(wait3(&status, WNOHANG, (struct rusage *)0) >= 0){ }
}

'공부 해 Boa요. > Network' 카테고리의 다른 글

Client  (0) 2006.10.15
Raw Socket  (0) 2006.09.29
Consol Echo Client  (0) 2006.09.27
실전 네트웍 프로그래밍 - 4 - IOCP Echo Server  (0) 2005.06.29
'실전 네트웍 프로그래밍 -3  (0) 2005.06.29
  • Linux의 Echo Server에 접속할 Client이다.
  • 항상 Server가 먼저 실행된 상태에서 접속해야한다.
  • vi editor로 작성한 다음 아래의 code를 입력한 뒤 저장해 Client 소스 파일을 만든다.
  • gcc를 이용해 Client 소스 파일을 컴파일 하고, Server가 실행된 상태에서 Client를 실행한다.
  •     ex) vi Client.c
  •           source code 입력 후 Shift + Z + Z로 저장.
  •           gcc Client.c -o Client.out

아래는 입력할 source code이다.


#include
<stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>

#define BUF_LEN 80

int main(int argc, char* argv[]){
    int skt, n, len_in, len_out;
    struct sockaddr_in server_addr;
    char* host_addr;
    char buf[BUF_LEN+1];

    if(argc != 3){
        printf("사용법 : %s ip_address\n", argv[0]);
        return -1;
    }

    host_addr = argv[1];


    if(skt = socket(PF_INET, SOCK_STREAM, 0) < 0){
        printf("소켓 생성실패\n");
        return -1;
    }

    bzero((char *)&server_addr, sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_addr.s_addr = inet_addr(host_addr);
    server_addr.sin_port = htons(atoi(argv[2]));

    if(connect(skt, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0){
        printf("연결 실패\n");
        return -1;
    }

    printf("문자열을 입력하시오: ");

    if(fgets(buf, BUF_LEN, stdin)){
        buf[BUF_LEN] = '\0';
        len_out = strlen(buf);
    }
    else{
        printf("오류\n");
        return -1;
    }

    write(skt, buf, 80);

    printf("서버로부터 되돌아 온 문장: ");

    for(len_in = 0, n = 0; len_in < len_out; len_in += n){
        if((n = read(skt, &buf[len_in], len_out - len_in))<0){
            printf("read 오류\n");
            return -1;
        }
    }

    printf("%s", buf);
    close(skt);

    return 0;
}

'공부 해 Boa요. > Network' 카테고리의 다른 글

동시형 서버  (0) 2006.10.27
Raw Socket  (0) 2006.09.29
Consol Echo Client  (0) 2006.09.27
실전 네트웍 프로그래밍 - 4 - IOCP Echo Server  (0) 2005.06.29
'실전 네트웍 프로그래밍 -3  (0) 2005.06.29

#include <winsock2.h>
#include <stdio.h>
#include <stdlib.h>
#define ICMP_ECHO 8// ICMP echo 요청 명령
#define ICMP_ECHOREPLY 0//ICMP echo 응답 명령
#define ICMP_MIN 8 // 최소 ICMP 크기(헤더만) 8 byte
#define default_pkt_size 32
#define max_pkt 1024

typedef struct iphdr {/* IP 헤더 구조 */
    unsigned int h_len:4; // 헤더 길이
    unsigned int version:4; // IP 버전
    unsigned char tos; // 서비스 유형
    unsigned short total_len; // 패킷 전체 길이
    unsigned short ident; // 식별자
    unsigned short frag_and_flags; // 플래그
    unsigned char ttl; // 수명
    unsigned char proto; // 프로토콜 종류(TCP, UDP)
    unsigned short checksum;// IP checksum
    unsigned int sourceIP; // 근원지 IP
    unsigned int destIP;// 목적지 IP
}IpHeader;

// ICMP 헤더 구조
typedef struct _ihdr {
    BYTE i_type;
    BYTE i_code; /* type sub code */
    USHORT i_cksum;
    USHORT i_id;
    USHORT i_seq;
    ULONG timestamp;
}IcmpHeader;

void make_icmp(char *, int);
USHORT checksum(USHORT *, int);
void analyse_response(char *,int ,struct sockaddr_in *);
void errorexit(char *errmsg);


int main(int argc, char **argv){
    WSADATA wsaData;
    SOCKET raw_skt;
    struct sockaddr_in target,from;
    struct hostent *host_ent;
    int return_value, datasize, byte_sent;
    int timeout = 1000, counter=10;
    char *target_ip, *icmp_data, *recvbuf;
    unsigned int addr=0;
    USHORT seq_no = 0;
    char hostname[]="ce.dyu.ac.kr"; // default server

    if(WSAStartup(MAKEWORD(2,1),&wsaData) != 0)
        errorexit("WSAStartup 실패\n");
 
    if(argc < 2)
        fprintf(stderr,"%s 호스트주소 [데이터 크기]\n 정해진 server, 데이터크기 사용함\n", argv[0]);
    else
        strcpy(hostname, argv[1]);

    raw_skt = WSASocket (AF_INET, SOCK_RAW, IPPROTO_ICMP, NULL, 0,0);

    if(raw_skt == INVALID_SOCKET)
        errorexit("WSASocket() 실패\n");

    return_value = setsockopt(raw_skt, SOL_SOCKET, SO_RCVTIMEO, (char*)&timeout,sizeof(timeout));

    if(return_value == SOCKET_ERROR){
        fprintf(stderr," 수신 제한시간 설정 실패\n");
        return -1;
 } 
 
    timeout = 1000;
    return_value = setsockopt(raw_skt,SOL_SOCKET,SO_SNDTIMEO, (char*)&timeout,sizeof(timeout));

    if(return_value == SOCKET_ERROR)
        errorexit("송신 제한시간 설정 실패\n");

    memset(&target, 0, sizeof(target));

 /* if((host_ent = gethostbyname(hostname)) == NULL){
        target.sin_addr.s_addr = (IN_ADDR *)(hostname);
        target.sin_family = AF_INET;
    }
    else*/{
        host_ent = gethostbyname(hostname);
        memcpy(&target.sin_addr, host_ent->h_addr, host_ent->h_length);
        target.sin_family = host_ent->h_addrtype;
 }

    target_ip = inet_ntoa(target.sin_addr);

    if(argc >2){/* 사용자가 데이터 크기를 설정했다면 */
        datasize = atoi(argv[2]);
        if(datasize == 0)
            datasize = default_pkt_size;
 }

    else
        datasize = default_pkt_size;

    datasize += sizeof(IcmpHeader);
    if((icmp_data = malloc(max_pkt))==0)
        errorexit("메모리 할당 실패");

    if((recvbuf = malloc(max_pkt))==0)
        errorexit("메모리 할당 실패");

 memset(icmp_data, 0, max_pkt);
 make_icmp(icmp_data, datasize);
 
//////////////////////////////////////////////////////////////////////////
 
 while(--counter>0) { /* 일정한 회수만큼 ICMP를 보냄*/
  int len_addr=sizeof(struct sockaddr);
  ((IcmpHeader*)icmp_data)->i_cksum = 0;
  ((IcmpHeader*)icmp_data)->timestamp = GetTickCount();
  // 왕복 지연 시간을 측정하기 위하여 현재 시간을 기록.
  ((IcmpHeader*)icmp_data)->i_seq = seq_no++;
  ((IcmpHeader*)icmp_data)->i_cksum =
   checksum((USHORT*)icmp_data, datasize);
  byte_sent = sendto(raw_skt,icmp_data,datasize,0,
   (struct sockaddr*)&target,sizeof(target));
  if (byte_sent == SOCKET_ERROR){
   if (WSAGetLastError() == WSAETIMEDOUT) {
    printf("시간 초과\n");continue;
   } errorexit("sendto 실패\n");
  }
 
 
//////////////////////////////////////////////////////////////////////////
 
  if (byte_sent < datasize ) fprintf(stdout,"%d bytes 전송\n",byte_sent);
  return_value = recvfrom(raw_skt,recvbuf,max_pkt,0,
   (struct sockaddr*)&from,&len_addr);
  if (return_value == SOCKET_ERROR){
   if (WSAGetLastError() == WSAETIMEDOUT) {
    printf("시간 초과\n");continue;
   }fprintf(stderr,"recvfrom 실패\n");
  }
  analyse_response(recvbuf, return_value, &from);
  Sleep(1000);
 }
 return 0;
}

/////////////////////////////////////////

void analyse_response(char *buf, int bytes,struct sockaddr_in *from) {     IpHeader *iphdr; IcmpHeader *icmphdr;  unsigned short iphdrlen;  iphdr = (IpHeader *)buf;   iphdrlen = iphdr->h_len * 4 ;
        if (bytes  < iphdrlen + ICMP_MIN) {
         printf("패킷 크기가 너무 작음%s\n",inet_ntoa(from->sin_addr)); }
        icmphdr = (IcmpHeader*)(buf + iphdrlen);
        if (icmphdr->i_type != ICMP_ECHOREPLY) {
                errorexit("에코 응답이 아님\n"); }                
        if (icmphdr->i_id != (USHORT)GetCurrentProcessId()) {
                errorexit("식별자가 다른 패킷임\n"); // 자신이 보내지 않은 엉뚱한 곳으로부터 응답이 도착했다. 
  }
        printf("%s로부터 %d bytes 도착", inet_ntoa(from->sin_addr),bytes);
        printf(" icmp 순서번호 = %d. ",icmphdr->i_seq);
        printf(" 왕복 지연 시간 : %d ms\n", GetTickCount()-icmphdr->timestamp); }
/////////////////////////////////////////////////////////////
USHORT checksum(USHORT *buffer, int size) {
  unsigned long cksum=0;
  while(size >1) {
        cksum+=*buffer++;   size -=sizeof(USHORT);  }
  if(size) {
        cksum += *(UCHAR*)buffer;  }
  cksum = (cksum >> 16) + (cksum & 0xffff);
  cksum += (cksum >>16);
  return (USHORT)(~cksum);
}
//////////////////////////////////////////////////////////////
void make_icmp(char * icmp_data, int datasize){
  IcmpHeader *icmp_hdr;
  icmp_hdr = (IcmpHeader*)icmp_data;
  icmp_hdr->i_type = ICMP_ECHO;
  icmp_hdr->i_code = 0;
  icmp_hdr->i_id = (USHORT)GetCurrentProcessId();
  icmp_hdr->i_cksum = 0;
  icmp_hdr->i_seq = 0;
  memset(icmp_data+sizeof(IcmpHeader), 0xaa , datasize - sizeof(IcmpHeader));
                /* 0xaa 임의 데이터로 채우기 */
}

void errorexit(char *errmsg){
 fprintf(stderr,errmsg);
 exit(-1);

'공부 해 Boa요. > Network' 카테고리의 다른 글

동시형 서버  (0) 2006.10.27
Client  (0) 2006.10.15
Consol Echo Client  (0) 2006.09.27
실전 네트웍 프로그래밍 - 4 - IOCP Echo Server  (0) 2005.06.29
'실전 네트웍 프로그래밍 -3  (0) 2005.06.29

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

void errorexit(char *);
BOOL parse(int, char **);
int socket_type = SOCK_STREAM;
char *server_name = "192.168.2.210"; // server adress
unsigned short port = 07;

int main(int argc, char **argv){
    char Buffer[128];
    int result;
    unsigned int addr;
    struct sockaddr_in server;
    struct hostent *hp;
    WSADATA wsaData;
    SOCKET serve_socket;

    if(parse(argc, argv)==FALSE)
        exit(-1);

    if((result = WSAStartup(0x202,&wsaData)) != 0)
        errorexit("WSAStartup 실패\n");

    if (port == 0) errorexit("사용법을 다시 보세요\n");

    if(isalpha(server_name[0]))
        hp = gethostbyname(server_name);

    else{
        addr = inet_addr(server_name);
        hp = gethostbyaddr((char *)&addr, 4, AF_INET);
    }
    if(hp == NULL)
        errorexit("%s 라는 주소를 구할 수 없음 \n");

    memset(&server,0,sizeof(server));
    memcpy(&(server.sin_addr),hp->h_addr,hp->h_length);
    server.sin_family = hp->h_addrtype;
    server.sin_port = htons(port);

    serve_socket = socket(AF_INET,socket_type,0);
    if(serve_socket < 0)
        errorexit("소켓 열수 없음\n");

    printf("%s 서버에 연결 성공\n",hp->h_name);
    if(connect(serve_socket, (struct sockaddr*)&server, sizeof(server)) == SOCKET_ERROR)
        errorexit("연결 실패\n");

    wsprintf(Buffer,"클라이언트 테스트 중");
    result = send(serve_socket, Buffer, sizeof(Buffer), 0);
    if(result == SOCKET_ERROR)
        errorexit("전송 실패\n");

    printf("보낸 데이터 [%s]\n",Buffer);
    result = recv(serve_socket, Buffer, sizeof(Buffer), 0);
    if(result == SOCKET_ERROR) {
        fprintf(stderr,"recv() 실패\n");
        closesocket(serve_socket);
        WSACleanup();
        return -1;
    }
    if (result == 0){
        printf("서버 연결을 닫음\n");
        closesocket(serve_socket);
        WSACleanup();
        return -1;
    }

    printf("데이터 수신 성공 : %s\n",Buffer);

    closesocket(serve_socket);
    WSACleanup();
    exit(0);

}

BOOL parse(int argc, char **argv){
    int i;

    if(argc > 1){
        for(i=1; i < argc; i++){
            if((argv[i][0] == '-') || (argv[i][0] == '/')){
                switch(tolower(argv[i][1])){
                    case 'p':
                        if(!stricmp(argv[i+1], "TCP"))
                            socket_type = SOCK_STREAM;
                        else if(!stricmp(argv[i+1],"UDP"))
                            socket_type = SOCK_DGRAM;
                        else{
                            fprintf(stderr,"사용법 : %s -p [protocol] -s [server] -n[port num]\n",argv[0]);
                            return FALSE;
                        }
                        i++;
                        break;

                    case 's':
                        server_name = argv[i++];
                        break;
                    case 'n':
                        port = atoi(argv[i++]);
                        break;

                    default:
                        fprintf(stderr,"사용법 : %s -p [protocol] -s(server) -n[port num]\n",argv[0]);
                        return FALSE;
                    }
            }
            else{
                fprintf(stderr,"사용법 : %s -p [protocol] =s [server] -n [port num]\n",argv[0]);
                return FALSE;
            }
        }
    }
}

void errorexit (char *err_msg){
    fprintf(stderr, err_msg);
    WSACleanup();
    exit(-1);
}

 

'공부 해 Boa요. > Network' 카테고리의 다른 글

Client  (0) 2006.10.15
Raw Socket  (0) 2006.09.29
실전 네트웍 프로그래밍 - 4 - IOCP Echo Server  (0) 2005.06.29
'실전 네트웍 프로그래밍 -3  (0) 2005.06.29
'실전 네트웍 프로그래밍 - 2  (0) 2005.06.29
저번회에 간단하게 IOCP에 대해서 알아 보았다.

저번회를 바탕으로 이번회엔 IOCP Echo Server를 만들것다. 클라이언트로 부터 들어온 메시지를 해당 클라이언트로 되돌려 주는 기능을 하는 아주 단순한 서버이다.

하지만, 근래에 들어서 느끼는 점이 하나 있다. "단순한 놈일수록 견고하게 만들어라." 이말에 공감하시는 분들도 꽤 될거라 생각한다.

각설하고, 우선 소스를 먼저 보자.

#include
#include
#include

#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 main(int argc, char* argv[])
{
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){
// 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()");
continue;
}
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) return -1;

// 소켓 정보 구조체 할당
SOCKETINFO *ptr = new SOCKETINFO;
if(ptr == NULL){
printf("[오류] 메모리가 부족합니다!\\n");
break;
}
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()");
}
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(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;
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->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->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()");
}
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);

'공부 해 Boa요. > Network' 카테고리의 다른 글

Raw Socket  (0) 2006.09.29
Consol Echo Client  (0) 2006.09.27
'실전 네트웍 프로그래밍 -3  (0) 2005.06.29
'실전 네트웍 프로그래밍 - 2  (0) 2005.06.29
실전 네트웍 프로그래밍 - 1  (0) 2005.06.29
1 IOCP 만들기

HANDLE CreateIoCompletionPort (
 HANDLE FileHandle,
 HANDLE ExistingCompletionPort,
 ULONG_PTR CompletionKey,
 DWORD NumberOfConcurrentThreads
);

이름 그대로 IOCP를 만들어 주는 함수 이다. 처음 IOCP를 만들 때는 네개의 인자 모두를 사용하는 것이 아니라 처음의 한개 인자만을 사용한다.그냥 단순히 IOCP를 생성하기만 하는 것 이므로,

HANDLE hIOCP = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);

와 같이 생성하면 된다.

이제 IOCP를 생성했으므로, 앞에서 말했던 것 처럼 이 IOCP를 통해 입출력 완료가 일어난 결과를 통보 받기만 하면 된다.

결과를 통보 받는다 하여 사용자에게 이벤트나 기타의 방법으로 알려 주는 것이 아니다. IOCP는 입출력 완료가 일어나면 그 결과를 특정한 큐에 집어 넣어 둔다. 때문에 사용자는 이 큐를 항상 감시 하고 있어야 한다. 이 큐에 먼가가 들어 오게 되면 입출력 작업중 하나가 완료 된 것이다. 그럼, 이 큐를 감시할 스레드를 만들어야 할 것이다. 보통은 이 스레드를 작업자 스레드(WorkerThread)
라 부른다.

_beginthreadex(NULL, 0, WorkerThread, , (LPVOID)hIOCP, 0, NULL);

작업자 스레드까지 생성 했으므로, 실제로 입출력을 할 FileHandle을 만들어야 한다. 우리는 소켓을 통해 입출력을 할 것 이므로 소켓을 생성해 주면 된다. 물론 파일 핸들을 통해서도 네트웍입출력이 가능하긴하 지만 그냥 편하게 소켓을 사용하자.

소켓을 생성할 때 주의할 것은 이 소켓이 Overlapped가 가능한 소켓이어야 한다는 것 이다. 그냥 일반적으로 소켓을 생성하면 Overlapped가 가능한 소켓이 된다. Winsock2의 WSASocket()을 사용하여 처음 소켓을 만들때 Overlapped 인자를 주어 소켓을 생성할 수도 있다.

SOCKET hSocket = WSASocket(AF_INET, SOCKET_STREAM, 0, NULL, WSA_FLAG_OVERLAPPED);

이제 이 소켓을 IOCP에 연결 해 준다. 그럼 이 소켓에서 입출력 완료가 일어났을 때 완료 통보가 완료큐에 들어가게 되고, 그럼 이 완료큐를 감시하고 있던 스레드에서 그 결과를 가져와서 해당 작업을 할 수있게 되는 것 이다.

CreateIoCompletionPort((HANDLE)hSocket, hIOCP, CompletionKey, 0);

IOCP를 만들고, 스레드를 만들었고 소켓을 만들어 IOCP에 등록까지 마쳤다. 이제 소켓을 통해 입출력 작업을 하면 된다. 그러면 입출력 작업이 완료 되었을 때 우리는 그 결과를 가져 올 수 있을 것 이다.


그럼, 입출력이 완료 되었을 때 완료 큐에서 결과를 가져 오는 방법을 알아야 할 것 이다.
GetQueuedCompletionStatus(
 HANDLE CompletionPort,
 LPDWORD lpNumberOfBytes,
PULONG_PTR lpCompletionKey,
 LPOVERLAPPED *lpOverlapped,
 WORD dwMilliseconds
);

완료 큐에서 결과를 가져오는 함수 이다. 대충 인자를 살펴보아도 무슨 역할을 하는 지 알 수 있을 것 이다.

자 이제 이것으로 준비는 모두 끝 났다. 이것을 바탕으로 가장 기본적인 IOCP Echo Server를 만들어 보도록 하자.

'공부 해 Boa요. > Network' 카테고리의 다른 글

Raw Socket  (0) 2006.09.29
Consol Echo Client  (0) 2006.09.27
실전 네트웍 프로그래밍 - 4 - IOCP Echo Server  (0) 2005.06.29
'실전 네트웍 프로그래밍 - 2  (0) 2005.06.29
실전 네트웍 프로그래밍 - 1  (0) 2005.06.29
1. IOCP
IO Completion Port. 우리말로 입출력 완료 포트 정도로 번역(?) 될 수 있는 윈속에 있어 최고의 성능을 보여 주는 모델이다.

흔히 요즘 출판되고 있는 윈도우 네트웍 관련 서적에는 빠지지 않고 그 내용이 포함되어 있다. 허나 정작 쓸만한 구현물을 포함하고 있는 서적은 별로 눈에 띄지는 않는다. 하지만 잘 찾아 보면 찾을 수 있을 지도...

우선 먼저 IOCP에 대해 집고 넘어 가자.

IOCP는 윈도우 NT 3.5 이상의 운영체제(OS)가 제공해 주는 입출력을 작업에 관계된 커널오브젝트다. 즉, 입출력에 관계된 핸들(HANDLE)에서 발생하는 작업완료를 통보해 주는 메커니즘이라 할 수 있다.

우리는 특정 입출력 핸들을 하나 생성하고 이것을 IOCP에 등록해 주게 되면 그후부턴 우리가 등록한 핸들을 통해 입출력이 이루어 질 때 마다 이러한 작업의 결과를 통보받을 수 있게 된다. 물론 이러한 통보는 프로그래밍하는 것이 아니라 OS단에서 처리하게 되므로 여러가지 루틴을 거치지 않고 바로 결과를 통보받게 된다.

'공부 해 Boa요. > Network' 카테고리의 다른 글

Raw Socket  (0) 2006.09.29
Consol Echo Client  (0) 2006.09.27
실전 네트웍 프로그래밍 - 4 - IOCP Echo Server  (0) 2005.06.29
'실전 네트웍 프로그래밍 -3  (0) 2005.06.29
실전 네트웍 프로그래밍 - 1  (0) 2005.06.29
MS Winsock 2를 이용한 네트웍 프로그래밍.

현재까지의 자료를 정리도 할겸 네트웍 프로그래밍에 대한 문서를 만들어 보고자 한다. 이기종 간의 통신을 다루는 것이 아니라 단지 Microsoft Windows Socket 2를 사용한 네트웍 프로그래밍에 대한 내용만을 다룰 것이다.

현재 계획한 바로는 한가지 모델을 기본으로 이 모델을 완성해 나가는 방향으로 진행 하고자 한다. 또한 글의 내용은 기초단계는 그냥 대충 넘어가고  Winsock의 고급기능과 Winsock의 동작 방식에 촛점을 맞추어 보도록 하겠다.

기획 모델.
Chatting Server.

일반적인 체팅 서버라 할 수 있겠으나 동접 2~3만을 커버할 수 있는 대용량 체팅 서버를 궁극적인 목표로 진행 할 것이다.

'공부 해 Boa요. > Network' 카테고리의 다른 글

Raw Socket  (0) 2006.09.29
Consol Echo Client  (0) 2006.09.27
실전 네트웍 프로그래밍 - 4 - IOCP Echo Server  (0) 2005.06.29
'실전 네트웍 프로그래밍 -3  (0) 2005.06.29
'실전 네트웍 프로그래밍 - 2  (0) 2005.06.29

+ Recent posts