이 에코서버는 "윈도우 네트워크 프로그래밍 [한빛미디어, 김선우]"의 책에 있는 내용을 기초로 작성된 소스 입니다.
소스 설명에 앞서… 우선 이 에코 서버의 소스는 이미 약간의 네트워크 프로그래밍 지식이 있다는 전제 하에 설명합니다.
여기서 약간은 서버와 클라이언트의 1:1 TCP 에코 서버를 기준으로 이해를 하신 뒤에 그 이후 과연 1대의 서버로 여러 클라이언트의 접속을 어떻게 할까에 대한의문점을 풀어 들이는데 약간이나마 도움이 될 꺼 같아 작성합니다.
음.. 아래 소스는 class 형식으로 작성되었습니다.
허나! 서버 작성에 class 는 uml 차트를 그리신후 설계등 이런전문적인게 아니면 그냥 C로 작성하는걸 권유하는 편입니다.
이유로는
-
절차적 흐름으로 가는 네트워크 프로그래밍에 굳이 객체 지향을 할 필요가 없다.
-
Class로 설계 하다보면 send, recv 쪽의 소스가 꼬일 가능성이 높다
-
유지보수 할 때 햇갈리기 쉽상이다
로 느껴집니다..
자세히는 이 위의 select server와 비교를 하시면, 왜 class로 하면 안되는지 이해가 가실꺼라 생각합니다.
일단 소스를 들어갑니다.
ws2_32.lib 의 링크를 설정 방법까진 설명하진 않습니다.
NetworkUtil.h // 여기는 윈도우 네트워크 프로그래밍 할 때 반복으로 쓰인 util함수 모음 헤더 |
#include <winsock2.h> // 소켓함수오류출력후종료 // 소켓함수오류출력 // 사용자정의데이터수신함수 |
Server.h |
#include <iostream> DWORD WINAPI ProcessClient(LPVOID arg); // 쓰레드 public: |
Server.cpp |
#include "Server.h" // 파일을 모두 받았으면 해당 클라이언트를 종료하는 함수 // 개별 클라이언트 에게 워드보내기 // 개별 클라이언트의 워드 받기 // 초기화 쓰레드로부터 값 가져오기 // 쓰레드종료시.. // 쓰레드 루트 // 5. 개별 클라이언트 클래스호출... 정의는 해더파일 참고 // 4. // 프로그램 종료시 마무리작업 // 3. 프로그램루프 // 1. 서버의 IP와 들어올 Port 넣어줌. |
음.. 그러면 이 다중 클라이언트의 개별적인 소통 하는건 언제 쓰일까요?
간단히 생각하면 로그인 서버를 예로 들 수 있겠네요.
각각 클라이언트로부터 IP/Password 를 입력 받아 그것을 DB로부터 검색 및 그 결과를 클라이언트에 돌려주는 작업이죠.
그보다 위의 소스는 이해가 되시나요?
Main 함수부터 따라가시면…
-
Server Class 를 생성 하고, 생성자에 초기값으로 IP, Port를 넘겨 서버의 listen 단계까지 처리합니다.
-
그리고 어느 클라이언트로부터 accept 이 들어오면 그 부분에서 쓰레드를 나눠서 만든 쓰레드로 클라이언트와 네트워크 처리 (send, recv)를 하고 원래 루트는 계속 listen 단계를 넣어 다른 클라이언트의 접속을 기다립니다.
클래스로 하다보니 아마 보기 여려울꺼라 생각됩니다.
이 프로그램에 대한 클라이언트 소스는 아래와 같습니다.
Client.h |
#include <iostream> #include "NetworkUtil.h" #define BUFSIZE 4096 using namespace std; class Client { private: int retval; // 여러통신간리턴값을저장하는변수 WSADATA wsa; // 윈속 SOCKET sock; // 클라이언트소켓 SOCKADDR_IN serveraddr; // 소켓구조체 char buf[BUFSIZE]; // 보낼 Text void Send_Proc(); // send 처리 void Recv_Proc(); // recv 처리 public: Client(); // 파일클라이언트connect 작업까지처리 ~Client(); // 기타소켓종료등. 마무리 void ClientProc(); // 구동부분 }; |
Client.cpp |
#include "Client.h" void Client::Send_Proc() { ZeroMemory(buf, BUFSIZE); cout << "보낼내용"; cin >> buf; retval = send(sock, buf, BUFSIZE, 0); if(retval == SOCKET_ERROR) err_quit("send()"); } void Client::Recv_Proc() { ZeroMemory(buf, BUFSIZE); retval = recvn(sock, (char *)&buf, BUFSIZE, 0); if(retval == SOCKET_ERROR) err_quit("recv()"); cout << "서버로부터받은메시지: " << buf << endl; } // 2. // connet 까지일괄처리 // 아래define을주석처리하면ip, port를입력할수있는소스로바뀝니다 #define test Client::Client() { // 윈속초기화 if(WSAStartup(MAKEWORD(2,2), &wsa) != 0) exit(1); // socket() sock = socket(AF_INET, SOCK_STREAM, 0); if(sock == INVALID_SOCKET) err_quit("socket()"); #ifdef test char ip[20] = "127.0.0.1"; int port = 9000; #else char ip[20]; int port; printf("접속하려는서버의IP주소를대시오: "); scanf("%s",ip); printf("접속하려는서버의Port를대시오: "); scanf("%d",&port); #endif // connect() ZeroMemory(&serveraddr, sizeof(serveraddr)); serveraddr.sin_family = AF_INET; serveraddr.sin_port = htons(port); serveraddr.sin_addr.s_addr = inet_addr(ip); retval = connect(sock, (SOCKADDR *)&serveraddr, sizeof(serveraddr)); if(retval == SOCKET_ERROR) err_quit("connect()"); } Client::~Client() { closesocket(sock); // closesocket() WSACleanup(); // 윈속종료 } // 3. 기본루트 void Client::ClientProc() { while(1) { Send_Proc(); Recv_Proc(); } } // 1. int main() { Client Client; Client.ClientProc(); return 0; } |
'리눅스 서버에 대해서' 카테고리의 다른 글
Epoll 채팅 서버 소스 (0) | 2008.05.25 |
---|---|
Select 채팅 서버 (0) | 2008.05.25 |
밑의 epoll 예제가 좀 복잡해서 좀 간단하게 수정함 (0) | 2008.03.29 |
epoll 서버 프로그래밍 (0) | 2008.03.29 |
겜서버 숙제 (0) | 2008.03.24 |