본문 바로가기

프로그래밍/C++

[C++ / 소켓프로그래밍] linux TCP socket server

예제 소스 찾아 헤매다 결국 직접 짜버렸다.

리눅스 소켓서버

다중 접속 (concurrent) 서버는 차후에...


centOS 7 64bit

Eclipse + CDT(C/C++ Development Toolkit)

//============================================================================
// Name        : linuxsrv.cpp
// Author      : jhjung
// Version     : 1.0
// Copyright   : Your copyright notice
// Description : Hello World in C++, Ansi-style
//============================================================================
#include <iostream>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <cstring>
#include <arpa/inet.h>

// 최대 접속대기 클라이언트 수
const int MAXCONNECTIONS = 5;

// 대기 버퍼 크기
const int MAXWAITBUFSIZE = 4096;

// SEND용 버퍼의 크기
const int MAXSENDBUFSIZE = 1024;

// RECV용 버퍼의 크기
const int MAXRECEIVEBUFSIZE = 1024;

// 메세지 타입
#define ODSP_COMMAND 1
#define ODSP_REQUEST 2
#define ODSP_ACK 3
#define ODSP_ECHO 4
#define ODSP_END 5

// 메세지 헤더
struct ODSP_HDR
{
    unsigned int msgType;
    unsigned int msgLen;
};

using namespace std;


int main ()
{
    // 버퍼에서 헤더를 분석할 순서 인지를 판별하기위한 변수
    bool isHeader = true;

    // 보낸 데이터 길이
    unsigned int sendByteLen;

    // 받은 데이터, 보낸 데이터 바이트 길이를 받는 변수
    unsigned int byteLen;

    // 데이터를 처리할 수 있는 길이가 되었는지 확인용 변수
    // 현재 대기 버퍼안에 있는 데이터의 길이를 저장
    unsigned int curLen;

    // listen socket file discriptor
    int listenSockFD;

    // client socket file discriptor
    int clientSockFD;

    // 버퍼의 인덱스 포인터
    char *ptrRecvBufIdx = 0;

    // data 정리용 포인터
    char *ptrDataSortingIdx = 0;

    // send buf 용 인덱스 포인터
    char *ptrSendBufIdx = 0;

    // RECV용 버퍼 선언
    char buf[MAXRECEIVEBUFSIZE];

    // SEND용 버퍼 선언 (헤더 + 메세지)
    char bufSend[MAXSENDBUFSIZE];

    // message SEND용 버퍼 선언
    char bufSendMsg[MAXSENDBUFSIZE];

    // 시스템용 대기 버퍼 선언
    char bufWait[MAXWAITBUFSIZE];

    // 헤더 처리 버퍼
    char bufHdr[MAXWAITBUFSIZE];

    // 메세지 처리 버퍼
    char bufMsg[MAXWAITBUFSIZE];

    // 대기 버퍼 스왑용 버퍼
    char bufTemp[MAXWAITBUFSIZE];

    string Msg = "";

    // 소켓 주소 구조체 선언
    sockaddr_in server_addr, client_addr;

    // 송신용 메세지 헤더 구조체 선언
    ODSP_HDR hdr;

    // 메세지 헤더 구조체 포인터 선언
    ODSP_HDR *recv_hdr;

    // listen socket 생성
    listenSockFD = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

    if(listenSockFD < 0)
    {
        cout << endl << "socket create error" << endl;
        return 0;
    }

    // 소켓 초기화
    int on = 1;
    if(setsockopt(listenSockFD, SOL_SOCKET, SO_REUSEADDR, (const char*) &on, sizeof(on)) < 0)
    {
        cout << endl  << "set option curLen = 0;화error!!" << endl;
        return 0;
    }

    // 서버에서 연결을 수락할 IP 주소 설정
    // INADDR_ANY -> 모든 주소 허용
    server_addr.sin_addr.s_addr = INADDR_ANY;

    // AF_INET ->  ipv4
    server_addr.sin_family = AF_INET;

    // 포트 셋팅
    server_addr.sin_port = htons(30000);

    // listen socket에 주소 할당
    if(bind(listenSockFD, (struct sockaddr *) &server_addr, sizeof(server_addr)) < 0)
    {
        cout << endl << "bind error" << endl;
        return 0;
    }

    cout << "server running. waiting client..." << endl;

    // passive 대기 상태로 client의 접속을 대기
    // client 에서 connect를 통해 접속 시도 시 3-way handshake 가 일어남
    if(listen(listenSockFD, MAXCONNECTIONS) < 0)
    {
        cout << endl << "listen error" << endl;
        return 0;
    }

    int clientAddrSize =  sizeof(client_addr);

    while(true)
    {
        // 대기버퍼에 있는 데이터 길이 초기
        curLen = 0;

        // 대기 버퍼 초기화
        memset(bufWait, 0, MAXWAITBUFSIZE);

        // 버퍼 포인터 초기화
        // 대기 버퍼의 시작 주소를 가진다.
        ptrRecvBufIdx = bufWait;

        // client에 대해 허용
        clientSockFD = accept(listenSockFD, (struct sockaddr *) &client_addr, (socklen_t *) &clientAddrSize);

        if(clientSockFD < 0)
        {
            cout << endl << "accept error" << endl;
            return 0;
        }

        cout << endl << "client accepted" << endl;
        cout << "address : " << inet_ntoa(client_addr.sin_addr) << endl;
        cout << "port : " << ntohs(client_addr.sin_port) << endl;

        // data 수신 처리
        while(true)
        {
            // receive 버퍼 초기화
            memset(buf, 0, MAXRECEIVEBUFSIZE);

            // data를 가져옴
            // 최대 버퍼 사이즈 만큼의 데이터를 가져와서 buf에 저장
            // recv 함수는 가져온 byte의 길이를 return
            byteLen = recv(clientSockFD, buf, MAXRECEIVEBUFSIZE, 0);

            if(byteLen == 0)
            {
                cout << endl << "client " << inet_ntoa(client_addr.sin_addr) << " closed." << endl;
                close(clientSockFD);
                break;
            }

            if(byteLen > MAXRECEIVEBUFSIZE)
            {
                cout << endl << "client " << inet_ntoa(client_addr.sin_addr) << " closed." << endl;
                close(clientSockFD);
                break;
            }

            if(byteLen > 0)
            {
                memcpy(ptrRecvBufIdx, buf, byteLen);
                curLen += byteLen;
                ptrRecvBufIdx += byteLen;
                byteLen = 0;

                // 헤더를 분석할 순서
                if(isHeader)
                {
                    // 헤더 길이 이상의 data가 있는지 확인
                    if(curLen >= sizeof(recv_hdr))
                    {
                        memset(bufHdr, 0, sizeof(bufHdr));
                        memcpy(bufHdr, bufWait, sizeof(recv_hdr));
                        bufHdr[sizeof(recv_hdr)] = '\0';
                        recv_hdr = (ODSP_HDR *)bufHdr;

                        // 버퍼의 data를 정리
                        ptrDataSortingIdx = bufWait + sizeof(recv_hdr);
                        memset(bufTemp, 0, sizeof(bufTemp));
                        memcpy(bufTemp, ptrDataSortingIdx, sizeof(bufWait) - sizeof(recv_hdr));
                        memset(bufWait, 0, sizeof(bufWait));
                        memcpy(bufWait, bufTemp, sizeof(bufTemp));

                        curLen -= sizeof(recv_hdr);
                        ptrRecvBufIdx -= sizeof(recv_hdr);
                        isHeader = false;
                    }
                }

                if(!isHeader)
                {
                    // 분석된 헤더의 메세지 길이 정보를 통해 현재 버퍼에 메세지 길이 이상의 data가 있는지 확인
                    if(curLen >= recv_hdr->msgLen)
                    {
                        memset(bufMsg, 0, sizeof(bufMsg));
                        memcpy(bufMsg, bufWait, recv_hdr->msgLen);
                        bufMsg[recv_hdr->msgLen] = '\0';

                        // 버퍼의 data를 정리
                        ptrDataSortingIdx = bufWait + recv_hdr->msgLen;
                        memset(bufTemp, 0, sizeof(bufTemp));
                        memcpy(bufTemp, ptrDataSortingIdx, sizeof(bufWait) - recv_hdr->msgLen);
                        memset(bufWait, 0, sizeof(bufWait));
                        memcpy(bufWait, bufTemp, sizeof(bufTemp));

                        curLen -= recv_hdr->msgLen;
                        ptrRecvBufIdx -= recv_hdr->msgLen;
                        isHeader = true;

                        if(recv_hdr->msgType == ODSP_COMMAND)
                        {
                            cout << endl << "received command message" << endl;
                            cout << "message length is " << recv_hdr->msgLen << "byte" << endl;
                            cout << "receive message : " << bufMsg << endl;

                            Msg = "received command message";
                            strcpy(bufSendMsg, Msg.c_str());

                            memset(&hdr, 0, sizeof(hdr));
                            hdr.msgType = ODSP_ACK;
                            hdr.msgLen = Msg.length();

                            ptrSendBufIdx = bufSend;
                            memcpy(ptrSendBufIdx, &hdr, sizeof(hdr));
                            ptrSendBufIdx = ptrSendBufIdx + sizeof(hdr);
                            memcpy(ptrSendBufIdx, bufSendMsg, hdr.msgLen);

                            sendByteLen = send(clientSockFD, bufSend, sizeof(hdr) + hdr.msgLen, 0);

                            if(sendByteLen < 0)
                            {
                                cout << endl << "send error" << endl;
                                return 0;
                            }
                        }
                        else if(recv_hdr->msgType == ODSP_REQUEST)
                        {
                            cout << endl << "received request message" << endl;
                            cout << "message length is " << recv_hdr->msgLen << "byte" << endl;
                            cout << "receive message : " << bufMsg << endl;
                        }
                        else if(recv_hdr->msgType == ODSP_ACK)
                        {
                            cout << endl << "received ack message" << endl;
                            cout << "message length is " << recv_hdr->msgLen << "byte" << endl;
                            cout << "receive message : " << bufMsg << endl;
                        }
                        else if(recv_hdr->msgType == ODSP_ECHO)
                        {
                            cout << endl << "received echo message" << endl;
                            cout << "message length is " << recv_hdr->msgLen << "byte" << endl;
                            cout << "receive message : " << bufMsg << endl;

                            Msg = bufMsg;
                            strcpy(bufSendMsg, Msg.c_str());

                            memset(&hdr, 0, sizeof(hdr));
                            hdr.msgType = ODSP_ECHO;
                            hdr.msgLen = recv_hdr->msgLen;

                            ptrSendBufIdx = bufSend;
                            memcpy(ptrSendBufIdx, &hdr, sizeof(hdr));
                            ptrSendBufIdx = ptrSendBufIdx + sizeof(hdr);
                            memcpy(ptrSendBufIdx, bufSendMsg, hdr.msgLen);

                            sendByteLen = send(clientSockFD, bufSend, sizeof(hdr) + hdr.msgLen, 0);

                            if(sendByteLen < 0)
                            {
                                cout << endl << "send error" << endl;
                                return 0;
                            }
                        }
                        else if(recv_hdr->msgType == ODSP_END)
                        {
                            cout << endl << "received end message" << endl;
                            cout << "message length is " << recv_hdr->msgLen << "byte" << endl;
                            cout << "receive message : " << bufMsg << endl;
                        }
                        else
                        {
                            cout << endl << "received unknown message" << endl;
                            cout << "exit server" << endl;
                            close(clientSockFD);
                            break;
                        }
                    }
                }
            } //byteLen > 0
        } //recv while
    } //accept while

    close(listenSockFD);

    cout << "server end" << endl;

    return 0;
}