4-1/인터넷프로토콜

[c++]소켓프로그래밍_visualstudio(2)[코드]

개발자 덕구🐾 2022. 5. 1. 14:39
728x90

이번 포스팅에서는 소켓프로그래밍의  코드부터 직접 실행하는 것까지 다룰 예정이다. 

 

 

 

먼저 앞선 포스팅에서 소켓프로그래밍을 위한 환경설정을 다루었다. 

https://what-am-i.tistory.com/222

 

[c++]소켓프로그래밍_visualstudio(1)[환경설정]

소켓이란 애플리케이션 계층과 전송계층을 연결해주는 인터페이스라고 생각하면 된다. 이를 코드를 이용해 직접 구현해보고 실행하여 결과를 확인해보자 2개로 글을 나누었다. 코드 부분의 글

what-am-i.tistory.com

 

여기에서 이어서 진행할것이다.

 

 

 

server의 코드 

#include <WinSock2.h>
#include <stdlib.h>
#include<stdio.h>

#define SERVERPORT 9000
#define BUFSIZE 512

int main(int argc, char* argv[]) {
	int retval;
	WSADATA wsa;
	if (WSAStartup(MAKEWORD(2, 2), &wsa) != 0) {
		return 1;
	}

	SOCKET listen_sock = socket(AF_INET, SOCK_STREAM, 0);

	SOCKADDR_IN serveraddr;
	ZeroMemory(&serveraddr, sizeof(serveraddr));
	serveraddr.sin_family = AF_INET;
	serveraddr.sin_addr.s_addr = htonl(INADDR_ANY); // 내 IP주소를 network서버 order로 변경하여 저장 
	serveraddr.sin_port = htons(SERVERPORT);
	bind(listen_sock, (SOCKADDR*)&serveraddr, sizeof(serveraddr));
	listen(listen_sock, SOMAXCONN); //하부 프로토콜의 최대의 큐의 크기로 동시에 받아들일 수 있도록한다.

	SOCKET client_sock;
	SOCKADDR_IN clientaddr;
	int addrlen;
	char buf[BUFSIZE + 1];


	while (1) {
		addrlen = sizeof(clientaddr);
		client_sock = accept(listen_sock, (SOCKADDR*)&clientaddr, &addrlen);

		printf("\n[TCP 서버] 클라리언트 접속 : IP 주소 = %s, 포트 번호 = %d\n",
			inet_ntoa(clientaddr.sin_addr), ntohs(clientaddr.sin_port));

		while (1) {
			retval = recv(client_sock, buf, BUFSIZE, 0);
			buf[retval] = '\0';
			printf("\n[TCP %s : %d ] %s\n", inet_ntoa(clientaddr.sin_addr),
				ntohs(clientaddr.sin_port), buf);
			retval = send(client_sock, buf, retval, 0);
		}

		closesocket(client_sock);
		printf("[TCP 서버] 클라이언트 종료 : IP 주소 =%s, 포트번호 = %d\n",
			inet_ntoa(clientaddr.sin_addr), ntohs(clientaddr.sin_port));
	}

	closesocket(listen_sock);
	WSACleanup();
	return 0;
}

 

 

 

client의 코드 

 

#include <WinSock2.h>
#include <stdlib.h>
#include <stdio.h>

#define SERVERIP "127.0.0.1"
#define SERVERPORT 9000
#define BUFSIZE 512

int main(int argc, char *argv[]) {
	int retval;
	WSADATA wsa;
	if (WSAStartup(MAKEWORD(2, 2), &wsa) != 0) return 1;

	SOCKET sock = socket(AF_INET, SOCK_STREAM, 0);

	SOCKADDR_IN serveraddr;
	ZeroMemory(&serveraddr, sizeof(serveraddr));
	serveraddr.sin_family = AF_INET;
	serveraddr.sin_addr.s_addr = inet_addr(SERVERIP);
	serveraddr.sin_port = htons(SERVERPORT);
	connect(sock, (SOCKADDR*)&serveraddr, sizeof(serveraddr));

	char buf[BUFSIZE + 1];
	int len;

	while (1) {
		printf("\n[보낼 데이터] ");
		if (fgets(buf, BUFSIZE + 1, stdin) == NULL) break;

		len = strlen(buf);
		if (buf[len - 1] == '\n') buf[len - 1] = '\0';

		if (strlen(buf) == 0) break;

		retval = send(sock, buf, strlen(buf), 0);
		printf("[TCP 클라이언트] %d바이트를 보냈습니다.\n", retval);
		
		retval = recv(sock, buf, retval, 0);
		if (retval == 0) break;

		buf[retval] = '\0';
		printf("[TCP 클라이언트] %d바이트를 받았습니다.\n", retval);
		printf("[받은 데이터] %s\n", buf); 
	}

	closesocket(sock);

	WSACleanup();
	return 0;
}

 

 

 

이렇게 코드를 만들고 

각각 우측상단의 빌드 -> 솔루션 빌드를 눌러준다.

 

그리고 서버 먼저 ctrl + f5를 이용해 실행시키고 client를 실행시켜 주면 둘은 connect되어 echo로 대화프로그램을 실행시킬 수 있다. 

 

 

 

 

다음과 같이 client에서 보내는 말을 서버에서 보여주고 다시 보내는 것을 확인할 수 있다.

 

 

 

 

 

 

 

코드에 대해서 설명을 한다면 

 

 

WSAStartup() 은 실행에 필요한 라이브러리를 초기화한다. 

 

socket은 사용할 소켓을 생성한다. SOCK_STREAM은 TCP를 의미하고 SOCK_DGRAM은 UDP를 의미한다.

 

bind에서는 생성한 소켓과 주소, 포트를 연결한다.

 

listen은 client가 연결을 시도하도록 기다린다. somaxconn은 주석에도 써놓았듯이 TCP가 감당할 수 있는 동시에 접속 요청을 받을 수 있는 최대 큐의 개수로 설정한다. 

 

accept은 접속요청을 한 client와의 연결을 허가하고 연결을 위해 새로운 소켓핸들을 만들어 반환한다. 

여기서 새로운 소켓핸들은 client_sock이다. 왜냐하면 listen_sock는 다른 client들의 연결도 허가하는 일을 해야하기 때문이다. 

 

 

그리고 recv, send를 이용해 전달할 말을 보내고 받는다. 

 

 

위 작성된 코드는 echo 코드로 client가 입력한 말을 그래도 server가 echo 한다.

이를 제대로된 채팅 프로그램으로 만드는 방법은 코드를 recv, send하는 부분을 약간만 바꿔주면 된다. 

 

 

server의 while(1)의 코드를 

 

while (1) {
			retval = recv(client_sock, buf, BUFSIZE, 0);
			buf[retval] = '\0';
			printf("\n[TCP %s : %d ] %s\n", inet_ntoa(clientaddr.sin_addr),
				ntohs(clientaddr.sin_port), buf);
			
			printf("\n[보낼 데이터] ");
			if (fgets(buf, BUFSIZE + 1, stdin) == NULL) break;

			len = strlen(buf);
			if (buf[len - 1] == '\n') buf[len - 1] = '\0';
			if (strlen(buf) == 0) break;

			retval = send(client_sock, buf, strlen(buf), 0);
		}

이처럼 fgets를 이용해 입력받아 보내는 코드를 추가하고 

 

 

 

 

client의 while(1)의 코드를 

while (1) {
		printf("\n[보낼 데이터] ");
		// 입력받아 server에게 전송
		if (fgets(buf, BUFSIZE + 1, stdin) == NULL) break;
		len = strlen(buf);
		if (buf[len - 1] == '\n') buf[len - 1] = '\0';
		if (strlen(buf) == 0) break;
		retval = send(sock, buf, strlen(buf), 0);
		printf("[TCP 클라이언트] %d바이트를 보냈습니다.\n", retval);
		
		retval = recv(sock, buf, BUFSIZE, 0);
		if (retval == 0) break;

		buf[retval] = '\0';
		printf("[TCP 클라이언트] %d바이트를 받았습니다.\n", retval);
		printf("[받은 데이터] %s\n", buf); 
	}

이렇게 수정하면 된다.

 

 

 

결과는 다음과 같다. 

 

 

hi와 hello를 잘 전달하고 받았음을 확인할 수 있다. 

 

 

반응형