[ft_irc] 1. 소켓 프로그래밍

aqualung·2023년 6월 25일
0

🖥 echo 서버

기본적인 서버 프로그램을 만들어 보자.

서버가 클라이언트의 요청을 처리하는 과정은 이러하다.

  1. 소켓 개설 - socket()
  2. 소켓과 서버의 주소를 연결 - bind()
  3. 클라이언트의 연결요청을 기다림 - listen()
  4. 연결수락 - accept()
  5. 클라이언트의 요청 읽기 및 쓰기 - read()/write()

1. socket()

#include <sys/socket.h>
int socket(int domain, int type, int protocol);

소켓을 생성해주는 함수이다.

prameter

  1. domain - 주소 패밀리

    AF_UNIX: 로컬에서 프로세스끼리 통신할 때
    AF_INET: IPv4 주소체계를 이용
    AF_INET6: IPv6 주소체계를 이용

  2. type - 소켓의 형식

    SOCK_STREAM: TCP 이용
    SOCK_DGRAM: UDP 이용
    SOCK_RAW: 네트워크와 전송 계층의 헤더를 사용자 임의로 조작

  3. protocol - 프로토콜

    IPPROTO_TCP: TCP 이용
    IPPROTO_UDP: UDP 이요
    0: type에서 지정한 값 이용

성공 시 0 실패 시 -1 반환

2. bind()

#include <sys/socket.h>
int bind(int sockfd, struct sockaddr *myaddr, socklen_t addrlen);

생성된 소켓과 서버의 정보를 묶어주는 함수이다.
클라이언트가 서버에서 연결을 할 때 IP주소와 port등의 '주소 정보'를 이용하여 연결을 요청하므로 이 주소 정보와 생성한 소켓을 묶어줄 필요가 있다.

parameter

  1. sockfd - 소켓의 fd
  2. myaddr - 서버의 주소정보
  3. addrlen - myaddr의 길이

성공 시 0 실패 시 -1 반환

3. listen()

#include <sys/socket.h>
int listen(int sockfd, int backlog);

소켓에 연결 요청이 들어오는지 기다리는 함수이다.

parameter

  1. sockfd - 소켓의 fd
  2. backlog - 연결 요청 대기열(queue)의 크기

성공 시 0 실패 시 -1 반환

4. accept()

#include <sys/socket.h>
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

연결 요청을 수락하는 함수이다.

parameter

  1. sockfd - listen하고 있는 소켓의 fd
  2. addr - 클라이언트의 주소 정보를 담을 객체
  3. addrlen - 클라이언트 주소 정보의 길이

성공 시 0 실패 시 -1 반환

5. echo 서버 프로그램

클라이언트의 입력을 그대로 echo해주는 서버이다.

echoserver.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>

#define BUF_SIZE 512

void error_handling(char* message)
{
    fputs(message, stderr);
    fputc('\n', stderr);
    exit(1);
}

int main(int argc, char* argv[])
{
    int serv_sock;
    int clnt_sock;

    // sockaddr은 주소정보를 담는 기본형태이다.
    // 주소체계에 따라 sockaddr_in, sockaddr_un, sockaddr_in6 등을 sockaddr로 형변환해서 사용하면 편리하다.
    struct sockaddr_in serv_addr;
    struct sockaddr_in clnt_addr;
    socklen_t clnt_addr_size;

    // TCP연결, IPv4 도메인을 위한 소켓 생성
    serv_sock = socket(AF_INET, SOCK_STREAM, 0);
    if (serv_sock == -1)
        error_handling("socket error");
    
    //서버의 주소 정보 설정
    memset(&serv_addr, 0, sizeof(serv_addr));
    serv_addr.sin_family = AF_INET; // 주소패밀리
    serv_addr.sin_addr.s_addr = htonl(INADDR_ANY); // 서버의 ip주소
    serv_addr.sin_port = htons(atoi(argv[1])); // 서버 프로그램의 포트
    //htonl, htos - 빅엔디안 타입으로 변환

    // 소켓과 주소정보를 결합
    if (bind(serv_sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) == -1)
        error_handling("bind error");
    
    // 연결요청 대기
    if (listen(serv_sock, 5) == -1)
        error_handling("listen error");
    
    // 연결 수락
    clnt_addr_size = sizeof(clnt_addr);
    clnt_sock = accept(serv_sock, (struct sockaddr*)&clnt_addr, &clnt_addr_size);
    if (clnt_sock == -1)
        error_handling("accept error");

    while (1)
    {
        char* buf[BUF_SIZE];

        // 클라이언트 데이터 읽기
        if (read(clnt_sock, buf, BUF_SIZE) <= 0)
        {
            close(clnt_sock);
            printf("client disconnected\n");
            break;
        }
        // 클라이언트에게 데이터 전송
        else
        {
            write(clnt_sock, buf, strlen((const char*)buf));
        }
        
        memset(buf, 0, BUF_SIZE);
    }
    close(serv_sock);

    return 0;
}

동작확인

컴파일 및 실행

> gcc echoserver.c -o echoserver
> ./echoserver 5150

netcat을 이용해 에코서버 접속

> nc localhost 5150
hello
hello
hi
hi

0개의 댓글

관련 채용 정보