이 에코서버는 "윈도우 네트워크 프로그래밍 [한빛미디어, 김선우]"의 책에 있는 내용을 기초로 작성된 소스 입니다.

소스 설명에 앞서… 우선 이 에코 서버의 소스는 이미 약간의 네트워크 프로그래밍 지식이 있다는 전제 하에 설명합니다.

여기서 약간은 서버와 클라이언트의 1:1 TCP 에코 서버를 기준으로 이해를 하신 뒤에 그 이후 과연 1대의 서버로 여러 클라이언트의 접속을 어떻게 할까에 대한의문점을 풀어 들이는데 약간이나마 도움이 될 꺼 같아 작성합니다.

음.. 아래 소스는 class 형식으로 작성되었습니다.
허나! 서버 작성에 class 는 uml 차트를 그리신후 설계등 이런전문적인게 아니면 그냥 C로 작성하는걸 권유하는 편입니다.

이유로는

  1. 절차적 흐름으로 가는 네트워크 프로그래밍에 굳이 객체 지향을 할 필요가 없다.
  2. Class로 설계 하다보면 send, recv 쪽의 소스가 꼬일 가능성이 높다
  3. 유지보수 할 때 햇갈리기 쉽상이다

로 느껴집니다..

자세히는 이 위의 select server와 비교를 하시면, 왜 class로 하면 안되는지 이해가 가실꺼라 생각합니다.

일단 소스를 들어갑니다.
ws2_32.lib 의 링크를 설정 방법까진 설명하진 않습니다.


NetworkUtil.h // 여기는 윈도우 네트워크 프로그래밍 할 때 반복으로 쓰인 util함수 모음 헤더

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

// 소켓함수오류출력후종료
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 recvn(SOCKET s, char *buf, int len, int flags)
{
    int received;
    char *ptr = buf;
    int left = len;
    while(left > 0){
        received = recv(s, ptr, left, flags);
        if(received == SOCKET_ERROR)
            return SOCKET_ERROR;
        else if(received == 0)
            break;
        left -= received;
        ptr += received;
    }
    return (len - left);
}


Server.h

#include <iostream>
#include "NetworkUtil.h"
using namespace std;
#define BUFSIZE 4096

DWORD WINAPI ProcessClient(LPVOID arg);     // 쓰레드
class Cilent_Connect
{
private:
    int retval;
    SOCKET client_sock;          // 접속하는 클라이언트의 소켓
    SOCKADDR_IN clientaddr;      // 접속하는 클라이언트의 구조체
    char buf[BUFSIZE];           // 버퍼
    void Client_Disconnect();    // 클라이언트를 종료하는 함수
    bool Send_Proc();            // 클라이언트에서 받은 Text 보내기
    bool Recv_Proc();            // 클라이언트로부터 Text 받기

public:
    Cilent_Connect(SOCKET client);
    ~Cilent_Connect();
    bool Server_Proc();
};

class
Server
{
private:
    int retval;                 // 공용변수ㅡㅡ.. 대략 어느함수의 리턴값에 활용
    WSADATA wsa;                // 윈소켓
    SOCKADDR_IN serveraddr;     // 서버소켓의 구조체
    SOCKET listen_sock;         // 대기중인 서버 소켓
    SOCKET client_sock;         // 접속하는 클라이언트의 소켓
    SOCKADDR_IN clientaddr;     // 접속하는 클라이언트의 구조체
    bool Client_Accept();       // 클라이언트 접근시 처리 함수

public:
    Server(char *ip, int port);    // socket 선언에서listen 단계까지 일괄처리단계
    ~Server();                     // 프로그램 종료시 마무리작업
    void Server_Loop();            // 서버루프
};



Server.cpp

#include "Server.h"

// 파일을 모두 받았으면 해당 클라이언트를 종료하는 함수
void Cilent_Connect::Client_Disconnect()
{
    // closesocket()
    closesocket(client_sock);
    printf("클라이언트: IP 주소=%s, 포트번호=%d의접속이끊김\n",
        inet_ntoa(clientaddr.sin_addr), ntohs(clientaddr.sin_port));
}

// 개별 클라이언트 에게 워드보내기
bool Cilent_Connect::Send_Proc()
{
    retval = send(client_sock, (char *)&buf, BUFSIZE, 0);
    if(retval == SOCKET_ERROR)
    {
        err_display("send()");
        closesocket(client_sock);
        return false;
    }        
    return true;
}

// 개별 클라이언트의 워드 받기
bool Cilent_Connect::Recv_Proc()
{
    // 대화 워드 받기
    ZeroMemory(buf, BUFSIZE);
    retval = recvn(client_sock, buf, BUFSIZE, 0);
    if(retval == SOCKET_ERROR)
    {
        err_display("recv()");
        closesocket(client_sock);
        return false;
    }
    printf("[Client IP = %s, port = %d] : %s\n",inet_ntoa(clientaddr.sin_addr),
                                               ntohs(clientaddr.sin_port), buf);
    return true;
}

// 초기화 쓰레드로부터 값 가져오기
Cilent_Connect::Cilent_Connect(SOCKET client)
{
    client_sock = client;
    int addrlen = sizeof(clientaddr);
    getpeername(client_sock, (SOCKADDR*)&clientaddr, &addrlen);
}

// 쓰레드종료시..
Cilent_Connect::~Cilent_Connect()
{
    Client_Disconnect();        
}

// 쓰레드 루트
bool Cilent_Connect::Server_Proc()
{
    while(1)
    {
        if(!Recv_Proc())            
            break;
        if(!Send_Proc())    
            break;
    }
    cout << "접속이종료됨." << endl;
    return true;
}

// 5. 개별 클라이언트 클래스호출... 정의는 해더파일 참고
DWORD WINAPI ProcessClient(LPVOID arg)
{
    Cilent_Connect Idle((SOCKET)arg);
    Idle.Server_Proc();
    return 0;
}

// 4.
bool Server::Client_Accept()            
{
    int addrlen;        // 의미없음ㅡㅡ.. 단순주소길이저장변수
    addrlen = sizeof(clientaddr);
    client_sock = accept(listen_sock, (SOCKADDR *)&clientaddr, &addrlen);
    if(client_sock == INVALID_SOCKET)
    {
        err_display("accept()");
        return false;
    }
    HANDLE hThread;
    DWORD ThreadId;
    printf("\n클라이언트접속: IP 주소=%s, 포트번호=%d\n",
        inet_ntoa(clientaddr.sin_addr), ntohs(clientaddr.sin_port));
    // 스레드생성
    hThread = CreateThread(NULL, 0, ProcessClient, (LPVOID)client_sock, 0,
                          ThreadId);
    if(hThread == NULL)
        printf("[오류] 스레드생성실패!\n");
    else
        CloseHandle(hThread);
    return true;
}

// 2. socket 선언에서listen 단계까지일괄처리단계
Server::Server(char *ip, int port)        // IP주소와port번호는따로입력받는다.
{
    cout << "******** 서버프로그램시작********\n";
    cout << "멀티스레드를 적용해서 여러 클라이언트의 말을 되돌려 주는 서버\n";
     
    // 윈속초기화
    if(WSAStartup(MAKEWORD(2,2), &wsa) != 0)
        exit(1);
   
    // 소켓정의, TCP사용임
    listen_sock = socket(AF_INET, SOCK_STREAM, 0);
    if(listen_sock == INVALID_SOCKET) err_quit("socket()");    
   
    // bind()        
    ZeroMemory(&serveraddr, sizeof(serveraddr));
    serveraddr.sin_family = AF_INET;
    serveraddr.sin_port = htons(port);
    serveraddr.sin_addr.s_addr = inet_addr(ip);
    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()");
}

// 프로그램 종료시 마무리작업
Server::~Server()
{
    closesocket(listen_sock);        // closesocket()
    WSACleanup();                // 윈속종료
}

// 3. 프로그램루프
void Server::Server_Loop()
{
    while(1)
    {
        if(!Client_Accept())        // accept 처리
            continue;
    }
}

// 1. 서버의 IP와 들어올 Port 넣어줌.
int main()
{
    Server process("127.0.0.1",9000);
    process.Server_Loop();
    return 0;
}


음.. 그러면 이 다중 클라이언트의 개별적인 소통 하는건 언제 쓰일까요?

간단히 생각하면 로그인 서버를 예로 들 수 있겠네요.

각각 클라이언트로부터 IP/Password 를 입력 받아 그것을 DB로부터 검색 및 그 결과를 클라이언트에 돌려주는 작업이죠.


그보다 위의 소스는 이해가 되시나요?

Main 함수부터 따라가시면…

  1. Server Class 를 생성 하고, 생성자에 초기값으로 IP, Port를 넘겨 서버의 listen 단계까지 처리합니다.
  2. 그리고 어느 클라이언트로부터 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
쓰레드 에코서버  (0) 2008.05.25
밑의 epoll 예제가 좀 복잡해서 좀 간단하게 수정함  (0) 2008.03.29
epoll 서버 프로그래밍  (0) 2008.03.29
겜서버 숙제  (0) 2008.03.24

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

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

작업은 VC 2005에서 함

Jlet에서의 encodeImage 란 함수가 있습니다.

Graphics 밑에 딸려 나온 것으로 역할은 이미지 데이터를 byte [] 뽑아줍니다.
음... 사용법에 대해선 전에 주의점으로 소개를 했지만 다시 써봅니다.

public static Image CopyImage(Image src)
{
  // 지금 들어오는 src를 바로  그래픽 받아오게 하면, 읽기 전용상태라 NullPointException이 발생한다
  // 이를 방지하기위해 Image copy본을 만든뒤 그 이미지를 encodeImage 하면된다.

  byte []temp = null;
  Image copy = Image.createImage(src);
  Graphics g = copy.getGraphics();
 
  temp = g.encodeImage(0, 0, src.getWidth(), src.getHeight());
  Image result = Image.createImage(temp, 0, temp.length);
 
  return result;
 }

C언어와 달리 들어오는 Image src 에 대해서 바로 접근이 안됩니다.
C언어라면, 함수가 호출되면서 Image src의 복사본이 오게 되겠지만, Java 문법상.. (아니 Wipi만 그런가?)
C 언어로 치면 Image CopyImage(const Image &src) 와 같이 들어오는거 같습니다.

참 비효율적이라고 생각 됩니다. 아니.. 내공이 부족한건지.. 하여간 윗 소스는
1. 받아온 Image의 Copy 를 생성한다.
2. 그 복사본 이미지에 대한 그래픽을 만든다.
3. byte [] temp 라는 배열 안에 복사본 이미지 전체 영역, (0,0)~(width, height)만큼 인코딩 시킨다
4. 가져온 temp byte 배열을 가지고 이미지를 생성한다. 생성하되, 시작 번지는 0 ~ temp 길이 만큼임

으로 생각하시면 됩니다..


왜 이렇게 하는건지는 잘 모르겠지만. 뭐 wipi가 그러니 그려러니 하는 수밖에 없죠.. 왠지 알면 알수록
꼬인다는 느낌이 Wipi입니다.. orz

하여간 이 encodeImage 저는 이미지에 대해 변화를 많이 주는 프로세싱을 하다 보니,
일반적으로
1. GetPixel 로 좌표의 RGB 알아낸다.
2. 이 RGB를 적당한 알고리즘으로 변형시킨다.
3. SetColor 로 RGB값을 지정한다.
4. SetPixel로 해당 좌표에 점을 찍는다…

를 하기엔, 퍼포먼스가 너무 딸리게 됩니다. 돌아가는 환경이 PC로 치면 386,486급을 달리고 있고,
거기에 어셈만큼 빠른 C언어가 아닌 Java… 버추얼 PC를 통해서 느린 판국에 Class 개념의 객체 지향은
일반 C언어의 순차적 언어에 비하면 많이 느리죠
(모바일 경우.. 뭐 어떻게 뜯어 고쳐서 Jlet도 Clet에 비하면 80%정도 성능을 낸다고 합니다만.. 음…)

하여간, MFC의 (비교대상이 되려나..) GetPixel, SetPixel 를 사용하면 최신 듀얼 CPU 컴퓨터도 버벅이는 상황에서
모바일이라고 별수 있나요.. 그래서 GetPixel, SetPixel 을 사용하지 않고 이것을 배열에 다 넣은뒤
이 배열 값을 조작한 다음 다시 나중에 이미지 만들면 되겠다 싶어서 encodeImage에 대해 파게 됬습니다.. orz

우선 들어가기 앞서 Wipi Jlet API 에선 뭐라고 설명하는지 살펴 봅니다.

encodeImage
public byte[] encodeImage(int x, int y, int w, int h)

화면의 특정 영역을 BMP 포맷으로 인코딩한다. 인코딩된 BMP 포맷은 바이트 어레이로 반환되며 파일로 저장하거나 다시 이미지 생성에 이용할 수도 있다.

Parameters:
x - 인코딩할 영역의 시작 x 좌표
y - 인코딩할 영역의 시작 x 좌표
w - 인코딩할 영역의 폭
h - 인코딩할 영역의 폭

Returns:
인코딩된 BMP 포맷의 바이트어레이, 인코딩에 실패하면 null

Throws:
java.lang.IllegalArgumentException - 대상 영역의 일부가 Graphics의 범위를 벗 어나는 경우 또는 w, h가 음수인 경우

출처 : http://www.developerzone.co.kr/release/wipi/SKT_WIPI_JAVA2.0_API/org/kwis/msp/lcdui/Graphics.html#encodeImage(int,%20int,%20int,%20int)

음….. 화면 특정 영역을 BMP포멧으로 인코딩이라고 되있습니다.
여기서 햇갈리는게.. BMP포멧이 뭘까요?
BMP 포멧에 대해선 BMP 구조에 대해 알아야 하는데, 일반적인 BMP는 MS에서 만든 그거겠죠… 라고 생각하다가 조금 고생했습니다..
우선 BMP가 크게 header + (8bit 이하 그림이면) palettle +pixel data 로 구성 되있죠.

처음엔 그냥 BMP 포멧이라 하길래 BGR 값으로 byte 가 쫙~~~~~~~~~ 뽑힐줄 알았는데…
앞에 Header가 있더군요. Orz 일단 Header size 부터 알아냅시다..

System.out.println(src.getWidth()+"x" + src.getHeight() + "Imaghe bytes are " + temp.length);

System.out.println("pixel total number : " + src.getWidth()* src.getHeight());

System.out.println("header size : " + (temp.length - (src.getWidth() * src.getHeight()*3)));

System.out.println();

위 소스를 보면 이미지의 넓이, 높이에 대한 인코딩 추출 byte 길이가 나오고,
실제 이미지에 쓰인 픽셀이 몇 개인지 (해상도라고 하죠 보통..) 출력하고
header 사이즈에 대해 출력됩니다.

저기에 보면 temp 전체 길이에 대해서 픽셀 숫자의 3배수를 뺍니다.
여기서 혼동이 되는게 BMP 포맷은, 1픽셀을 4byte 로 표현합니다, AABBGGRR 로 구성되어있죠
하지만 Wipi 상 BMP 포맷은 RRGGBB 3byte 입니다.

가끔 MSDN 이 좋다고 느껴지는게, 조금 복잡하거나 생소한 API경우 예제 소스를 같이 포함해서 사용법에 대해 나와있는데
Wipi경우 이건 뭐 API만 쑥 던져주고 알아서 삽질하셈 인건지 뭔지… orz

하여간 저렇게 해서 header size를 알아냈으니 그 이후 픽셀부터 수정하면 될까요…?
정답은 꼭 그렇지는 않다 입니다..

허나 대부분은 저렇게 해서 나온 size 에서 + a 지점부터 x,y좌표 계산한 번지의 byte 숫자를 바꾸면 이미지 색이 바뀜니다.
alpha 값은… 저도 많이 삽질했지만 특별한 규칙이 있는건지 없는건지;;;
alpha 값은… header + 특정변수 의 변경값이 0,0의 픽셀을 변하는 곳이.. alpha값입니다.. (이 뭐 무책임한.. orz)
즉, 결론은... 저런거 삽질 할 시간에 차라리 int 같은 배열로 뽑아서 관리 하는게 훨~~~ 씬 편합니다.
int 로 어떻게 뽑냐면..
static int[] CraeteArrayImage(Image srcImg)
 {
      Image copyImg = Image.createImage(srcImg);
      Graphics g = copyImg.getGraphics();
   
      int[] arrayImg = new int[srcImg.getWidth()*srcImg.getHeight()];
 
      int bpp = 32;// getDisplay().getBitsPerPixel();
      int bpl = (srcImg.getWidth() * bpp + 7) / 8;
      g.getRGBPixels(0, 0, srcImg.getWidth(), srcImg.getHeight(), arrayImg, 0, bpl);
      return arrayImg;
 }
이렇게 해주시거나..
아니면...

static int[] CraeteArrayImage(Image srcImg)
 {
      Image copyImg = Image.createImage(srcImg);
      Graphics g = copyImg.getGraphics();
   
      int[] arrayImg = new int[srcImg.getWidth()*srcImg.getHeight()];
      int cnt = 0;
      for(int y=0; y < copyImg.getHeight(); ++y)
      {
           for(int x=0; x < copyImg.getWidth(); ++x)
          {
                 arrayImg[cnt++] = g.getPixel(x, y);
           }
       }
 
       return arrayImg;
 }
물론... int 는 4byte... 그림 RGB만 차지하는 byte는 3byte 이므로 불필요한 공간 25%가 더 쌓이는 코드입니다.
저걸 byte로 바꿔서 코딩하면.. 금방 될껍니다 ^^;

// 하앍 include 가 넘 많다...
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/epoll.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <errno.h>

#define EPOLL_SIZE 60
#define EPOLL_EVENT_SIZE 100
#define OPENPORT 5000

// main
int
main (int argc, char **argv)
{
 struct epoll_event *events;
 struct epoll_event ev;
 struct sockaddr_in addr, clientaddr;
 int clilen;
 int sfd, efd, cfd;
 int i;
 int max_got_events;
 int result;
 int readn;
 int sendflags = 0;
 char buf_in[256] = { '\0' };

 if ((efd = epoll_create (EPOLL_EVENT_SIZE)) < 0)
 {
  perror ("epoll_create (1) error");
  return -1;
 }
 
 // init pool
 events = (struct epoll_event *) malloc (sizeof (*events) * EPOLL_SIZE);
 if (NULL == events)
 {
  perror ("epoll_create (0) error");
  return -1;
 }

 clilen = sizeof (clientaddr);
 sfd = socket (AF_INET, SOCK_STREAM, 0);
 if (sfd == -1)
 {
  perror ("socket error :");
  close (sfd);
  return -1;
 }

 addr.sin_family = AF_INET;
 addr.sin_port = htons (OPENPORT);
 addr.sin_addr.s_addr = htonl (INADDR_ANY);
 if (bind (sfd, (struct sockaddr *) &addr, sizeof (addr)) == -1)
 {
  close (sfd);
  return -2;
 }
 listen (sfd, 5);
 
 if (sfd < 0)
 {
  perror ("init_acceptsock error");
  return 1;
 }

 printf("Running Server port %d\n",port);
 ev.events = EPOLLIN;
 ev.data.fd = sfd;
 result = epoll_ctl (efd, EPOLL_CTL_ADD, sfd, &ev);
 if (result < 0)
 {
  perror ("epoll_ctl error");
  return 1;
 }

 while (1)
 {
  max_got_events = epoll_wait (efd, events, EPOLL_SIZE, -1);
  for (i = 0; i < max_got_events; i++)
  {
   if (events[i].data.fd == sfd)
   {
    printf("Accepted\n");
    cfd = accept (sfd, (struct sockaddr *) &clientaddr, &clilen);
    if (cfd < 0)
    {
     perror ("Accept error");
     return -1;
    }

    ev.events = EPOLLIN;
    ev.data.fd = cfd;
    epoll_ctl (efd, EPOLL_CTL_ADD, cfd, &ev);
   }
   else
   {
    cfd = events[i].data.fd;

    memset (buf_in, 0x00, 256);
    readn = read (cfd, buf_in, 255);
    // if it occured ploblems with reading, delete from epoll event pool and close socket
    if (readn <= 0)
    {
     epoll_ctl (efd, EPOLL_CTL_DEL, cfd, &ev);
     close (cfd);
     printf ("Close fd ", cfd);
    }
    else
    {
     printf ("%s", buf_in);
     send (cfd, buf_in, strlen (buf_in), sendflags);
    }

   }
  }
 }
 return 1;
}

'리눅스 서버에 대해서' 카테고리의 다른 글

Epoll 채팅 서버 소스  (0) 2008.05.25
Select 채팅 서버  (0) 2008.05.25
쓰레드 에코서버  (0) 2008.05.25
밑의 epoll 예제가 좀 복잡해서 좀 간단하게 수정함  (0) 2008.03.29
epoll 서버 프로그래밍  (0) 2008.03.29
겜서버 숙제  (0) 2008.03.24

+ Recent posts