티스토리 뷰

반응형


* 본 포스팅은 한국소프트웨어진흥원 출처의 리눅스 네트워크 프로그래밍 PDF 파일을 토대로 작성되었습니다.

* 개인공부 후 자료를 남기기 위한 목적임으로 내용 상에 오류가 있을 수 있음을 밝힙니다.




이번에는 TCP를 사용한 간단한 서버 프로그램을 만들어 보겠습니다.


서버 쪽에서는 포트번호를 arg로 입력받고 서버를 열고

클라이언트는 서버에 붙어 메시지를 입력하면 입력한 메시지를 다시 돌려받는

에코 서비스 서버입니다. 


echo_server.c


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
#include "stdio.h"
#include "stdlib.h"
#include "string.h"
#include "time.h"
#include "sys/types.h"
#include "sys/socket.h"
#include "netinet/in.h"
//소켓 프로그래밍에 사용될 헤더파일 선언
 
#define BUF_LEN 128
//메시지 송수신에 사용될 버퍼 크기를 선언
 
int main(int argc, char *argv[])
{
    char buffer[BUF_LEN];
    struct sockaddr_in server_addr, client_addr;
    char temp[20];
    int server_fd, client_fd;
    //server_fd, client_fd : 각 소켓 번호
    int len, msg_size;
 
    if(argc != 2)
    {
        printf("usage : %s [port]\n", argv[0]);
        exit(0);
    }
 
    if((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
    {// 소켓 생성
        printf("Server : Can't open stream socket\n");
        exit(0);
    }
    memset(&server_addr, 0x00, sizeof(server_addr));
    //server_Addr 을 NULL로 초기화
 
    server_addr.sin_family = AF_INET;
    server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    server_addr.sin_port = htons(atoi(argv[1]));
    //server_addr 셋팅
 
    if(bind(server_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) <0)
    {//bind() 호출
        printf("Server : Can't bind local address.\n");
        exit(0);
    }
 
    if(listen(server_fd, 5) < 0)
    {//소켓을 수동 대기모드로 설정
        printf("Server : Can't listening connect.\n");
        exit(0);
    }
 
    memset(buffer, 0x00, sizeof(buffer));
    printf("Server : wating connection request.\n");
    len = sizeof(client_addr);
    while(1)
    {
        client_fd = accept(server_fd, (struct sockaddr *)&client_addr, &len);
        if(client_fd < 0)
        {
            printf("Server: accept failed.\n");
            exit(0);
        }
        inet_ntop(AF_INET, &client_addr.sin_addr.s_addr, temp, sizeof(temp));
        printf("Server : %s client connected.\n", temp);
    
        msg_size = read(client_fd, buffer, 1024);
        write(client_fd, buffer, msg_size);
        close(client_fd);
        printf("Server : %s client closed.\n", temp);
    }
    close(server_fd);
    return 0;
}


22~26 line : 서버 프로그램을 실행할 때 포트번호를 입력하지 않으면 프로그램이 종료됩니다.

28~32 line : socket 함수를 사용하여 서버측의 소켓을 생성합니다.

36~49 line : 서버측의 정보를 server_addr 구조체에 입력받습니다.

41~45 line : 생성한 소켓과 외부의 소켓주소를 묶어주는 bind 함수입니다.

47~51 line : 서버가 listen 함수를 통해 대기상태로 진입합니다. 최대 대기 클라이언트의 수는 5입니다.

58~63 line : 클라이언트의 요청을 감지하면 accept 함수가 실행되고 그 반환 값으로 클라이언트의 소켓번호가 생성됩니다.

64 line : 접속한 클라이언트의 IP를 확인하기 위해 inet_ntop 함수를 사용합니다. 

(관련 포스팅 : 2014/02/03 - [IT를만나다/Socket Programming] - 리눅스 소켓 프로그래밍 05 : IP, 도메인 주소변환)

67 line : 클라이언트가 값을 입력하는 값을 받기 위한 read 함수입니다.

68 line : 클라이언트로 부터 입력받은 값을 다시 write 합니다.

69 line : 클라이언트의 소켓을 닫습니다.

72 line : 서버의 소켓을 닫습니다.



실제로 컴파일하고 실행해보겠습니다.



gcc로 컴파일한 후 포트번호를 인자로 준 에코서버 프로그램을 실행하면

연결을 기다리고 있다는 문구가 뜹니다 (포트번호가 사용 중이거나 등등의 이유로 bind 에러가 발생할 수 있습니다)



다른 쉘을 띄워서 로컬호스트(127.0.0.1)로 접속해 보겠습니다



nc 명령어로 접속한 후 프롬프트에 특정 문구를 입력하면 똑같이 되돌아 오는 것을 확인 할 수 있습니다.


그리고 다시 서버로 돌아오면



위와 같이 접속한 클라이언트의 로그가 남는 것을 확인 할 수 있네요.


accept 부분에 무한루프(while)을 걸어놨기 때문에 Ctrl+c 를 통해 종료하지 않는 이상 서버 프로그램이 종료 되지 않기때문에

서버 프로그램만 돌아가고 있으면 계속해서 nc로 붙을 수가 있습니다.


반응형
프로필사진

Yowu (Yu Yongwoo)

흔한 Node.js/Java 백엔드 개발자입니다
Ubuntu와 MacOS 데스크탑 개발 환경을 선호합니다
최근에는 vscode와 IntelliJ를 사용하고 있습니다
vscode에는 neovim, IntelliJ는 ideaVim
개발용 키보드는 역시 HHKB Pro 2 무각입니다
락 밴드에서 드럼을 쳤습니다

최근에 올라온 글
최근에 달린 댓글
«   2024/04   »
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30
글 보관함
Total
Today
Yesterday