태터데스크 관리자

도움말
닫기
적용하기   첫페이지 만들기

태터데스크 메시지

저장하였습니다.

티스토리 툴바



2009/04/23 17:55

스레드를 이용한 Server


#include<winsock2.h>

#include<stdio.h>

#include<stdlib.h>

 

#define BUFSIZE 512

 

DWORD WINAPI ProcessClient(LPVOID arg);

 

///////////// 소켓 함수 오류 출력 후 종료 //////////////

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);

}

 

int main(int argc, char* argv[])

{

           int retval;

          

           /////////////// 윈속 초기화 //////////////

           WSADATA wsa;

           if(WSAStartup(MAKEWORD(2,2), &wsa) != 0) //WSAStartup 성공:0, 실패:오류코드 이며, &wsa WSADATA타입 변수의 주소

                                                                                                          //makeword는 주어진 상위, 하위 바이트를 조합하여 word 값을 반환합니다.                

                     return -1;

          

           /////////////// 1.socket /////////////

           SOCKET listen_sock=socket(AF_INET,SOCK_STREAM,0); //socket(주소체계, 소켓 타입, 프로토콜)

           if(listen_sock==INVALID_SOCKET) err_quit("socket()"); //listen_sock의 리턴값이 INVALID_SOCKET이면 err_quit함수 호출

          

           /////////////////// 2.bind /////////////////////

           SOCKADDR_IN serveraddr;  /*     struct sockaddr_in {

                                                     short   sin_family;

                                                     u_short sin_port;

                                                     struct  in_addr sin_addr;

                                                     char    sin_zero[8];

                                                     };

                                          */

           ZeroMemory(&serveraddr,sizeof(serveraddr)); //ZeroMemory는 구조체나 변수를 초기화, serveraddr의 구조체 변수를 초기화

           serveraddr.sin_family=AF_INET;

           serveraddr.sin_port=htons(9090);  //htons host byte order network byte order Port정보의 바이트 순서변경

           serveraddr.sin_addr.s_addr=htonl(INADDR_ANY); 

          

           retval=bind(listen_sock, (SOCKADDR*)&serveraddr, sizeof(serveraddr)); /* int bind (

                                                                                               SOCKET s,

                                                                                               const struct sockaddr* name,

                                                                                               int namelen

                                                                                      ); 성공:0, 실패:SOCKET_ERROR

           */         //bind()는 서버의 지역IP주소와 지역 포트 번호를 결정된다. 성공:0, 실패:SOCKET_ERROR

                     //listen_sock, ip주소, 포트번호

           if(retval==SOCKET_ERROR) err_quit("bind()"); //int형 변수retval SOCKET_ERROR 이면 err_quit함수 호출

          

           //////////////////// 3.listen ///////////////////

           retval=listen(listen_sock, SOMAXCONN);  /* listen(        

                                                                SOCKET s,

                                                                int backlog);   성공:0, 실패:SOCKET_ERROR

                                                     */

           if(retval==SOCKET_ERROR) err_quit("listen()");  //retval SOCKET_ERROR이면 err_quit()함수 실행

          

           // 데이터 통신에 사용할 변수

           SOCKET client_sock;

           SOCKADDR_IN clientaddr;

           int addrlen;

          

           DWORD lpThread = 0; 

 

           while(1){

                     //////////////////// 4.accept //////////////////

                     //클라이언트 접속 수용 1

                     addrlen=sizeof(clientaddr);

                     client_sock=accept(listen_sock, (SOCKADDR*)&clientaddr, &addrlen);  /* SOCKET accept(

                                                                                                          SOCKET s,

                                                                                                          struct sockaddr* addr,

                                                                                                          int* addrlen

                                                                                               );   성공:새로운 소켓, 실패:INVALID_SOCKET

                                                                                                */

                     if(client_sock==INVALID_SOCKET){     //client_sock INVALID_SOCKET이면 err_display()함수 실행

                                err_display("accept()");

                                continue;            //다시 while(1)문으로

                     }

                     //스레드 생성 2

                     CreateThread(NULL, 0, ProcessClient, (LPVOID)client_sock, 0, &lpThread); 

                                                     /*  HANDLE CreateThread (

                                                                  LPSECURITY_ATTRIBUTES lpThreadAttributes, // NULL

                                                                  SIZE_T dwStackSize, // 0

                                                                  LPTHREAD_START_ROUTINE lpStartAddress, // 스레드 함수

                                                                  LPVOID lpParameter, // 스레드 함수 인자

                                                                  DWORD dwCreationFlags, // 0 또는 CREATE_SUSPENDED

                                                                  LPDWORD lpThreadId // 스레드 ID

                                                         ) ;   성공: 스레드 핸들, 실패: NULL

                                                                                                                                                                                                                      */

           }

 

           /////////////// 7.closesocket() ////////////////

           closesocket(listen_sock);  //listen_sock 소켓을 닫음

 

    // 윈속 종료

    WSACleanup();  //성공:0, 실패:SOCKET_ERROR

    return 0;

}

 

DWORD WINAPI ProcessClient(LPVOID arg)

{

           SOCKADDR_IN clientaddr;

           int addrlen;

           int retval;

           char buf[BUFSIZE+1];

 

           //전달된 소켓 3

           SOCKET client_sock = (SOCKET)arg;

 

           //클라이언트 정보 얻기 4

           addrlen = sizeof(clientaddr);

           getpeername(client_sock, (SOCKADDR *)&clientaddr, &addrlen);

 

           printf("\n[TCP 서버] 클라이언트 접속: IP 주소=%s, 포트 번호=%d\n", inet_ntoa(clientaddr.sin_addr), ntohs(clientaddr.sin_port));

           //클라이언트와 데이터 통신 5

           while(1){

                                ////////////////// 5.recv ///////////////////

                     retval=recv(client_sock, buf, BUFSIZE, 0);   /* int recv (

                                                                                     SOCKET s,

                                                                                     char* buf,

                                                                                     int len,

                                                                                     int flags

                                                                          ); 성공: 받은 바이트 수 또는 0(연결 종료시), 실패: SOCKET_ERROR

                                                                           수신 버퍼에 도착한 데이터를 애플리케이션 버퍼로 복사

                                                                      */

                     if(retval==SOCKET_ERROR){

                                err_display("recv()");

                                break;

                                }

                     else if(retval==0)

                                break;

                    

                     //받은 데이터 출력

                     buf[retval] = '\0';   //buf[retval] \0 으로 초기화

                     printf("[TCP/%s:%d] %s\n", inet_ntoa(clientaddr.sin_addr), ntohs(clientaddr.sin_port), buf);

                    

                     /////////////// 6.send ////////////////

                     retval = send(client_sock, buf, retval, 0);   /*  int send (

                                                                                       SOCKET s,

                                                                                       const char* buf,

                                                                                       int len,

                                                                                       int flags

                                                                            ); 성공: 보낸 바이트 수, 실패: SOCKET_ERROR

                                          애플리케이션 데이터를 송신 버퍼에 복사함으로써 궁극적으로 하부 프로토콜에 의해 데이터가 전송

                                            */

                     if(retval == SOCKET_ERROR){

                                err_display("send()");

                                break;

                     }

           }

          

           // closesocket()

           closesocket(client_sock);

          

           return 0;

}

Trackback 0 Comment 0