반응형

 

 

결론 => recv 함수에서 에러 (0)를 반환하게 된다. recv 함수 반환값에 대한 처리에 따라 달려있다. 

(즉, recv 함수에 대한 반환값이 0보다 작거나 같을 때에 대한 처리를 어떻게 하냐에 달려있다.)

 

배경 설명

 두 프로세스가 있다.

하나의 프로세스는 데이터를 송신하는 프로세스이고,

또 다른 하나는 데이이터를 수신하는 프로세스이다.

 

여기서, 수신하는 프로세스의 파일 디스크립터(fd)를 닫아버리면 어떻게될까?

 

 먼저 예제 코드를 보자.

클라이언트 쪽 예제코드는, 클라이언트가 1초마다 3번 서버 쪽에게 메시지를 전송하는 코드이고

서버 쪽 코드는, 클라이언트의 메시지를 계속 수신하돼, 5초 후에 다른 스레드에서 수신 FD를 close하는 예제이다.

 

■ 클라이언트 소스

더보기
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>

#define PORT 8080
#define BUFFER_SIZE 1024

int main() {
    int sock = 0;
    struct sockaddr_in serv_addr;
    char buffer[BUFFER_SIZE] = {0};
    char *hello = "Hello from client";

    // 소켓 생성
    if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
        printf("\n Socket creation error \n");
        return -1;
    }

    serv_addr.sin_family = AF_INET;
    serv_addr.sin_port = htons(PORT);

    // IP 주소 변환
    if (inet_pton(AF_INET, "127.0.0.1", &serv_addr.sin_addr) <= 0) {
        printf("\nInvalid address/ Address not supported \n");
        return -1;
    }

    // 서버에 연결
    if (connect(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) {
        printf("\nConnection Failed \n");
        return -1;
    }

    for (int i = 0; i < 3; i++) {
        // 서버로 메시지 전송
        send(sock, hello, strlen(hello), 0);
        printf("Hello message sent\n");
        sleep(1);
    }

    // 소켓 종료
    close(sock);

    return 0;
}

gcc -o client client.c

./client

 

■ 서버 소스

더보기
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <pthread.h>

#define PORT 8080
#define BUFFER_SIZE 1024

int server_fd;

void* close_fd_after_5sec(void* arg) {
        sleep(5);
        close(server_fd);
}


int main() {
    int new_socket;
    struct sockaddr_in address;
    int addrlen = sizeof(address);
    char buffer[BUFFER_SIZE] = {0};
    char *hello = "Hello from server";
    pthread_t th;

    // 소켓 생성
    if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) {
        perror("socket failed");
        exit(EXIT_FAILURE);
    }

    // 주소 구조체 초기화
    address.sin_family = AF_INET;
    address.sin_addr.s_addr = INADDR_ANY;
    address.sin_port = htons(PORT);

    // 소켓에 주소를 바인딩
    if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0) {
        perror("bind failed");
        close(server_fd);
        exit(EXIT_FAILURE);
    }

    // 연결 대기 (백로그 큐 크기: 3)
    if (listen(server_fd, 3) < 0) {
        perror("listen failed");
        close(server_fd);
        exit(EXIT_FAILURE);
    }

    printf("Server is waiting for connections...\n");

    // 클라이언트 연결 수락
    if ((new_socket = accept(server_fd, (struct sockaddr *)&address, (socklen_t *)&addrlen)) < 0) {
        perror("accept failed");
        close(server_fd);
        exit(EXIT_FAILURE);
    }

        if (pthread_create(&th, NULL, close_fd_after_5sec, (void*)NULL)) {
        printf("Error creating thread 1\n");
        return 1;
    }

    while (1) {
        // 클라이언트로부터 메시지 수신
        int valread = read(new_socket, buffer, BUFFER_SIZE);
        if (valread > 0) {
            printf("Received from client: %s\n", buffer);
        } else {
            printf("Invalid valread: %d\n", valread);
            break;
        }
    }

    // 소켓 종료
    close(new_socket);
    close(server_fd);

    return 0;
}

gcc -o server server.c -lpthread

./server

 

결과화면

1) 클라이언트 측

$ ./client
Hello message sent
Hello message sent
Hello message sent

 

 

2) 서버 측

$ ./server
Server is waiting for connections...
Received from client: Hello from client
Received from client: Hello from client
Received from client: Hello from client
Invalid valread: 0

 

 

고찰

여기서 알 수 있는 것은, 다른 스레드에서 File descriptor를 close해버리면, recv 함수가 불리울 때, 0을 반환한다는 것이다. 여기서 주의할 점이 몇가지 있다.

 

(1) recv 함수로 인해 block이 된 상황에서, fd를 닫아버릴 때, 0을 반환하는 것이다.

(2) 저 쪽 서버쪽 코드에는 recv함수가 0과 작거나 같을 때, while문을 break하게 되어있다. 이 작업을 하지 않으면 닫혀진 fd를 통해 계속 recv 시도를 하게 될 것이고, 이 recv 함수는 계속 0과 작거나 같은 값만 반환하게 될 것이다.

 

 

 

반응형

'C > C' 카테고리의 다른 글

[C/C++] __attribute__((packed))에 대한 이해  (1) 2024.03.28
[C] 파일입출력 예제  (0) 2023.03.31
[C/C++] 실행시간 측정하기 time(), clock()  (0) 2023.03.30
[C] C언어 알아두기 (Main)  (0) 2022.11.16
아스키코드표 (ASCII Code table)  (0) 2022.07.18