저번 주 다량의 클라이언트 접근하기 위해 스레드, 포크 기법 사용해 봄
+ 블록킹 해결하기 위해서 스레드 기법 사용해 봄
이번 주차는 멀티플렉스라는 방법 사용해 볼 것임
이벤트를 모니터링하고 이벤트 오면 그때 read
블록킹 해결 가능
그중 멀티플렉스에서 SELECT, poll 기법을 사용함
read.c
#include <stdio.h>
#include <string.h>
#include <strings.h>
#include <unistd.h>
#include <time.h>
int main(void)
{
time_t nTime; // 시간 정보를 담을 변수
char strBuffer[BUFSIZ]; // 문자열을 저장할 버퍼
int nBufferLen = 0; // 버퍼의 길이를 저장할 변수
for(int i = 0; ; i++) // 무한 루프
{
memset(strBuffer, 0, BUFSIZ); // 버퍼 초기화
nBufferLen = read(0, strBuffer, BUFSIZ); // 사용자 입력을 버퍼에 저장하고 길이를 저장
if (nBufferLen > 0) // 입력 길이가 0보다 큰 경우
{
if(strncasecmp(strBuffer, "exit", 4) == 0) // 입력이 "exit"인 경우
break; // 루프를 종료
printf("[%02d] : %s", nBufferLen, strBuffer); // 입력의 길이와 내용을 출력
}
sleep(5); // 5초 동안 대기
time(&nTime); // 현재 시간을 nTime 변수에 저장
printf("Current: %s", ctime(&nTime)); // 현재 시간 출력
}
return 0; // 프로그램 종료
}
- 사용자로부터 입력된 문자열을 저장할 버퍼 strBuffer와 버퍼의 길이를 저장할 변수 nBufferLen을 선언합니다.
- 무한 루프를 시작합니다. 루프 내부에서는 다음 작업을 반복합니다.
- memset 함수를 사용하여 strBuffer를 초기화합니다.
- read 함수를 사용하여 표준 입력에서 문자열을 읽어 strBuffer에 저장하고, 입력된 문자열의 길이를 nBufferLen에 저장합니다.
- 만약 입력된 문자열의 길이가 0보다 큰 경우, 즉 사용자가 입력을 하였을 때, 아래의 조건을 확인합니다.
- 입력된 문자열이 "exit"인 경우, 루프를 종료하여 프로그램이 종료됩니다.
- 그렇지 않은 경우, 입력된 문자열의 길이와 내용을 출력합니다.
- sleep 함수를 사용하여 5초 동안 프로그램을 일시 정지시킵니다.
- time 함수를 사용하여 현재 시간을 nTime 변수에 저장하고, 현재 시간을 출력합니다.
- 다시 루프의 처음으로 돌아가 입력 대기 상태로 진입합니다.
즉, 이 코드는 사용자로부터 문자열을 입력받고, 입력된 문자열을 출력하며, 입력이 "exit"일 경우 프로그램을 종료하는 기능을 수행합니다. 또한 5초마다 현재 시간을 출력하여 프로그램이 동작 중임을 보여줍니다.
해당코드에서는 블록킹이 발생하는 문제가 있습니다.
글 입력 시 길이와 데이터가 나오고
입력한 시간이 출력됨
글을 입력하지 않아도 5초 뒤에 시간이 출력되는 것을 확인할 수 있음 계속 감시하고 있기 때문
select_read.c
#include <stdio.h>
#include <stdlib.h>
#include <sys/select.h>
#include <string.h>
#include <sys/types.h>
#include <time.h>
#include <unistd.h>
int main(void)
{
fd_set rfds; // 파일 디스크립터 집합을 저장할 변수
struct timeval tv; // select 함수의 타임아웃 값을 저장할 구조체
int nRet; // select 함수의 반환값을 저장할 변수
time_t nTime; // 시간 정보를 담을 변수
char szBuffer[BUFSIZ]; // 문자열을 저장할 버퍼
int nBufferLen = 0; // 버퍼의 길이를 저장할 변수
do {
FD_ZERO(&rfds); // rfds 집합 초기화
FD_SET(0, &rfds); // 표준 입력 파일 디스크립터를 rfds 집합에 추가
tv.tv_sec = 5; // 타임아웃 시간 설정 (5초)
tv.tv_usec = 0;
nRet = select(1, &rfds, NULL, NULL, &tv); // select 함수를 사용하여 입력 대기
if (nRet > 0) {
if (FD_ISSET(0, &rfds)) { // 표준 입력에 변화가 있는지 확인
memset(szBuffer, 0, BUFSIZ); // 버퍼 초기화
nBufferLen = read(0, szBuffer, BUFSIZ); // 사용자 입력을 버퍼에 저장하고 길이 저장
if (nBufferLen > 0) {
if (strncasecmp(szBuffer, "exit", 4) == 0)
break; // 입력이 "exit"인 경우 루프를 종료
printf("[%02d] : %s", nBufferLen, szBuffer); // 입력의 길이와 내용을 출력
}
}
} else {
time(&nTime); // 현재 시간을 nTime 변수에 저장
printf("Current: %s", ctime(&nTime)); // 현재 시간 출력
}
} while (1);
return 0; // 프로그램 종료
}
- do-while 루프를 실행하여 프로그램이 무한히 반복됩니다.
- FD_ZERO 함수를 사용하여 rfds 변수의 파일 디스크립터 집합을 초기화합니다.
- FD_SET 함수를 사용하여 표준 입력 파일 디스크립터를 rfds 집합에 추가합니다.
- select 함수를 호출하여 입력 대기를 수행합니다. select 함수는 지정된 시간 동안 입력 변화를 감지합니다.
- 만약 select 함수가 반환한 값인 nRet이 0보다 크다면, 입력 변화가 있음을 의미합니다.
- FD_ISSET 함수를 사용하여 표준 입력 파일 디스크립터에 변화가 있는지 확인합니다.
- 변화가 있다면, read 함수를 사용하여 사용자로부터 입력을 읽어 szBuffer 버퍼에 저장하고, 입력의 길이를 nBufferLen에 저장합니다.
- 만약 입력된 문자열의 길이가 0보다 큰 경우, 즉 사용자가 입력을 하였을 때, 아래의 조건을 확인합니다.
- 입력된 문자열이 "exit"인 경우, 루프를 종료하여 프로그램이 종료됩니다.
- 그렇지 않은 경우, 입력된 문자열의 길이와 내용을 출력합니다.
- 만약 select 함수가 0을 반환한 경우, 즉 입력 변화가 없는 경우, 현재 시간을 얻어와 출력합니다.
- 루프의 조건이 항상 참이므로, 프로그램은 무한히 반복됩니다.
즉, 이 코드는 사용자로부터 문자열을 입력받고, 입력된 문자열을 출력하며, 입력이 "exit"일 경우 프로그램을 종료하는 기능을 수행합니다. 또한 5초마다 현재 시간을 출력하여 프로그램이 동작 중임을 보여줍니다.
블로킹 방지하는 것을 알 수 있음. 클라이언트가 언제 쏴줄지 모르기 때문에 SELECT 등록하고 다른 작업 하면 됨
다른 방법 poll
poll.c
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <poll.h>
#include <time.h>
#include <unistd.h>
#define MAX_FDS 10
int main(void)
{
int nRetval = 0; // poll 함수의 반환값을 저장할 변수
char szBuffer[BUFSIZ]; // 입력된 문자열을 저장할 버퍼
int nBufferLen = 0; // 버퍼의 길이를 저장할 변수
struct pollfd fds[MAX_FDS]; // pollfd 구조체 배열
time_t nPrevTime = 0; // 이전 시간을 저장할 변수
time_t nCurTime = 0; // 현재 시간을 저장할 변수
for (int i = 0; i < MAX_FDS; i++)
{
fds[i].fd = -1; // 파일 디스크립터 초기화
fds[i].events = 0x00; // 이벤트 초기화
fds[i].revents = 0x00; // 이벤트 결과 초기화
}
fds[0].fd = 0; // 표준 입력 파일 디스크립터 설정
fds[0].events = POLLIN; // POLLIN 이벤트 설정
fds[0].revents = 0; // 이벤트 결과 초기화
time(&nPrevTime); // 이전 시간 설정
do {
nRetval = poll(fds, 1, 5000); // 5초 동안 입력 대기
if (nRetval > 0)
{
time(&nPrevTime); // 현재 시간을 이전 시간으로 설정
if (fds[0].revents & POLLIN)
{
bzero(szBuffer, BUFSIZ); // 버퍼 초기화
nBufferLen = 0;
nBufferLen = read(fds[0].fd, szBuffer, BUFSIZ); // 입력을 읽고 버퍼에 저장
printf("[%02d] %s", nBufferLen, szBuffer); // 입력의 길이와 내용을 출력
if (strncasecmp(szBuffer, "EXIT", 4) == 0)
break; // 입력이 "EXIT"인 경우 루프를 종료
}
}
else if (nRetval == 0)
{
time(&nCurTime); // 현재 시간을 얻어옴
if (nCurTime >= nPrevTime + 5)
{
printf("Current: %s", ctime(&nCurTime)); // 현재 시간 출력
nPrevTime = nCurTime; // 이전 시간을 현재 시간으로 업데이트
}
}
else
{
perror("Poll exception!!\n"); // poll 함수 예외 처리
return -1;
}
} while (1);
printf("Exiting.\n");
return 0; // 프로그램 종료
}
1. `poll()` 함수를 사용하여 표준 입력을 비동기적으로 감시합니다.
2. `fds` 배열에 표준 입력 파일 디스크립터(`0`)를 등록하고, `POLLIN` 이벤트를 설정합니다.
3. `poll()` 함수를 호출하여 5초 동안 입력을 대기합니다.
4. 입력이 있으면, 해당 입력을 읽고 버퍼에 저장한 후 입력의 길이와 내용을 출력합니다.
5. 입력이 "EXIT"인 경우 루프를 종료합니다.
6. 입력이 없는 경우, 이전 시간과 현재 시간을 비교하여 5초가 경과한 경우 현재 시간을 출력하고 이전 시간을 업데이트합니다.
7. `poll()` 함수의 반환값이 예외인 경우 오류 메시지를 출력하고 프로그램을 종료합니다.
8. 무한 루프를 통해 사용자가 "EXIT"를 입력할 때까지 프로그램이 실행됩니다.
9. "Exiting." 메시지를 출력하고 프로그램을 종료합니다.
이 프로그램은 입력을 비동기적으로 처리하기 위해 `poll()` 함수를 사용하고, 입력이 없는 경우 일정 시간마다 현재 시간을 출력합니다. 또한, "EXIT" 입력을 받으면 프로그램을 종료합니다.
채팅 프로그램
chat_server.c
#include <stdio.h>
#include <string.h>
#include <strings.h>
#include <sys/poll.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"
#define MAX_CLIENTS 10
int main(int argc, char *argv[])
{
struct sockaddr_in stCAddr;
socklen_t nCAddr;
struct pollfd rfds[MAX_CLIENTS + 2]; // 클라이언트 소켓 및 표준 입력 소켓을 포함한 pollfd 구조체 배열
int nTimeout = 0;
int nRetval;
int nKeepRunning = 1;
for (int i = 0; i < MAX_CLIENTS + 2; i++)
{
rfds[i].fd = -1; // 파일 디스크립터 초기화
rfds[i].events = 0; // 이벤트 초기화
rfds[i].revents = 0; // 이벤트 결과 초기화
}
if (argc != 3)
{
printf("usage: %s <port> <queue>\n", argv[0]);
return -1;
}
printf("Port %s \n", argv[1]);
printf("Queue %s\n", argv[2]);
if (atoi(argv[2]) > MAX_CLIENTS)
{
return -1;
}
rfds[0].fd = 0; // 표준 입력 파일 디스크립터 설정
rfds[0].events = POLLIN; // POLLIN 이벤트 설정
rfds[0].revents = 0; // 이벤트 결과 초기화
rfds[1].fd = start_tcp_server(atoi(argv[1]), atoi(argv[2])); // TCP 서버 소켓 시작
if (rfds[1].fd < 0)
{
printf("Starting server failed. \n");
return -1;
}
rfds[1].events = POLLIN; // POLLIN 이벤트 설정
rfds[1].revents = 0; // 이벤트 결과 초기화
nTimeout = 1000;
do
{
nRetval = poll(rfds, MAX_CLIENTS + 2, nTimeout); // 클라이언트 소켓 및 표준 입력 소켓의 이벤트 감지
if (nRetval > 0)
{
for (int i = 0; i < MAX_CLIENTS + 2; i++)
{
if (rfds[i].fd < 0)
continue;
if (rfds[i].revents & POLLIN) // POLLIN 이벤트가 발생한 경우
{
if (i == 0) // 표준 입력 소켓인 경우
{
char strBuffer[BUFSIZ];
int nBufferLen = 0;
char strBuffer2[BUFSIZ];
int nBufferLen2 = 0;
bzero(strBuffer, BUFSIZ);
bzero(strBuffer2, BUFSIZ);
nBufferLen = read(0, strBuffer, BUFSIZ); // 표준 입력 읽기
printf("Input: %s", strBuffer);
if (nBufferLen > 0)
{
if (strncasecmp(strBuffer, "exit", 4) == 0)
{
nKeepRunning = 0;
break;
}
nBufferLen2 = sprintf(strBuffer2, "[Server] %s", strBuffer); // 서버에서 클라이언트로 보낼 메시지 생성
for (int i = 2; i < MAX_CLIENTS + 2; i++)
{
if (rfds[i].fd < 0)
continue;
send(rfds[i].fd, strBuffer2, nBufferLen2, 0); // 클라이언트로 메시지 전송
}
}
}
else if (i == 1) // 새로운 클라이언트가 연결된 경우
{
printf("Connecting...");
int fd = accept(rfds[i].fd, (struct sockaddr *)&stCAddr, &nCAddr); // 클라이언트 연결 수락
if (fd > 0)
{
for (int i = 2; i < MAX_CLIENTS + 2; i++)
{
if (rfds[i].fd < 0)
{
rfds[i].fd = fd; // 클라이언트 소켓 등록
rfds[i].events = POLLIN; // POLLIN 이벤트 설정
rfds[i].revents = 0; // 이벤트 결과 초기화
char strBuffer[BUFSIZ];
int nBufferLen = 0;
bzero(strBuffer, BUFSIZ);
nBufferLen = sprintf(strBuffer, "[%d] Your ID is [%d]\n", fd, fd);
send(rfds[i].fd, strBuffer, nBufferLen, 0); // 클라이언트에게 ID 정보 전송
break;
}
}
printf("Success!!");
}
printf("\n");
}
else // 클라이언트 소켓에서 데이터를 수신한 경우
{
char strBuffer[BUFSIZ];
int nBufferLen = 0;
bzero(strBuffer, BUFSIZ);
nBufferLen = read(rfds[i].fd, strBuffer, BUFSIZ); // 클라이언트로부터 데이터 읽기
if (nBufferLen <= 0)
{
printf("A Client (%d) is disconnected. \n", rfds[i].fd);
close(rfds[i].fd); // 클라이언트 연결 종료
rfds[i].fd = -1; // 파일 디스크립터 초기화
rfds[i].events = 0; // 이벤트 초기화
rfds[i].revents = 0; // 이벤트 결과 초기화
}
else
{
char strBuffer2[BUFSIZ];
int nBufferLen2 = 0;
bzero(strBuffer2, BUFSIZ);
nBufferLen2 = sprintf(strBuffer2, "[%d] %s",
strBuffer2, rfds[i].fd, strBuffer);
printf("%s", strBuffer2);
for (int i = 2; i < MAX_CLIENTS + 2; i++)
{
if (rfds[i].fd < 0)
continue;
send(rfds[i].fd, strBuffer2, nBufferLen2, 0); // 수신한 데이터를 다른 클라이언트에게 전송
}
}
}
}
}
if (nKeepRunning == 0)
{
break;
}
}
else if (nRetval < 0)
{
break;
}
} while (1);
for (int i = 0; i < MAX_CLIENTS + 2; i++)
{
if (rfds[i].fd > 0)
close(rfds[i].fd); // 모든 클라이언트 소켓 연결 종료
}
printf("Bye~\n");
return 0;
}
이 코드는 단순한 채팅 서버를 구현하는 프로그램입니다. 주요 기능은 다음과 같습니다:
1. TCP 서버 소켓을 생성하고 클라이언트의 연결을 수락합니다.
2. 표준 입력을 통해 서버에게 메시지를 보낼 수 있습니다.
3. 서버는 받은 메시지를 클라이언트에게 전달하며, 클라이언트로부터 수신한 메시지를 다른 클라이언트에게 전송합니다.
4. "exit"라는 메시지를 입력하면 프로그램이 종료됩니다.
이 코드는 `poll` 함수를 사용하여 I/O 멀티플렉싱을 수행합니다. `poll` 함수는 등록된 파일 디스크립터의 이벤트를 감지하고, 해당 이벤트에 대한 처리를 수행합니다. 프로그램은 계속해서 `poll` 함수를 호출하여 이벤트를 감지하고 처리합니다.
`pollfd` 구조체 배열은 클라이언트 소켓 및 표준 입력 소켓에 대한 정보를 저장합니다. 각 구조체의 필드는 다음과 같이 사용됩니다:
- `fd`: 파일 디스크립터 값. `-1`은 미사용 상태를 나타냅니다.
- `events`: 이벤트 타입을 나타내는 비트 마스크. `POLLIN`은 읽기 가능한 데이터를 나타냅니다.
- `revents`: `poll` 함수에 의해 설정되는 실제 이벤트 결과.
코드 내에 `sprintf`, `send`, `read`, `accept` 함수 등 다양한 소켓 및 문자열 처리 함수가 사용되었습니다.
이 코드는 다음과 같은 흐름으로 동작합니다:
1. 먼저, `main` 함수에서 필요한 변수 및 구조체를 초기화합니다. `rfds` 배열은 `pollfd` 구조체를 저장하는 배열로, 클라이언트 소켓과 표준 입력 소켓에 대한 정보를 저장합니다.
2. 프로그램 실행 시 명령줄 인수를 통해 포트 번호와 대기열 크기를 입력받습니다. 이 정보를 기반으로 TCP 서버 소켓을 생성합니다.
3. `poll` 함수를 이용하여 클라이언트 소켓과 표준 입력 소켓의 이벤트를 감지합니다. `poll` 함수의 첫 번째 인수로는 `rfds` 배열을 전달하고, 두 번째 인수로는 배열의 크기를 전달합니다.
4. `poll` 함수가 반환한 값인 `nRetval`을 확인하여 이벤트를 처리합니다. 반환 값이 양수인 경우 이벤트가 발생한 파일 디스크립터를 찾아 해당 이벤트를 처리합니다.
5. 이벤트가 발생한 파일 디스크립터가 표준 입력 소켓인 경우, 표준 입력에서 데이터를 읽어들입니다. 읽은 데이터가 "exit"인 경우 프로그램을 종료하고, 그렇지 않은 경우 서버에서 클라이언트로 메시지를 생성하여 다른 클라이언트에게 전송합니다.
6. 이벤트가 발생한 파일 디스크립터가 서버 소켓인 경우, 새로운 클라이언트가 연결되었음을 의미합니다. `accept` 함수를 사용하여 클라이언트의 연결을 수락하고, 해당 클라이언트 소켓을 `rfds` 배열에 등록합니다. 클라이언트에게는 고유한 ID를 할당하여 전송합니다.
7. 이벤트가 발생한 파일 디스크립터가 클라이언트 소켓인 경우, 해당 클라이언트로부터 데이터를 읽어들입니다. 읽은 데이터를 다른 클라이언트에게 전송합니다.
8. 이벤트 처리가 완료되면 다시 `poll` 함수를 호출하여 새로운 이벤트를 감지합니다. 이 과정은 `nKeepRunning` 변수가 0이 될 때까지 계속됩니다.
9. 프로그램 종료 시 모든 클라이언트 소켓 연결을 종료합니다.
이 코드는 간단한 채팅 서버를 구현한 예시입니다. 클라이언트들은 서버에 연결하여 서로 간의 채팅을 할 수 있습니다. 서버는 받은 메시지를 다른 클라이언트들에게 서버에서 받은 메시지를 다른 클라이언트들에게 전달하여 채팅을 이어나갈 수 있도록 구현되어 있습니다. 클라이언트들은 서버에 연결된 상태에서 메시지를 보낼 수 있고, 서버는 해당 메시지를 받아 다른 클라이언트들에게 전송합니다.
10. 모든 클라이언트 소켓이 종료되거나 "exit" 메시지가 입력되면 `nKeepRunning` 변수가 0이 되어 루프가 종료됩니다.
11. 종료된 후에는 모든 클라이언트 소켓 연결을 종료하고, 메시지를 출력한 뒤 프로그램이 종료됩니다.
이 코드는 단순한 채팅 서버의 동작을 보여주는 예시이며, 클라이언트와 서버 간의 통신을 담당하는 네트워크 함수가 별도의 파일 "Network_Common.h"에 구현되어 있다고 가정합니다. 해당 파일에서는 소켓 생성, 연결 수락, 데이터 송수신 등의 기능을 제공합니다.
서버는 `poll` 함수를 사용하여 I/O 멀티플렉싱을 수행하므로, 동시에 여러 클라이언트와 표준 입력을 처리할 수 있습니다. 이를 통해 클라이언트와 실시간으로 채팅할 수 있는 환경을 구축할 수 있습니다.
상대방이 내 컴퓨터에 접근해 채팅을 보내면 뜨는 것을 확인할 수 있음
채팅앱 성공
서버 끊어버리는 명령 exit
telnet으로 확인
상대방 서버에 접속해서 채팅 입력하면 상대방 서버 컴퓨터에 뜨는 것을 확인 할 수 있음
chat_client.c
#include <stdio.h>
#include <stdlib.h>
#include <sys/poll.h>
#include <string.h>
#include <strings.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <time.h>
#include <unistd.h>
#include "Network_Common.h"
int main(int argc, char *argv[])
{
struct sockaddr_in stCAddr;
socklen_t nCAddr;
struct pollfd rfds[2]; // 표준 입력 및 서버 소켓을 담을 pollfd 구조체 배열
int nTimeout = 0;
int nRetval;
// 변수 초기화
for (int i = 0; i < 2; i++)
{
rfds[i].fd = -1;
rfds[i].events = 0;
rfds[i].revents = 0;
}
if (argc != 3)
{
printf("usage: %s <IP> <Port>\n", argv[0]);
return -1;
}
printf("IP %s\n", argv[1]);
printf("Port %s\n", argv[2]);
rfds[0].fd = 0; // 표준 입력 파일 디스크립터 설정
rfds[0].events = POLLIN; // POLLIN 이벤트 설정
rfds[0].revents = 0; // 이벤트 결과 초기화
rfds[1].fd = connect_to_tcp_server(argv[1], atoi(argv[2])); // TCP 서버에 연결
if (rfds[1].fd < 0)
{
printf("Connecting Server is failed.\n");
return -1;
}
rfds[1].events = POLLIN; // POLLIN 이벤트 설정
rfds[1].revents = 0; // 이벤트 결과 초기화
nTimeout = 1000;
do
{
nRetval = poll(rfds, 2, nTimeout); // 표준 입력 및 소켓의 이벤트 감지
if (nRetval > 0)
{
// 키보드 입력 처리
if (rfds[0].revents & POLLIN) // POLLIN 이벤트가 발생한 경우
{
char strBuffer[BUFSIZ];
int nBufferLen = 0;
bzero(strBuffer, BUFSIZ);
nBufferLen = read(0, strBuffer, BUFSIZ); // 표준 입력 읽기
printf("Input: %s", strBuffer);
if (nBufferLen > 0)
{
if (strncasecmp(strBuffer, "exit", 4) == 0) // "exit" 메시지가 입력된 경우
{
break; // 루프 종료
}
send(rfds[1].fd, strBuffer, nBufferLen, 0); // 서버에 메시지 전송
}
}
// 소켓 수신 처리
if (rfds[1].revents &POLLIN) // POLLIN 이벤트가 발생한 경우
{
char strBuffer[BUFSIZ];
int nBufferLen = 0;
bzero(strBuffer, BUFSIZ);
nBufferLen = read(rfds[1].fd, strBuffer, BUFSIZ); // 소켓으로부터 데이터 읽기
printf("%s", strBuffer);
}
}
else if (nRetval < 0)
{
break;
}
} while (1);
// 소켓 닫기
close(rfds[1].fd);
printf("Bye~\n");
return 0;
}
이 코드는 클라이언트 프로그램으로서, TCP 서버에 연결하고 사용자로부터 키보드 입력을 받아 서버로 전송하며, 서버로부터의 응답을 출력합니다.
1. 헤더 파일 및 라이브러리를 포함합니다.
2. `main()` 함수에서 변수 및 구조체를 초기화합니다.
3. 커맨드 라인 인수를 통해 서버의 IP 주소와 포트 번호를 입력받습니다.
4. 표준 입력 및 소켓 디스크립터를 `pollfd` 구조체에 설정합니다.
5. `connect_to_tcp_server()` 함수를 사용하여 서버에 연결합니다.
6. `poll()` 함수를 사용하여 이벤트를 감지합니다.
7. 표준 입력으로부터 데이터를 읽고, "exit"가 입력되면 루프를 종료합니다.
8. 입력된 데이터를 서버로 전송합니다.
9. 소켓으로부터 데이터를 읽고 출력합니다.
10. 루프를 반복하며 이벤트를 감지하고 처리합니다.
11. 루프 종료 후 소켓을 닫고 프로그램을 종료합니다.
'자료구조, 운영체제, 네트워크, 시스템설계 > 정보보안' 카테고리의 다른 글
UDP 서버 만들기 및 채팅 프로그램 만들기 (0) | 2023.05.30 |
---|---|
TCP 서버, 클라이언트 구축 (0) | 2023.05.14 |
네트워크용기본 API바이트 순서 및 주소체계 변환 호스트정보 취득 및 송수신 함수들 (0) | 2023.05.02 |
파일기술자, 바이트 배열 처리 함수 (0) | 2023.04.18 |