카테고리 없음

TCP 서버, 클라이언트 프로그래밍, blocking 해결하기

개발자 aloe 2023. 5. 15. 13:45
728x90

listen.c

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

int main(int argc, char *argv[])
{
    int nFd; // 소켓 파일 디스크립터
    uint16_t nPort; // 포트 번호
    struct sockaddr_in sAddr; // 서버 주소 구조체

    if (argc != 3) // 명령행 인자로 IP 주소와 포트 번호가 전달되지 않은 경우
    {
        printf("usage: %s <ip> <port>\n", argv[0]);
        return -1;
    }

    nPort = atoi(argv[2]); // 문자열 형태의 포트 번호를 정수형으로 변환
    printf("IP Address: %s\n", argv[1]);
    printf("Port: %d\n", nPort);

    nFd = socket(AF_INET, SOCK_STREAM, 0); // TCP 소켓 생성
    printf("Server FD: %d.\n", nFd);

    bzero(&sAddr, sizeof(struct sockaddr_in)); // 서버 주소 구조체 초기화
    sAddr.sin_family = PF_INET; // IPv4 주소 체계 사용
    sAddr.sin_addr.s_addr = inet_addr(argv[1]); // 인자로 전달받은 IP 주소를 네트워크 바이트 순서로 변환하여 설정
    sAddr.sin_port = htons(nPort); // 인자로 전달받은 포트 번호를 네트워크 바이트 순서로 변환하여 설정

    if (bind(nFd, (struct sockaddr *)&sAddr, sizeof(sAddr)) < 0) // 소켓에 IP 주소와 포트 번호를 바인딩
    {
        printf("Binding Failed...\n");
        return -1;
    }

    listen(nFd, 10); // 연결 요청을 대기하는 큐의 크기를 10으로 설정
    printf("Listening is OK now.\n");
    printf("Press Enter key to finish.\n");
    getchar(); // Enter 키 입력 대기
    close(nFd); // 소켓 닫기
    return 0;
}

ip번호와 포트를 연결 

정상적으로 만들어진것을 확인 할 수 있음 

8080으로 서버가 돌아가는 것을 확인 할수 있음

TCP에 대한 정보, 네트워크 상태 확인 아래 

현제 iPv4에 대한 것만 출력함 4 옵션을 줘서

 

이렇게 telnet을 통해 접근 하면 대기큐에 하나가 증가 되는 것을 확인 할 수 있음 

 

 

 

 

 

accept.c

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

int main(int argc, char *argv[])
{
    int nSFd; // 서버 소켓 파일 디스크립터
    int nCFd; // 클라이언트 소켓 파일 디스크립터
    uint16_t nPort; // 서버 포트 번호
    struct sockaddr_in sAddr; // 서버 주소 구조체
    struct sockaddr_in cAddr; // 클라이언트 주소 구조체
    socklen_t nCAddr; // 클라이언트 주소 구조체의 길이
    char strBuffer[BUFSIZ];
    int nBufferLen = 0;

    if (argc != 3)
    {
        // 클라이언트 주소 구조체의 길이
        printf("usage: %s <ip> <port>\n", argv[0]);
        return -1;
    }

    nPort = atoi(argv[2]); // 문자열 형태의 포트 번호를 정수형으로 변환
    printf("IP Address: %s\n", argv[1]);
    printf("Port: %d\n", nPort);

    nSFd = socket(AF_INET, SOCK_STREAM, 0); // TCP 소켓 생성
    printf("Server FD: %d.\n", nSFd);

    bzero(&sAddr, sizeof(struct sockaddr_in)); // 서버 주소 구조체 초기화
    sAddr.sin_family = PF_INET; // IPv4 주소 체계 사용
    sAddr.sin_addr.s_addr = inet_addr(argv[1]); // 인자로 전달받은 IP 주소를 네트워크 바이트 순서로 변환하여 설정
    sAddr.sin_port = htons(nPort); // 인자로 전달받은 포트 번호를 네트워크 바이트 순서로 변환하여 설정

    if (bind(nSFd, (struct sockaddr *)&sAddr, sizeof(sAddr)) < 0) // 소켓에 IP 주소와 포트 번호를 바인딩
    {
        printf("Binding Failed...\n");
        return -1;
    }

    listen(nSFd, 10); // 연결 요청을 대기하는 큐의 크기를 10으로 설정
    printf("Listening is OK, now.\n");

    for (int i = 0; i < 5; i++)
    {
        nCAddr = sizeof(cAddr); // 클라이언트 주소 구조체의 길이 설정
        nCFd = accept(nSFd, (struct sockaddr *)&cAddr, &nCAddr); // 클라이언트의 연결 요청 수락
        if (nCFd > 0)
        {
            printf("Client Info:\n");
            printf("\t - IP Address: %s\n", inet_ntoa(cAddr.sin_addr)); // 클라이언트 IP 주소 출력
            printf("\t - Port: %d\n", ntohs(cAddr.sin_port)); // 클라이언트 포트 번
            nBufferLen = sprintf(strBuffer, "Hello There!!\n"); // 버퍼에 메시지를 저장하고 그 길이를 반환
            send(nCFd, strBuffer, nBufferLen, 0); // 클라이언트에게 메시지를 전송
            close(nCFd); // 클라이언트 소켓 파일 디스크립터를 닫음
        }
    }

    close(nSFd); // 서버 소켓 파일 디스크립터를 닫음
    return 0;
}

이 코드는 TCP 기반의 간단한 서버를 구현합니다. 주어진 IP 주소와 포트 번호를 사용하여 서버를 시작하고, 클라이언트의 연결 요청을 수락합니다. 연결이 수락되면 클라이언트 정보를 출력하고 "Hello There!!"라는 메시지를 클라이언트에게 전송합니다. 이후 클라이언트 소켓을 닫고, 서버 소켓도 마지막에 닫습니다. 반복문을 통해 최대 5개의 클라이언트 연결을 처리할 수 있습니다.

 

 

해당 서버 ip와 포트번호로 계속 요청을 보내서 수락이 되었기 때문에 Hello There!! 메세지가 출력 되는 것을 확인 할 수 있습니다.

서버쪽에서는 클라쪽에 정보를 출력합니다.

 

Network_Common.h

#include <sys/types.h> 
#include <unistd.h>

// TCP 서버를 시작하는 함수
// nPort: 서버의 포트 번호
// nBacklog: 연결 대기 큐의 크기
// 반환값: 성공 시 서버 소켓 파일 디스크립터, 실패 시 -1
int start_tcp_server(uint16_t nPort, int nBacklog);

// TCP 서버에 연결하는 함수
// strIpAddress: 서버의 IP 주소
// nPort: 서버의 포트 번호
// 반환값: 성공 시 클라이언트 소켓 파일 디스크립터, 실패 시 -1
int connect_to_tcp_server(char* strIpAddress, uint16_t nPort);

Network_Common.c

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

// TCP 서버를 시작하는 함수
// nPort: 서버의 포트 번호
// nBacklog: 연결 대기 큐의 크기
// 반환값: 성공 시 서버 소켓 파일 디스크립터, 실패 시 -1
int start_tcp_server(uint16_t nPort, int nBacklog)
{
    struct sockaddr_in stSAddr;
    int nSFd = -1;

    // 소켓 생성
    nSFd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (nSFd < 0) {
        printf("Creating a socket was failed.\n"); 
        return -1;
    }

    // 소켓 주소 구조체 초기화
    bzero(&stSAddr, sizeof(stSAddr)); 
    stSAddr.sin_family = AF_INET;
    stSAddr.sin_addr.s_addr = htonl(INADDR_ANY); // 모든 인터페이스에서 연결을 받아들임
    stSAddr.sin_port = htons(nPort); // 포트 번호 설정

    // 소켓에 주소 바인딩
    if (bind(nSFd, (struct sockaddr*)&stSAddr, sizeof(stSAddr)) < 0) {
        printf("Socket Binding Failure.\n"); 
        close(nSFd);
        return -1;
    }

    // 연결 대기 상태로 소켓 설정
    if (listen(nSFd, nBacklog) != 0) { 
        printf("Listening Failure.\n"); 
        close(nSFd);
        return -1;
    }

    return nSFd;
}

// TCP 서버에 연결하는 함수
// strIpAddress: 서버의 IP 주소
// nPort: 서버의 포트 번호
// 반환값: 성공 시 클라이언트 소켓 파일 디스크립터, 실패 시 -1
int connect_to_tcp_server(char* strIpAddress, uint16_t nPort)
{
    struct sockaddr_in stCAddr; 
    int nCFd = -1;

    memset(&stCAddr, 0, sizeof(stCAddr));

    // 소켓 생성
    nCFd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); 
    if (nCFd < 0) {
        printf("Creating a socket was failed.\n");
        return -1;
    }

    stCAddr.sin_family = AF_INET;
    stCAddr.sin_addr.s_addr = (in_addr_t)inet_addr(strIpAddress); // 서버 IP 주소 설정
    stCAddr.sin_port = htons(nPort); // 서버 포트 번호 설정

    // 서버에 연결
    if (connect(nCFd, (struct sockaddr*)&stCAddr, sizeof(stCAddr)) == 0) {
        printf("Connection Success.\n");
    }
    else {
        printf("Connection Failure.\n"); 
        close(nCFd);
        nCFd = -1;
    }

    return nCFd;
}

 

TimeServer.c

 

#include <unistd.h> 
#include <stdio.h>
#include <stdlib.h> 
#include <sys/types.h> 
#include <sys/socket.h> 
#include <arpa/inet.h> 
#include <sys/time.h> 
#include <time.h>
#include "Network_Common.h"

// 클라이언트에게 시간 정보를 전송하는 함수
// nCFd: 클라이언트 소켓 파일 디스크립터
int sending_time_info(int nCFd);

int main(int argc, char* argv[])
{
    int nSFd; // 서버 소켓 FD
    int nCFd; // 클라이언트 소켓 FD

    struct sockaddr_in cAddr; // 클라이언트 주소
    socklen_t nCAddr; // 클라이언트 주소 길이

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

    printf("Port %s\n", argv[1]);
    nSFd = start_tcp_server(atoi(argv[1]), 1); // TCP 서버 시작

    if (nSFd > 0) {
        printf("TCP Server is started.\n");
    }
    else {
        printf("Starting Server is failed.\n");
        return -1;
    }

    nCFd = accept(nSFd, (struct sockaddr*)&cAddr, &nCAddr); // 클라이언트 연결 수락
    if (nCFd > 0) {
        printf("Client Info:\n");
        printf("\t- IP Address: %s\n", inet_ntoa(cAddr.sin_addr));
        printf("\t- Port: %d\n", ntohs(cAddr.sin_port));
        sending_time_info(nCFd); // 클라이언트에게 시간 정보 전송
        close(nCFd);
    }
    else {
        printf("Connection Failure.\n");
    }
    close(nSFd);
    return 0;
}

// 클라이언트에게 시간 정보를 전송하는 함수
// nCFd: 클라이언트 소켓 파일 디스크립터
int sending_time_info(int nCFd)
{
    char strBuffer[BUFSIZ];
    int nBufferLen = 0;
    for (int i = 0; i < 10; i++) {
        struct timeval stTimeval; 
        struct tm stTm;
        gettimeofday(&stTimeval, 0); // 현재 시간 정보 얻기
        localtime_r(&stTimeval.tv_sec, &stTm); // 로컬 시간으로 변환

        // 시간 정보를 버퍼에 저장
        nBufferLen = sprintf(strBuffer,
            "%04d/%02d/%02d %02d:%02d:%02d.%03d\n",
            stTm.tm_year + 1900,
            stTm.tm_mon + 1,
            stTm.tm_mday,
            stTm.tm_hour,
            stTm.tm_min,
            stTm.tm_sec,
            stTimeval.tv_usec / 1000);

        send(nCFd, strBuffer, nBufferLen, 0); // 클라이언트에게 버퍼의 내용 전송
        sleep(1); // 1초 대기
    }
    return 0
}

 

TimeReceiver.c

 

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

int main(int argc, char* argv[])
{
    int nCFd = -1;                   // 클라이언트 소켓 파일 디스크립터
    char strBuffer[BUFSIZ];          // 수신된 데이터를 저장할 버퍼
    int nBufferLen = 0;              // 수신된 데이터의 길이

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

    nCFd = connect_to_tcp_server(argv[1], atoi(argv[2]));  // TCP 서버에 연결

    if (nCFd < 0) {
        return -1;
    }

    while ((nBufferLen = recv(nCFd, strBuffer, BUFSIZ, 0)) > 0) {
        printf("Recv: %s", strBuffer);                     // 수신된 데이터를 출력
    }

    printf("Bye!!\n");
    close(nCFd);
    return 0;
}

이 코드는 TCP 클라이언트를 생성하고 지정된 서버 IP와 포트에 연결합니다. 그 후 서버로부터 전송되는 데이터를 수신하고 출력합니다.

  • int nCFd = -1;: 클라이언트 소켓 파일 디스크립터를 초기화합니다. 
  • char strBuffer[BUFSIZ];: 수신된 데이터를 저장할 버퍼를 선언합니다. 
  • int nBufferLen = 0;: 수신된 데이터의 길이를 저장합니다. 
  • if (argc != 3) { ... }: 프로그램 실행 시 커맨드 라인 인수로 IP 주소와 포트 번호를 받습니다. 
  • 인수가 3개가 아닐 경우 사용법을 출력하고 프로그램을 종료합니다. 
  • nCFd = connect_to_tcp_server(argv[1], atoi(argv[2]));: 지정된 IP 주소와 포트 번호로 TCP 서버에 연결합니다. 
  • while ((nBufferLen = recv(nCFd, strBuffer, BUFSIZ, 0)) > 0) { ... }: 연결된 서버로부터 데이터를 수신합니다. 
  • 수신된 데이터를 출력합니다. printf("Bye!!\n");: 
  • 모든 데이터 수신이 완료되면 종료 메시지를 출력합니다. close(nCFd);: 클라이언트 소켓을 닫습니다. return 0;: 프로그램을 성공적으로 종료합니다.

서버에 포트번호 만들기

 

만들어 진것을 확인 할 수 있음 1개의 요청을 담아 둘 수 있음

초당 정보가 나타나는 것을 확인 할 수 있고 10번 실행후 종료 되는 것을 확인 할 수 있음 

현제  서버쪽의 백로그가 하나만 있기 때문에  돌아가는 동안 또 다른 클라이언트 접속에 대한 처리가 불가능함 

 

 

이런것을 해결하기 위해서 서버가 현제 작업중일때 다른 클라이언트 처리를 하기위해 자기 자신을 복사 해주면 해결할 수 있음

 

fork.c

 

부모와 복제된 자식 프로세스를 확인 할 수 있음 

#include <sys/types.h> 
#include <unistd.h>
#include <stdio.h>

int main(void)
{
    int nPid = 0;           // 프로세스 ID를 저장할 변수

    nPid = fork();          // 현재 프로세스를 복제하여 자식 프로세스를 생성하고, 부모 프로세스에는 자식 프로세스의 ID를 반환하고 자식 프로세스에는 0을 반환합니다.

    if (nPid == 0) {
        printf("I am a child. (PID: %d)\n", nPid);   // 자식 프로세스인 경우 자식 프로세스의 ID를 출력합니다.
    }
    else {
        printf("I am a parent. (PID: %d)\n", nPid);  // 부모 프로세스인 경우 자식 프로세스의 ID를 출력합니다.
    }

    return 0;
}

 

TimeServer_fork.c

 

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

#include "Network_Common.h"

int sending_time_info(int nCFd);

int main(int argc, char* argv[]) {
    int nSFd;
    int nCFd;

    struct sockaddr_in cAddr;
    socklen_t nCAddr;

    // 명령행 인수로 포트를 입력받음
    if (argc = 2) {
        printf("usage: %s <port>\n", argv[0]);
        return -1;
    }

    printf("Port %s\n", argv[1]);

    // TCP 서버 시작
    nSFd = start_tcp_server(atoi(argv[1]), 10); // 10개의 클라이언트 접속 허용
    if (nSFd > 0) {
        printf("TCP Server is started.\n");
    } else {
        printf("Starting Server is failed.\n");
        return -1;
    }

    do {
        // 클라이언트 연결 수락
        nCFd = accept(nSFd, (struct sockaddr*)&cAddr, &nCAddr);
        if (nCFd > 0) {
            printf("Client Info:\n");
            printf("\t- IP Address : %s\n", inet_ntoa(cAddr.sin_addr));
            printf("\t- Port : %d\n", ntohs(cAddr.sin_port));

            // 자식 프로세스를 생성하여 클라이언트 연결 처리
            // 각 클라이언트를 위한 기능을 수행
            if (fork() == 0) {
                sending_time_info(nCFd);
                close(nCFd);
                return 0;
            }

            close(nCFd);
        } else {
            printf("Connection Failure.\n");
        }
    } while (1); // 무한 루프를 걸었음 
    // 클라이언트 요청 수락 받고 처리를 하는 중에 accept를 이용해 또다른 클라리언트 접속을 받는 구조임

    wait(NULL);

    close(nSFd);
    return 0;
}

// 클라이언트에게 시간 정보 전송
int sending_time_info(int nCFd) {
    char strBuffer[BUFSIZ];
    int nBufferLen = 0;

    for (int i = 0; i < 10; i++) {
        struct timeval stTimeval;
        struct tm stTm;

        gettimeofday(&stTimeval, 0);
        localtime_r(&stTimeval.tv_sec, &stTm);
        nBufferLen = sprintf(strBuffer,
                             "%04d/%02d/%02d %02d:%02d:%02d.%03d\n",
                             stTm.tm_year + 1900,
                             stTm.tm_mon + 1,
                             stTm.tm_mday,
                             stTm.tm_hour,
                             stTm.tm_min,
                             stTm.tm_sec,
                             stTimeval.tv_usec / 1000);

        send(nCFd, strBuffer, nBufferLen, 0);

        sleep(1);
    }

    return 0;
}

이전과 다르게 각각의 클라이언트에서 연결되어 정보가 나오는 것을 확인 할 수가 있음 최대 10개 까지 나오게 됨 

 

 

blocking_read.c

- 사용자가 입력하는 것을 항상 대기하고 있어야 하는데 그것을  blocking이라고함 

#include <stdio.h>
#include <string.h>
#include <unistd.h>

int main(void)
{
    char strBuffer[BUFSIZ]; // 버퍼 배열 선언
    int nBufferLen = 0; // 버퍼 길이 변수 초기화

    for (int i = 0;; i++)
    {
        memset(strBuffer, 0, BUFSIZ); // 버퍼 배열 초기화

        nBufferLen = read(0, strBuffer, BUFSIZ); // 표준 입력으로부터 데이터 읽기
        if (nBufferLen <= 0)
        {
            break; // 읽은 데이터가 없거나 오류가 발생하면 반복문 탈출
        }
        else
        {
            printf("Recv: %s", strBuffer); // 읽은 데이터 출력
        }
        sleep(1); // 1초 대기
        printf("Step: %d\n", i); // 현재 단계 출력
    }
    return 0;
}

키보드 입력이 없으면 무한대로 기다림

ctrl + d  : 명령 입력의 끝이다

 

이 blocking을 해결하기 위해서 네트워크 발생하는 부분에서 blocking 해당하는 부분을 따로 분리 시키고

네트워크 부분만 계속 돌리면 됨 방법은 2가지 fork, thread 기법이 있음

fork는 별도의 프로세스로 돌아가기 때문에 키보드 입력 받는 것을 옮기는것이 쉽지 않음

 

thread를 사용하면 같은 본체에서 따로 따로 분리 시키는게 가능함 전체적인 공간을 같이 사용하며

 

pthread.c

- 메인 스레드와 새로운 스레드를 생성하고, value 변수를 공유하는 예제

#include <stdio.h> 
#include <stdlib.h>
#include <pthread.h>
#include <string.h>
#include <unistd.h>

void *thread_function(void *arg); // 스레드 함수의 선언

int main(void)
{
	int value = 10; // 변수 value 초기화
	pthread_t pthread; // pthread_t 타입의 변수 pthread 선언

	pthread_create(&pthread, NULL, thread_function, (void *)&value); // 새로운 스레드 생성

	for (int i = 0; i < 10; i++)
	{
		printf("Value: %d\n", value); // 현재 value 값 출력
		sleep(1); // 1초 대기
	}

	pthread_join(pthread, NULL); // 생성한 스레드가 종료될 때까지 대기

	return 0;
}

void *thread_function(void *arg)
{
	int *value = (int *)arg; // 전달받은 인자를 int 포인터로 캐스팅하여 변수 value에 대입

	for (int i = 0; i < 10; i++)
	{
		(*value) ++; // value 값을 1씩 증가
		sleep(1); // 1초 대기
	}

	return NULL;
}

쓰레드는 별도의 스케줄러의 의해 돌기 때문에 미묘한 시간 차이 때문에 같은 값 혹은 예상한 값과 조금 다른 값이 나오는 것을 확인 할 수 있음 

 

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

#include "Network_Common.h"

void *keyboardReader(void *p_nCFd); // 키보드 입력을 읽는 스레드 함수의 선언
void receiver(int nCFd); // 데이터를 수신하는 함수의 선언

int g_bRunning = 1; // 전역 변수 g_bRunning 초기화

int main(int argc, char* argv[])
{
    int nSFd;
    int nCFd;

    struct sockaddr_in cAddr;
    socklen_t nCAddr = sizeof(struct sockaddr_in); // nCAddr 초기화 추가

    if (argc != 2) { // 입력 인자의 개수 확인
        printf("usage: %s <port>\n", argv[0]);
        return -1;
    }

    printf("Port %s\n", argv[1]);
    nSFd = start_tcp_server(atoi(argv[1]), 1);

    if (nSFd > 0) {
        printf("TCP Server is started.\n");
    } else {
        printf("Starting Server is failed.\n");
        return -1;
    }

    nCFd = accept(nSFd, (struct sockaddr*)&cAddr, &nCAddr); // 클라이언트의 연결 수락

    if (nCFd > 0) {
        printf("Client Info:\n");
        printf("\t- IP Address : %s\n", inet_ntoa(cAddr.sin_addr));
        printf("\t- Port : %d\n", ntohs(cAddr.sin_port));

        pthread_t pthread_KeyboardReader; // 키보드 입력 스레드를 위한 pthread_t 변수 추가

        pthread_create(&pthread_KeyboardReader, NULL, keyboardReader, (void*)&nCFd); // 키보드 입력 스레드 생성
        receiver(nCFd); // 데이터 수신 함수 호출
        pthread_join(pthread_KeyboardReader, NULL); // 키보드 입력 스레드의 종료를 대기
        close(nCFd); // 클라이언트 소켓 닫기
    } else {
        printf("Connection Failure.\n");
    }

    close(nSFd); // 서버 소켓 닫기
    return 0;
}

void receiver(int nCFd)
{
    char szBuffer[BUFSIZ];
    int nLen = 0;
    do {
        bzero(szBuffer, BUFSIZ); // szBuffer 초기화
        nLen = recv(nCFd, szBuffer, BUFSIZ, 0); // 클라이언트로부터 데이터 수신
        if (nLen > 0) {
            printf("Recv: %s", szBuffer); // 수신한 데이터 출력
        }
    } while (nLen > 0); // 수신된 데이터가 있을 경우 반복
    printf("receiver function is finished.\n"); // 데이터 수신 함수 종료 메시지 출력
}

void *keyboardReader(void *p_nCFd)
{
    int nCFd = *(int*)p_nCFd; // 전달받은 클라이언트 소켓 번호를 int 형으로 변환하여 변수에 저장
    char szBuffer[BUFSIZ];
    int nLen = 0;

    while ((nLen = read(0, szBuffer, BUFSIZ)) > 0) { // 표준 입력에서 데이터를 읽어옴
        send(nCFd, szBuffer, nLen, 0); // 클라이언트로 데이터 전송
        if (strncasecmp(szBuffer, "exit", 4) == 0) // "exit"가 입력되면 종료
            break;
    }

    g_bRunning = 0; // g_bRunning을 0으로 설정하여 메인 스레드의 종료 조건 설정
    return NULL; // 스레드 함수는 void* 타입의 포인터를 반환해야 함
}

telnet을 통해 접근하면  리시버가 계속 돌면서 키보드 입력받기 위한 쓰레드가 돌고있음

값을 입력하면 

아래 서버 쪽에서 동시에 출력 되는 것을 확인 할 수 있음

blocking 부분에 대한 사항이 어느 정도 해결 되었음 

728x90