본문 바로가기

개인 공부/네트워크

네트워크P-008 (두 데이터 합 & 1:1 간단한 TCP 채팅 )

TCP 두 수 합 구하는 프로그램 작성

  • 클라이언트에게 두 정수를 입력 받아서 서버가 데이터를 연산하여 보내준다

<TCPServerSum.cpp>

#include "Common.h"
#define SERVERPORT 9000
#define SIZE 512

int main() {

	int retval;

	// ===== 윈속 초기화 =====
	WSADATA wsa;
	if (WSAStartup(MAKEWORD(2, 2), &wsa) != 0)	return 1;

	// ===== 소켓 생성 =====
	SOCKET listenSocket = socket(AF_INET, SOCK_STREAM, 0);	// IPv4, TCP 통신 소켓
	if (listenSocket == INVALID_SOCKET)	printf("Socket ERROR!!!\n");

	// ===== bind() =====	
	struct sockaddr_in serveraddr;	//IPv4 소켓 주소 구조체
	memset(&serveraddr, 0, sizeof(serveraddr));		// 주소 초기화
	serveraddr.sin_family = AF_INET;	// IPv4
	serveraddr.sin_addr.s_addr = htonl(INADDR_ANY);	// INADRR_ANY : 모든 IP에서 접속 가능!!
	serveraddr.sin_port = htons(SERVERPORT);	// h -> n Port Number 설정

	retval = bind(listenSocket, (struct sockaddr*)&serveraddr, sizeof(serveraddr));
	if (retval == SOCKET_ERROR)	printf("BIND() ERROR!!\n");

	// -- LISTEN() -- 대기 상태 변경, 클라이언트 갯수
	retval = listen(listenSocket, SOMAXCONN);
	if (retval == SOCKET_ERROR)	printf("LISTEN() ERROR!!!\n");
	printf("Server Listen...\n");

	// 통신에 사용할 변수 선언
	SOCKET clientSocket;			// 클라이언트 소켓
	struct sockaddr_in clientaddr;	// 클라이언트 소켓 주소(원격 IP / 원격 PORT)
	int addrlen;						// 주소 길이
	int num[2];			// 받은 데이터를 저장할 응용프로그램 버퍼
	int result = 0;		// 받은 데이터 결과

	while (1) {
		// ===== accept() =====
		addrlen = sizeof(clientaddr);
		clientSocket = accept(listenSocket, (struct sockaddr*)&clientaddr, &addrlen);
		if (clientSocket == INVALID_SOCKET) {
			printf("ACCEPT() ERROR!!\n");
			break;
		}

		char addr[INET_ADDRSTRLEN];
		inet_ntop(AF_INET, &clientaddr.sin_addr, addr, sizeof(addr));	//  inet_ntop : 네트워크 바이트 정렬
		printf("TCP Connect : IP = %s, PORT = %d\n", addr, ntohs(clientaddr.sin_port));
		// inet_ntop 네트워크 바이트 정렬(숫자) ->문자열로
		// inet_btop(주소체계, IP주소, 저장공간, 크기)

		while (1) {
			// ===== recv() =====
			retval = recv(clientSocket, (char*)num, sizeof(num), MSG_WAITALL);	// recv(원격소켓, 버퍼, 버퍼크기, 0);
			// 수신 버퍼에 있는 값을 읽어 와서 응용프로그램 버퍼에 복사
			if (retval == SOCKET_ERROR) {	// 오류가 있을 경우
				printf("RECV() ERROR!!!\n");
				break;
			}
			else if (retval == 0)	break; // 정상 종료

		
			printf("[TCP : %s : %d] %d %d\n", addr, ntohs(clientaddr.sin_port), num[0], num[1]);
			// ===== send() =====
			result = num[0] + num[1];
			retval = send(clientSocket, (char*)&result, sizeof(result), 0);
			if (retval == SOCKET_ERROR) {
				printf("SEND() ERROR!!!\n");
				break;
			}
		}
		// 클라이언트 소켓 닫기
		closesocket(clientSocket);
		printf("Client 종료 IP = %s, Port Number = %d\n", addr, ntohs(clientaddr.sin_port));
	}
	// Listen Socket 닫기
	closesocket(listenSocket);

	// 윈속 종료
	WSACleanup();
	return 0;
}
  • 서버는 클라이언트가 로 보내준 데이터를 recv()로 받아서 계산해서 send()로 전달해준다

<TCPClientSum.cpp>

#include "Common.h"
#define SERVERPORT 9000
#define BUFSIZE 512

char* SERVERIP = (char*)"127.0.0.1";

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);
	if (sock == INVALID_SOCKET)	printf("SOCKET ERORR!!!\n");

	struct sockaddr_in serveraddr;
	memset(&serveraddr, 0, sizeof(serveraddr));	// 소켓 초기화
	serveraddr.sin_family = AF_INET;
	inet_pton(AF_INET, SERVERIP, &serveraddr.sin_addr);
	serveraddr.sin_port = ntohs(SERVERPORT);

	// ===== connect() =====
	retval = connect(sock, (struct sockaddr*)&serveraddr, sizeof(serveraddr));
	if (retval == SOCKET_ERROR)	printf("CONNECT() ERORR!!!\n");

	// 데이터 통신에 사용할 변수 
	int num[2];
	int result = 0;

	while (1) {
		// 데이터 입력
		printf("\n[보낼 두 정수 데이터] ");
		scanf("%d %d", &num[0], &num[1]);

		// ===== send() =====
		retval = send(sock, (const char*)num, sizeof(num), 0);
		if (retval == SOCKET_ERROR) {
			printf("SEND() ERROR!!!\n");
			break;
		}
		printf("[TCP Client] %dByte를 보냈습니다\n", retval);

		// ===== recv() =====
		retval = recv(sock, (char*)&result, sizeof(result), MSG_WAITALL);
		if (retval == SOCKET_ERROR) {
			printf("RECV() ERROR!!!\n");
			break;
		}
		else if (retval == 0) break;


		printf("[TCP Client] %dByte를 받았습니다\n", retval);
		printf("[Result] %d\n", result);

	}
	closesocket(sock);
	WSACleanup();


	return 0;
}
  • 클라이언트는 2개의 정수 데이터를 입력받고 send()로 보내서 결과를 recv()를 받는다



TCP 간단한 채팅(1:1 = 클라이언트 : 서버) 프로그램 작성

※단, 채팅 순서는 클라이언트, 서버 순서로 입력 한다!!

<TCPServerChat.cpp>

#include "Common.h"
#define SERVERPORT 9000		// 서버 포트
#define IDSIZE 16		// 서버의 채팅 ID 최대 길이 ( < > 기호 포함해서!!) 
#define MSGSIZE 496		// 채팅 메시지 최대 길이
#define BUFSIZE (IDSIZE + MSGSIZE) // 버퍼의 전체 크기 = 채팅 ID 길이 + 메시지 길이 

int main(int argc, char *argv[]) {
	int retval;
	
	if (argc != 2) {
		fprintf(stderr, "Usage : %s <ChatID>\n", argv[0]);
		exit(1);
	}

	// ===== (서버)채팅 아이디 길이 체크 =====
	const char* chatid = argv[1];
	if (strlen(chatid) > (IDSIZE - 2)) {	// -2 : <> 기호 크기
		fprintf(stderr, "<ChaID>의 최대 길이는 %d 입니다\n", (IDSIZE - 2));
		exit(1);
	}


	// ===== 윈속 초기화 =====
	WSADATA wsa;
	if (WSAStartup(MAKEWORD(2, 2), &wsa) != 0)	return 1;

	// ===== 소켓 생성 =====
	SOCKET listenSocket = socket(AF_INET, SOCK_STREAM, 0);	// IPv4, TCP 통신 소켓
	if (listenSocket == INVALID_SOCKET)	printf("Socket ERROR!!!\n");

	// ===== bind() =====
	struct sockaddr_in serveraddr;	//IPv4 소켓 주소 구조체
	memset(&serveraddr, 0, sizeof(serveraddr));		// 초기화
	serveraddr.sin_family = AF_INET;
	serveraddr.sin_addr.s_addr = htonl(INADDR_ANY);	// INADRR_ANY : 모든 IP에서 접속 가능!!
	serveraddr.sin_port = htons(SERVERPORT);

	retval = bind(listenSocket, (struct sockaddr*)&serveraddr, sizeof(serveraddr));
	if (retval == SOCKET_ERROR)	printf("BIND() ERROR!!\n");

	// ===== listen() =====
	retval = listen(listenSocket, SOMAXCONN);
	if (retval == SOCKET_ERROR)	printf("LISTEN() ERROR!!!\n");
	//printf("Server Listen...");

	// 통신에 사용할 변수 선언
	SOCKET clientSocket;			// 클라이언트 소켓
	struct sockaddr_in clientaddr;	// 클라이언트 소켓 주소(원격 IP / 원격 PORT)
	int addrlen;						// 주소 길이
	int len;
	char msg[MSGSIZE + 1];			// 입력 메시지 저장
	char buf[BUFSIZE + 1];			// 받은 메세지를 저장할 응용프로그램 버퍼
	
	while (1) {
		// ===== accept() =====
		addrlen = sizeof(clientaddr);
		clientSocket = accept(listenSocket, (struct sockaddr*)&clientaddr, &addrlen);
		if (clientSocket == INVALID_SOCKET) {
			printf("ACCEPT() ERROR!!\n");
			break;
		}
		// 접속한 클라이언트의 IP, Port 출력
		char addr[INET_ADDRSTRLEN];
		inet_ntop(AF_INET, &clientaddr.sin_addr, addr, sizeof(addr));	//  inet_ntop : 네트워크 바이트 정렬
		printf("TCP Connect : IP = %s, PORT = %d\n", addr, ntohs(clientaddr.sin_port));

		// ===== 클라이언트와 통신 =====
		while (1) {
        	//	===== recv =====
			//  === 고정길이 데이터 받기 === 
			retval = recv(clientSocket, (char*)&len, sizeof(int), MSG_WAITALL);
			if (retval == SOCKET_ERROR) {	// 오류가 있을 경우
				printf("FIXED_LENGHT_DATA RECV() ERROR!!!\n");
				break;
			}
			else if (retval == 0)	break; // 정상 종료

			// === 가변 길이 메시지 받기 ===
			retval = recv(clientSocket, buf, len, MSG_WAITALL);
			if (retval == SOCKET_ERROR) {
				printf("VARIABLE_LENGTH_DATA RECV() ERROR!!!\n");
				break;
			}
			else if (retval == 0) break;

			//	받은 메시지 출력
			buf[retval] = '\0';
			printf("%s", buf);

			//	메시지 입력
			printf("<%s>", chatid);
			if (fgets(msg, MSGSIZE + 1, stdin) == NULL) {
				break;
			}

            //	'\n' 문자 제거
			len = (int)strlen(msg);
			if (msg[len - 1] == '\n')	msg[len - 1] = '\0';
			if (strlen(msg) == 0)	break;

			//	메시지 준비하기;
			sprintf(buf, "<%s> %s\n", chatid, msg);
			len = (int)strlen(buf);

			//	===== send() =====
			//	===== 고정 크기 데이터 보내기(메시지 길이) =====
			retval = send(clientSocket, (char *)&len, sizeof(int), 0);
			if (retval == SOCKET_ERROR) {
				printf("FIX_DATA_SEND_ERROR!!!\n");
				break;
			}
			
            //	===== 가변 크기 메시지 보내기 (메시지) =====
			retval = send(clientSocket, buf, len, 0);
			if (retval == SOCKET_ERROR) {
				printf("VAR_MSG_SEND_ERROR!!!\n");
				break;
			}
		}
		// 클라이언트 소켓 닫기
		closesocket(clientSocket);
		printf("Client 종료 IP = %s, Port Number = %d\n", addr, ntohs(clientaddr.sin_port));
	}
	// Listen Socket 닫기
	closesocket(listenSocket);

	// 윈속 종료
	WSACleanup();
	return 0;
}
  • 고정 길이 데이터에 가변 길이 데이터의 길이를 입력 받아 가변길이 데이터를 처리한다
  • 서버의 아이디는 "프로젝트 속성 -> 구성 속성 -> 디버깅 -> 명령인수"에 입력 한다

<TCPClientChat.cpp>

#include "Common.h"
#define SERVERPORT 9000	// 통신할 서버 포트
#define IDSIZE 16	// (클라이언트) 채팅 ID 최대 길이 (<> 기호 포함!!)
#define MSGSIZE 240	// 채팅 메시지 최대 길이
#define BUFSIZE (IDSIZE + MSGSIZE)	// 버퍼 전체 크기 = 채팅 ID 길이 + 채팅 메시지 길이 
char* SERVERIP = (char*)"127.0.0.1";	

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

	if (argc != 3) {
		fprintf(stderr, "Usage : %s <IP> <ChaID>\n", argv[0]);
		exit(1);
	}

	// 명령행 인수가 있으면 IP주소로 사용하기!
	if (argc > 1) SERVERIP = argv[1];

	// 명령행 인수로 전달된 ID를 저장, 길이 점검
	const char* chatid = argv[2];
	if (strlen(chatid) > (IDSIZE - 2)) {
		fprintf(stderr, "<ChatID>의 최대 길이는 %d 입니다", (IDSIZE-2));
		exit(1);
	}

	// 윈속 초기화
	WSADATA wsa;
	if (WSAStartup(MAKEWORD(2, 2), &wsa) != 0)	return 1;

	// 소켓 생성
	SOCKET sock = socket(AF_INET, SOCK_STREAM, 0);
	if (sock == INVALID_SOCKET)	printf("SOCKET ERORR!!!\n");

	struct sockaddr_in serveraddr;
	memset(&serveraddr, 0, sizeof(serveraddr));	// 소켓 초기화
	serveraddr.sin_family = AF_INET;
	inet_pton(AF_INET, SERVERIP, &serveraddr.sin_addr);
	serveraddr.sin_port = htons(SERVERPORT);

	// ===== connect() =====
	retval = connect(sock, (struct sockaddr*)&serveraddr, sizeof(serveraddr));
	if (retval == SOCKET_ERROR)	printf("CONNECT() ERORR!!!\n");

	//	데이터 통신에 사용할 변수 
	char msg[MSGSIZE + 1];	// 입력 메시지 저장
	char buf[BUFSIZE + 1];	// 받은 메시지를 저장할 응용프로그램 버퍼
	int len; // 입력한 문자 길이 정리
	

	//	서버와 데이터 통신
	while(1){
		
		//	메시지 입력 
		printf("<%s> ", chatid);
		if (fgets(msg, MSGSIZE + 1, stdin) == NULL)	break;

		//	'\n' 문자 제거
		len = (int)strlen(msg);
		if (msg[len - 1] == '\n')	msg[len - 1] = '\0';
		if (strlen(msg) == 0)	break;

		//	메시지 준비!
		sprintf(buf, "<%s> %s\n", chatid, msg);
		len = (int)strlen(buf);

		//	===== send() =====
		// 고정 길이 데이터 보내기
		retval = send(sock, (char*)&len, sizeof(int), 0);
		if (retval == SOCKET_ERROR) {
			printf("FIXED_LENGHT_MSG SEND() ERROR!!!\n");
			break;
		} 

		//	가변길이 메시지 보내기
		retval = send(sock, buf, len, 0);
		if (retval == SOCKET_ERROR) {
			printf("VARIABLE_LENGTH_MSG SEND() ERROR!!!\n");
			break;
		} 

		//	===== recv() =====
		//	고정 길이 데이터 받기
		retval = recv(sock, (char*)&len, sizeof(int), MSG_WAITALL);
		if (retval == SOCKET_ERROR) {
			printf("FIXED_LENGHT_MSG RECV() ERROR!!!\n");
			break;
		} else if (retval == 0){
			break;
		}

		//	가변 길이 메시지 받기
		retval = recv(sock, buf, len, MSG_WAITALL);
		if (retval == SOCKET_ERROR) {
			printf("VARIABLE_LENGTH_MSG RECV() ERROR!!!\n");
			break;
		} else if (retval == 0) {
			break;
		}

		// 받은 메시지 출력!
		buf[retval] = '\0';
		printf("%s", buf);
	}
	// 소켓 닫기
	closesocket(sock);

	// 윈속 종료
	WSACleanup();
	return 0;
}
  • 고정 길이 데이터에 가변 길이 데이터의 길이를 입력 받아 가변길이 데이터를 처리한다
  • 클라이언트의 아이디와 서버 IP주소는 "프로젝트 속성 -> 구성 속성 -> 디버깅 -> 명령인수"에 입력 한다