ddingz 2022. 1. 10. 16:56

토크 서버에서는 먼저 listen()을 호출하여 소켓을 수동 대기 모드로 바꾸고 accept()를 호출하여 클라이언트로부터의 연결을 기다리다 연결이 이루어지면 fork()를 호출한다.
부모 프로세스는 사용자의 키보드 입력을 받아 클라이언트에게 전송하며, 자식 프로세스는 클라이언트가 보내온 메시지를 화면에 출력한다.
서버가 사용할 포트번호는 명령문 인자로 입력한 값(argv[1])을 사용한다.
사용자가 종료문자(예 "exit")를 입력하면 부모 프로세스가 자식 프로세스를 종료시키고 프로그램을 종료시키도록 하였다.
입력된 문자열 내에 종료문자가 포함되어 있는지를 확인하기 위해서 strstr() 함수를 사용하였다.
또한 상대방이 "exit" 문자열이 포함된 메시지를 전송해 오면 토크 서비스를 중단하고 연결을 끊도록 하였다.


tcp_talkserv.c

토크 서버 프로그램이다.
키보드로부터 데이터 입력을 받고 상대방에게 메시지를 전달하는 함수 input_and_send()와 상대로부터 메시지를 수신한 후 이를 화면에 출력하는 함수 recv_and_print()를 정의하였다.

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

char *EXIT_STRING = "exit"; // 종료문자
int recv_and_print(int sd); // 상대로부터 메시지 수신후 화면 출력
int input_and_send(int sd); // 키보드 입력받고 상대에게 메시지 전달

#define MAXLINE 511

int main(int argc, char *argv[]) {
    struct sockaddr_in cliaddr, servaddr;
    int listen_sock;
    int accp_sock; // 소켓번호
    int addrlen = sizeof(cliaddr);
    pid_t pid;

    if (argc != 2) {
        printf("사용법: %s port\n", argv[0]);
        exit(0);
    }

    // 소켓 생성
    if ((listen_sock = socket(PF_INET, SOCK_STREAM, 0)) < 0) {
        perror("socket fail");
        exit(0);
    }

    // 서버의 소켓주소 구조체 servaddr을 '0'으로 초기화
    bzero((char *)&servaddr, sizeof(servaddr));

    // servaddr을 세팅
    servaddr.sin_family = AF_INET;
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    servaddr.sin_port = htons(atoi(argv[1]));

    // bind() 호출
    if (bind(listen_sock, (struct sockaddr *)&servaddr, sizeof(servaddr)) < 0) {
        perror("bind fail");
        exit(0);
    }
    puts("서버가 클라이언트를 기다리고 있습니다.");
    listen(listen_sock, 1);

    // 클라이언트 연결요청 수락
    if ((accp_sock = accept(listen_sock, (struct sockaddr *)&cliaddr, &addrlen)) < 0) {
        perror("accept fail");
        exit(0);
    }
    puts("클라이언트가 연결되었습니다.");

    if ((pid = fork()) > 0)
        input_and_send(accp_sock); // 키보드 입력받고 상대에게 메시지 전달
    else if (pid == 0)
        recv_and_print(accp_sock); // 상대로부터 메시지 수신후 화면 출력

    close(listen_sock);
    close(accp_sock);

    return 0;
}

// 키보드 입력받고 상대에게 메시지 전달
int input_and_send(int sd) {
    char buf[MAXLINE + 1];
    int nbyte;

    while (fgets(buf, sizeof(buf), stdin) != NULL) {
        nbyte = strlen(buf);
        write(sd, buf, strlen(buf));

        // 종료문자열 입력 처리
        if (strstr(buf, EXIT_STRING) != NULL) {
            puts("Good bye.");
            close(sd);
            exit(0);
        }
    }

    return 0;
}

// 상대로부터 메시지 수신후 화면 출력
int recv_and_print(int sd) {
    char buf[MAXLINE + 1];
    int nbyte;

    while(1) {
        if ((nbyte = read(sd, buf, MAXLINE)) < 0) {
            perror("read fail");
            close(sd);
            exit(0);
        }
        buf[nbyte] = 0;

        // 종료문자열 수신 시 종료
        if (strstr(buf, EXIT_STRING) != NULL)
            break;

        printf("%s", buf); // 화면 출력
    }

    return 0;
}

실행 결과