본문 바로가기
[ ★ ]Study/Network

TCP 에코 서버 예제 (리눅스)

by nroses-taek 2017. 6. 11.

TCP 에코 서버 예제 - TCP echo Server example

서버 전체 소스코드

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
 
#define PORT 9999
 
int main(void){
    int sock, client_sock;
    struct sockaddr_in addr, client_addr;
    char buffer[1024];
    int len, addr_len, recv_len;
 
    if((sock = socket(AF_INET, SOCK_STREAM, 0))<0){
        perror("socket ");
        return 1;
    }
 
    memset(&addr, 0x00, sizeof(addr));
    addr.sin_family = AF_INET;
    addr.sin_addr.s_addr = htonl(INADDR_ANY);
    addr.sin_port = htons(PORT);
 
    if(bind(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0){
        perror("bind ");
        return 1;
    }
 
    if(listen(sock, 5) < 0){
        perror("listen ");
        return 1;
    }
 
    addr_len = sizeof(client_addr);
 
    printf("waiting for clinet..\n");
 
    while((client_sock = accept(sock, (struct sockaddr *)&client_addr, &addr_len)) > 0){
        printf("clinet ip : %s\n", inet_ntoa(client_addr.sin_addr));
        
        if((recv_len = recv(client_sock, buffer, 1024, 0)) < 0){
            perror("recv ");
            return 1;
            break;
        }
        buffer[recv_len] = '\0';
        printf("received data : %s\n", buffer);
        send(client_sock, buffer, strlen(buffer), 0);
 
        close(client_sock);
    }
    close(sock);
    return 0;
}

TCP 서버 프로그램의 순서도는 아래와 같다

1. 소켓 초기화 (socket)

2. 생성된 소켓에 주소 부여 (bind)

3. 접속 연결 대기 (listen)

4. 클라이언트 접속을 받아들임 (accept)

5. 데이터 송수진 (recv, recvfrom / send, sendto)

6. 연결 종료 (close)

윈도우 프로그래밍에서는 소켓 버전을 명시하면서 소켓 라이브러리를 초기화하는 과정이 socket 함수를 이용하여 소켓을 초기화하는 과정보다 먼저 실행되어야 한다. 소켓 관련 함수를 더는 사용하지 않을 것이라면 소켓 관련 라이브러리를 하제해 주어야 한다는 점이 리눅스와는 다르다.

윈도우에서 한 번더 언급하겠다.

RESULT
( 해당 사진의 설명은 사진 바로 아래에 )

위 사진은 실행 후 아래의 사진을 실행한 결과 입니다.

로컬에서 주고 받았기 때문에 ip는 127.0.0.1로 뜨는게 당연하죠.

위 사진은 서버프로그램을 실행 후 ' nc localhost 9999 ' 로 실행후

hello network를 타이핑하였습니다.

만약 다른 PC라면 해당 서버 IP를 입력해야 됩니다.

 

========= details ========

if((sock = socket(AF_INET, SOCK_STREAM, 0))<0){
        perror("socket ");
        return 1;
}

socket() 함수를 호출하여 소켓을 초기화한다. 소켓을 초기화하면서 소켓 타입을 SOCK_STREAM으로 하여 스트림 연결 타입으로 생성하는데 이는 TCP 프로토콜을 이용하여 소켓을 생성하겠다는 의미이다.

memset(&addr, 0x00, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = htonl(INADDR_ANY);
addr.sin_port = htons(PORT);

주소 정보가 저정되는 addr 구조체를 초기화하여, 소켓 주소 정보를 연결하는 내용이다.

소켓 구조체 접오는 위와 같은 방법으로 초기화하고 이쓴데, 구조체 변수 addr의 내용을 모두 0x00값으로 채운 뒤에 각각 구조체 멤버에 값을 저장하고 있다

서버에서 클라이언트의 접속을 기다리는 소켓에 주소 정보를 연결하기 위해서 접속을 받아들이는 IP는 모든 IP로부터 받기 위해서 INADDR_ANY 매크로를 사용하였다. 접속을 받아들이는 포트는 매크로에서 장의한 9999번 포트를 사용하여 클라이언트의 접속을 받도록 한다.

if(listen(sock, 5) < 0){
        perror("listen ");
        return 1;
}

listen()함수를 이용하여 연결 요청대기 큐의 개수를 지정하고, while반복문의 조건 내에서 accept()함수를 이용하여 클라이언트의 접속을 받아들인다.

accept() 함수는 블록킹 모드로 실행되는 함수로 만약 클라이언트의 접속이 없으면 클라이언트가 접속할 때까지 기다리게 된다. 클라이언트의 접속이 성공적으로 이루어지면 client_addr 구조체 변수에 클라이언트의 접속 정보가 저장되면서 반복문 내의 구문이 실행된다.


while((client_sock = accept(sock, (struct sockaddr *)&client_addr, &addr_len)) > 0){
        printf("clinet ip : %s\n", inet_ntoa(client_addr.sin_addr));
        
        if((recv_len = recv(client_sock, buffer, 1024, 0)) < 0){
            perror("recv ");
            return 1;
            break;
        }
        buffer[recv_len] = '\0';
        printf("received data : %s\n", buffer);
        send(client_sock, buffer, strlen(buffer), 0);
        close(client_sock);
}

 

클라이언트가 접속하면 실행되는 내용으로, client_addr 구조체의 클라이언트의 연결 정보가 저장되었기 때문에 client_addr.sin_addr 구조체의 멤버를 주소 변환 함수 inet_addr()을 이용하여 클라이언트의 IP주소를 출력할 수 있게 된다.

그 다음에 recv() 함수를 통해서 클라이언트가 전송하는 메시지를 수신받고, 배열 buffer에 저장하게 된다. 전송받은 메시지를 화면에 출력하고 해당 내용을 다시 클라이언트로 전송한 뒤에 클라이언트의 연결을 종료하며 위의 내용은 클라이언트가 접속할 때마다 반복문을 실행하면서 반복하게 된다.

 

TCP 에코 클라이언트 <--클릭

'[ ★ ]Study > Network' 카테고리의 다른 글

TCP 에코 서버 (윈도우)  (0) 2017.06.11
TCP 에코 서버 클라이언트 (리눅스)  (0) 2017.06.11
TCP 프로토콜 (2)  (0) 2017.06.10
TCP 프로토콜 (1)  (0) 2017.06.09
WSACleanup 함수(Windows)  (0) 2017.03.15

댓글