Part1_TCP 기반 서버 클라이언트 1

·2023년 11월 1일
0

연결 요청 대기 상태로의 진입

bind 함수 호출을 통해 소켓에 주소를 할당했다면 listen 함수를 통해 ‘연결 요청 대기 상태’로 들어가야 함

#include <sys/type.h>

int listen(int s, int backlog);

// s : '연결 요청 대기 상태'에서 클라이언트의 연결 요청을 받아들이는 역할을 하게 될 소켓의
// 파일 디스크립터를 인자로 전달하게 됨 -> 이 소켓을 서버 소켓이라고 함
// backlog : 연결 요청 대기 큐의 크기를 나타냄
// 인자로 5가 들어오면 큐의 크기가 5가 되어 클라이언트의 연결 요청을 5개까지 대기시킬 수 있게 됨

서버가 연결 요청 대기 상태에 있다는 것은 클라이언트가 연결 요청을 했을 때 연결이 수락될 때까지 연결 요청 자체를 대기시킬 수 있는 상태에 있다는 것을 의미

클라이언트 연결 요청도 인터넷을 통해 흘러 들어오는 일종의 데이터 전송이기 때문에 이것을 받아들이기 위한 소켓이 필요함 → 서버 소켓
⇒ 즉, listen 함수가 호출되면 문지기 역할을 하는 서버 소켓을 만들어 주고, 두 번재로 정달되는 정수를 참조해서 대기실(연결 요청 대기 큐)을 만듬

대기 큐와 서버 소켓이 준비되어서 클라이언트 연결 요청을 받아들일 수 있는 상태를 ‘연결 요청 대기 상태’라고 함

[연결 요청 수락 하기]

대기 큐에 들어 있는 연결 요청을 순서대로 수락해야 함

수락한다는 것은 요청한 클라이언트와 데이터를 주고 받을 수 있는 상태가 되는 것을 의미

→ 당연히 소켓이 필요함

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

int accept(int s, struct sockaddr* addr, int* addrlen);

// 성공 시 소켓의 새로 생성된 파일 디스크립터를 실패 시 -1 리턴
// s : 서버 소켓의 파일 디스크립터를 인자로 전달
// addr : 연결 요청을 수락할 클라이언트 주소 정보를 저장할 변수의 포인터임
// 함수 호출이 성공하게 되면 addr이 가리키는 변수에는 클라이언트의 주소 정보로 채워지게 됨
// addrlen : 함수 호출 시 인자로 전달된 addr 포인터가 가리키는 구조체의 크기를 저장하고 있는
// 변수의 포인터를 넘겨줌
// 함수 호출이 성공적으로 끝나게 되면 addrlen 포인터가 가리키는 변수 안에는 리턴 받은
// 클라이언트의 주소 정보 길이가 바이트 단위로 채워지게 됨

accept 함수는 호출 성공 시 내부적으로 클라이언트와의 데이터 입/출력을 하기 위해 사용될 소켓을 생성하고, 그 소켓의 파일 디스크립터를 리턴해 줌 (소켓을 알아서 생성해줌!!)

[Hello World 서버 프로그램 다시 보기]

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

void error_handling(char* message);

int main(int argc, char** argv)
{
	int serv_sock;
	int clnt_sock;
	struct sockaddr_in serv_addr;
	struct sockaddr_in clnt_addr;
	int clnt_addr_size;
	char message[]="Hello World!\n";
	
	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_addr, 0, sizeof(serv_addr));
	serv_addr.sin_family=AF_INET;
	serv_addr.sin_addr.s_addr=htonl(INADDR_ANY);
	serv_addr.sin_port=htons(atoi(argv[1]));

	if(bind(serv_sock, (struct sockaddr*) &serv_addr,
			sizeof(serv_addr)) == -1) // 소켓에 주소 할당
		error_handling("bind() error");

	if(listen(serv_sock, 5) == -1) // 연결 요청 대기 상태로 진입
		error_handling("listen() error");

	clnt_addr_size = sizeof(clnt_addr);
	// 연결 요청 수락
	clnt_sock = accept(serv_sock, (struct sockaddr*) &clnt_addr, &clnt_addr_size);
	
	if(clnt_sock == -1)
		error_handling("accept() error");

	// 데이터 전송
	write(clnt_sock, message, sizeof(message));
	close(clnt_sock); // 연결 종료
	return 0;
}

void error_handling(char* message)
{
	fputs(message, stderr);
	fputc('\n', stderr);
	exit(1);
}
  • 서버 프로그램의 구현 순서에서 가장 먼저 해야 하는 일은 서버 소켓의 생성 (socket 함수)
  • 소켓에 주소 할당 (bind 함수)
  • 연결 요청 대기 상태로 진입 (listen 함수)
  • 클라이언트 요청 수락 (accept 함수)
  • 데이터 주고 받기 (여기서는 write)
  • 소켓을 닫아 주며 연결 종료 (close)

0개의 댓글