IOT 실습

강준호·2024년 4월 9일
0

IOT

목록 보기
6/13

server.c

//server.c

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

#define BUF_SIZE 1024

void error_handling(char *message);

int main(int argc, char *argv[])
{
    int serv_sock;
    int clnt_sock;

    char message[BUF_SIZE];
    int str_len, i;

    struct sockaddr_in serv_adr;
    struct sockaddr_in clnt_adr;
    socklen_t clnt_adr_sz;

    if (argc != 2)
    {
        printf("Usage : %s <port>\n", argv[0]);
        exit(1);
    }

    serv_sock = socket(PF_INET, SOCK_STREAM, 0);
    if (serv_sock == -1)
    {
        error_handling("socket() error");
    }
    memset(&serv_adr, 0, sizeof(serv_adr));
    serv_adr.sin_family = AF_INET;
    serv_adr.sin_addr.s_addr = htonl(INADDR_ANY);
    serv_adr.sin_port = htons(atoi(argv[1]));

    if (bind(serv_sock, (struct sockaddr *)&serv_adr, sizeof(serv_adr)) == -1)
    {
        error_handling("bind() error");
    }
    if (listen(serv_sock, 5) == -1)
    {
        error_handling("listen() error");
    }

    clnt_adr_sz = sizeof(clnt_adr);

    for (i = 0; i < 5; i++)
    {
        clnt_sock = accept(serv_sock, (struct sockaddr *)&clnt_adr, &clnt_adr_sz);
        if (clnt_sock == -1)
        {
            error_handling("accept() error");
        }
        else
        {
            printf("Connected client %d \n", i + 1);
        }
        while ((str_len = read(clnt_sock, message, BUF_SIZE)) != 0)
        {
            write(clnt_sock, message, str_len);
        }
        close(clnt_sock);
    }

    close(serv_sock);
    return 0;
}

void error_handling(char *message)
{
    fputs(message, stderr);
    fputc('\n', stderr);
    exit(1);
}

client.c

//client.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>

#define BUF_SIZE 1024

void error_handling(char *message);

int main(int argc, char *argv[])
{
    int sock;
    char message[BUF_SIZE];
    int str_len, recv_len, recv_cnt;
    struct sockaddr_in serv_adr;

    if (argc != 3)
    {
        printf("Usage : %s <port>\n", argv[0]);
        exit(1);
    }

    sock = socket(PF_INET, SOCK_STREAM, 0);
    if (sock == -1)
    {
        error_handling("socket() error");
    }
    memset(&serv_adr, 0, sizeof(serv_adr));
    serv_adr.sin_family = AF_INET;
    serv_adr.sin_addr.s_addr = inet_addr(argv[1]);
    serv_adr.sin_port = htons(atoi(argv[2]));

    if (connect(sock, (struct sockaddr *)&serv_adr, sizeof(serv_adr)) == -1)
    {
        error_handling("connect() error");
    }
    else
    {
        puts("Connected...........");
    }
    while (1)
    {
        fputs("Input message(Q to quit): ", stdout);
        fgets(message, BUF_SIZE, stdin);

        if (!strcmp(message, "q\n") || !strcmp(message, "Q\n"))
            break;

        str_len = write(sock, message, strlen(message));

        recv_len = 0;
        while (recv_len < str_len)
        {
            recv_cnt = read(sock, &message[recv_len], BUF_SIZE - 1);
            if (recv_cnt == -1)
                error_handling("read() error!");
            recv_len += recv_cnt;
        }
        message[recv_len] = 0;
        printf("Message from server: %s", message);
    }
    close(sock);
    return 0;
}

void error_handling(char *message)
{
    fputs(message, stderr);
    fputc('\n', stderr);
    exit(1);
}

클라이언트 코드를 먼저 작동시키면 에러가 발생하는 이유

  • 일반적으로 서버에서 연결을 수락할 수 없기 때문

발생하는 connect() 오류

  • 클라이언트 코드의 connect() 함수는 지정된 IP 주소와 포트 번호에서 서버에 대한 연결 설정을 시도합니다. 클라이언트가 서버보다 먼저 실행될 때 'connect()' 오류가 발생하는 주요 사항은 다음과 같습니다.

1. 서버가 수신되지 않음

  • 서버가 실행되고 있지 않으면 지정된 포트에 바인딩되어 들어오는 연결을 수신하는 프로세스가 없습니다. 결과적으로 클라이언트의 연결 시도에 대한 "끝점"이 없어 오류가 발생합니다.

2. 네트워크 오류 코드

  • ECONNREFUSED: 이 시나리오에서 반환되는 가장 일반적인 오류 코드는 'ECONNREFUSED'입니다. 이는 대상 IP 주소와 포트를 수신하는 서버가 없음을 나타냅니다. 요청된 포트에 바인딩된 프로세스를 찾을 수 없을 때 운영 체제에서 이를 다시 보냅니다.

3. 클라이언트 초기 실행

  • 클라이언트를 처음 실행하면 지정된 서버 주소와 포트를 사용하여 즉시 연결을 시도합니다. 서버가 아직 실행되고 있지 않으면 운영 체제는 반대편에 연결을 수락할 것이 없다는 것을 인식하고 즉시 거부합니다.

다중 액세스가 작동하지 않는 이유

  • 서버의 메인 루프는 차단 방식으로 한 번에 하나씩 연결을 처리하도록 설계되었기 때문!
for (i = 0; i < 5; i++)
{
    clnt_sock = accept(serv_sock, (struct sockaddr *)&clnt_adr, &clnt_adr_sz);
    ...
    while ((str_len = read(clnt_sock, message, BUF_SIZE)) > 0) {
        write(clnt_sock, message, str_len);
    }
    close(clnt_sock);
}
  • 이전 클라이언트가 서버와의 모든 상호 작용(예: 연결 해제)을 완료할 때까지 기다려야 연결 및 서비스를 받을 수 있습니다.

여러 클라이언트 처리를 위한 솔루션

멀티스레딩: 각 클라이언트 연결에 대해 새 스레드를 만듭니다. 이를 통해 서버는 별도의 스레드에서 각 클라이언트의 세션을 실행하여 여러 클라이언트를 동시에 처리할 수 있습니다.

멀티프로세싱: 멀티스레딩과 유사하지만 스레드 대신 별도의 프로세스가 생성됩니다. 이 방법은 일반적으로 멀티스레딩보다 리소스 집약적이며 프로세스 간 통신이 필요하기 때문에 관리가 더 복잡할 수 있습니다.

비차단 I/O(비동기 I/O): 클라이언트 입력을 기다리는 동안 서버를 차단하지 않는 비차단 소켓 또는 비동기 I/O 라이브러리를 사용합니다. libuv 또는 Boost.Asio(C++용)와 같은 라이브러리와 Node.js(JavaScript용)와 같은 프레임워크는 이 모델을 사용합니다.

선택 또는 폴링: 'select()' 또는 'poll()' 시스템 호출을 사용하여 단일 스레드 내에서 동시에 여러 소켓의 가독성이나 쓰기 가능성을 모니터링하고 필요에 따라 소켓으로 주의를 전환합니다.

멀티쓰레딩 예시

#include <pthread.h>

void *handle_client(void *arg) {
    int clnt_sock = *((int *)arg);
    free(arg);
    char message[BUF_SIZE];
    int str_len;

    while ((str_len = read(clnt_sock, message, BUF_SIZE)) > 0) {
        write(clnt_sock, message, str_len);
    }
    close(clnt_sock);
    return NULL;
}

int main(int argc, char *argv[]) {
    int serv_sock;
    struct sockaddr_in serv_adr, clnt_adr;
    socklen_t clnt_adr_sz;

    serv_sock = socket(PF_INET, SOCK_STREAM, 0);
    memset(&serv_adr, 0, sizeof(serv_adr));
    serv_adr.sin_family = AF_INET;
    serv_adr.sin_addr.s_addr = htonl(INADDR_ANY);
    serv_adr.sin_port = htons(atoi(argv[1]));

    bind(serv_sock, (struct sockaddr *)&serv_adr, sizeof(serv_adr));
    listen(serv_sock, 5);

    while (1) {
        clnt_adr_sz = sizeof(clnt_adr);
        int *clnt_sock = malloc(sizeof(int));
        *clnt_sock = accept(serv_sock, (struct sockaddr *)&clnt_adr, &clnt_adr_sz);
        
        pthread_t thread;
        pthread_create(&thread, NULL, handle_client, clnt_sock);
        pthread_detach(thread); // Optionally detach the thread to free resources once it's done
    }
    close(serv_sock);
    return 0;
}
  • 이 코드는 각 클라이언트 연결에 대해 새 스레드를 생성하여 여러 클라이언트가 서버와 동시에 상호 작용할 수 있도록 합니다. -lpthread로 컴파일하여 pthread 라이브러리에 연결해야 합니다.

0개의 댓글

관련 채용 정보