관리 메뉴

너와 나의 스토리

[CH.4-2,3] TCP기반 서버, 클리이언트의 구현 - Iterative 기반으로 구현 본문

Computer Networks/실습

[CH.4-2,3] TCP기반 서버, 클리이언트의 구현 - Iterative 기반으로 구현

노는게제일좋아! 2019. 10. 9. 12:41
반응형

TCP 서버의 기본적인 함수 호출 순서

 

 

 

 

listen() 함수를 호출하면 연결 요청이 가능한 상태가 된다.

이 listen() 함수의 호출이 의미하는 바를 알아보자

 

 

 

 

 

 

 

 

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

  • 연결 요청도 일종의 데이터 전송이다. 따라서 연결 요청을 받아들이기 위해서는 하나의 소켓이 필요하다. 
  • 이 소켓을 가리켜 서버 소켓 또는 리스닝 소켓이라고 한다.
  • listen 함수의 호출은 소켓을 리스닝 소켓이 되게 한다.

 

클라이언트의 연결 요청 수락

 

  • 연결 요청 정보를 참조하여 클라이언트 소켓과의 통신을 위한 별도의 소켓을 추가로 하나 더 생성한다.

 

  • 이렇게 생성된 소켓을 대상으로 데이터의 송수신이 진행된다. 

 

 

 

 

 

TCP 클라이언트의 기본적인 함수호출 순서

 

 

 

connect() 함수 호출할 때 연결할 서버의 주소 정보도 함께 전달

 

 

 

 

 

 

 

 

TCP 기반 서버, 클라이언트의 함수 호출 관계

 

 

 

 

 

 

서버의 listen() 함수 이후에야 클라이언트의 connect() 함수 호출이 유효하다.

-> listen()을 해야지만 연결 요청을 받을 수 있는 상태가 되기 때문

 

 

 

 

 

 

Iterative 서버의 구현

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

iterative 서버 코드

//iterative_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, clnt_sock;
	char message[BUF_SIZE];
	int str_len, i;

	struct sockaddr_in serv_adr;
	struct sockaddr_in clnt_adr;
	//sockaddr_in은 IPv4 뒤에 8비트를 0으로 쉽게 넣기 위해 사용하는 것

	socklen_t clnt_adr_sz;

	if (argc != 2) { // port번호 길이가 2이여야한다.
		printf("Usage : $s <port>\n", argv[0]);
		exit(1);
	}

	serv_sock = socket(PF_INET, SOCK_STREAM, 0); // 다른 호스트와 통신하며, tcp 프로토콜을 사용한다.
	if (serv_sock == -1)
		error_handling("socket() error");

	memset(&serv_adr, 0, sizeof(serv_adr));  // 뒤에 8바이트는 안써서 0으로 초기화 해주기 위해
	serv_adr.sin_family = AF_INET;
	serv_adr.sin_addr.s_addr = htonl(INADDR_ANY);
	serv_adr.sin_port = htons(atoi(argv[1]));  // 이렇게 하면 자신의 IP가 들어감

	// 여기까지가 정보 초기화
	
	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++) 
	{  // 5번의 연결을 받겠다.
		
		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);
}

 

 

iterative 클라이언트 코드

// iterative_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;

	struct sockaddr_in serv_adr;

	if (argc != 3) {
		printf("Usage : %s <IP> <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);  // fputs, fgets 찾아보기
		fgets(message, BUF_SIZE, stdin);

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

		write(sock, message, strlen(message));
		str_len = read(sock, message, BUF_SIZE - 1);
		message[str_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);
}

 

 

실행 해보기

1. 서버 실행

 

2. 클라이언트1 실행

 

 

 

서버한테 메시지 보내면 서버가 답장

 

 

 

현재 서버 상황

 

2. 클라이언트 5개가 연결 요청 보냄

첫 번째 클라이언트는 연결이 되어 서버로부터 답신이 오고, 나머지 4개의 클라이언트는 대기 상태가 된다.

 

 

3. 첫 번째 클라이언트가 연결을 끊으면

기다리던 두 번째 클라이언트가 연결이 된다.

 

 

 

 

 

 

 

 

 

 

 

출처: [열혈 TCP/IP 소켓 프로그래밍] 

 

반응형
Comments