C/TCPIP 소켓 프로그래밍(윤성우 저)

리눅스 기반 서버, 클라이언트 예제

꾸준함. 2017. 5. 13. 13:56

윈도우에서는 winsock2.h 헤더파일을 사용하는 것과 달리 리눅스는 세개의 헤더파일을 사용합니다.

1. unistd.h

2. arpa/inet.h

3. sys/socket.h


<윈도우와 리눅스의 차이점>

앞서 리눅스는 내부적으로 소켓도 파일로 취급하기 때문에, 파일을 생성하건 소켓을 생성하건 파일 디스크립터가 반환됩니다.

마찬가지로 윈도우에서도 시스템 함수의 호출을 통해서 파일을 생성할 때 '핸들(handle)'이라는 것을 반환한다.

즉, 리눅스에서의 파일 디스크립터와 윈도우의 핸들은 함수만 다르지 거의 같다고 볼 수 있다.

하지만 윈도우 같은 경우 리눅스와는 달리 파일 핸들과 소켓 핸들을 구분하고 있습니다.(가장 큰 차이점)


서버 프로그램

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#include <unistd.h>

#include <srpa/inet.h>

#include <sys/socket.h>

 

void error_handling(char *message);

 

int main(int argc, char *argv[])

{

        int serv_sock;

        int clnt_sock;

 

        struct sockaddr_in serv_addr;

        struct sockaddr_in clnt_addr;

        socklen_t clnt_addr_size;

 

        char message[] = "Hello World!";

 

        if (argc != 2)

        {

               printf("Usage:%s <port>\n", argv[0]);

               exit(1);

        }

 

        serv_sock = socket(PF_INET, SOCK_STREAM, 0); //socket 함수호출을 통해서 소켓을 생성하고 있다

        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);

        serv_addr.sin_port = htons(atoi(argv[1]));

 

        if (bind(serv_sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) == -1) //bind 함수호출을 통해서 IP주소와 PORT번호를 할당하고 있다

               error_handling("bind() error");

 

        if (listen(serv_sock, 5) == -1) //listen 함수를 호출하고 있다. 이로써 소켓은 연결요청을 받아들일 있는 상태가 된다

               error_handling("listen() error");

 

        clnt_addr_size = sizeof(clnt_addr);

        clnt_sock = accept(serv_sock, (struct sockaddr*)&clnt_addr, &clnt_addr_size); //연결요청의 수락을 위한 accept 함수를 호출하고 있다

        //연결요청이 없는 상태에서 함수가 호출되면, 연결요청이 있을 때까지 함수는 반환하지 않는다

        if (clnt_sock == -1)

               error_handling("accept() error");

 

        write(clnt_sock, message, sizeof(message)); //write 함수는 데이터를 전송하는 기능의 함수인데, 문장이 실행됬다는 것은 연결요청이 있었다는

        close(clnt_sock);

        close(serv_sock);

        return 0;

}

 

void error_handling(char *message)

{

        fputs(message, stderr);

        fputc('\n', stderr);

        exit(1);

}

 

윈도우 기반 서버 프로그램과 마찬가지로 실행하면 화면이 멈추고 커서만 깜빡일 것입니다.


윈도우에서는 비주얼 스튜디오를 2개 켜서 서버, 클라이언트 프로그램을 실행했듯이

리눅스에서도 서버프로그램을 실행한 창을 그대로 두고 클라이언트 프로그램을 실행해야합니다.

화면이 멈췄다고 ctrl c를 누르고 클라이언트 프로그램을 실행했다면 

서버프로그램이 종료되어있는 시점에서 클라이언트 프로그램을 돌렸기 때문에 connect() error 메세지가 전달됩니다.


따라서 화면이 멈추고 커서만 깜빡이는 창에서 ALT키와 F2키(F3, F4 누르셔도 무방합니다)를 누릅니다.

누르시면 위와 같이 로그인 창이 뜨는데 이것이 비주얼 스튜디오에서 다른 프로젝트를 띄우는 것과 같은 역할을 합니다.

로그인을 하시고 클라이언트 프로그램을 실행시키시면 됩니다.


클라이언트 프로그램

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#include <unistd.h>

#include <arpa/inet.h>

#include <sys/socket.h>

 

void error_handling(char *message);

 

int main(int argc, char *argv[])

{

        int sock;

        struct sockaddr_in serv_addr;

        char message[30];

        int str_len;

 

        if (argc != 3)

        {

               printf("Usage: %s <IP> <port>\n", argv[0]);

               exit(1);

        }

 

        sock = socket(PF_INET, SOCK_STREAM, 0); //소켓을 생성하고 있다.

        //소켓을 생성하는 순간에는 서버 소켓과 클라이언트 소켓으로 나뉘지 않는다.

        //bind listen함수의 호출이 이어지면 서버 소켓, connect 함수의 호출로 이어지면 클라이언트 소켓

        if (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 = inet_addr(argv[1]);

        serv_addr.sin_port = htons(atoi(argv[2]));

 

        if (connect(sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) == -1) //connect 함수호출을 통해서 서버 프로그램에 연결 요청

               error_handling("connect() error");

 

        str_len = read(sock, message, sizeof(message) - 1);

        if (str_len == -1)

               error_handling("read() error");

 

        printf("Message from server:%s\n", message);

        close(socket);

        return 0;

}

 

void error_handling(char *message)

{

        fputs(message, stderr);

        fputc('\n', stderr);

        exit(1);

}

프로그램이 정상적으로 실행되어 서버프로그램으로부터 Hello World! 메세지를 전달받았습니다!


[참고] 윤성우 저 TCP/IP 소켓 프로그래밍


반응형