Echo Server는 클라이언트가 보낸 데이터를 그대로 다시 반환하는 서버를 말한다.
즉, 클라가 서버에 메세지를 전송하면, 서버는 그 메세지를 그대로 클라이언트에게 다시 보낸다.

 

🔹 Echo Server, Echo Client 의 주요 특징

  • 구현이 간단하기 때문에 네트워크 프로그래밍을 학습하는 초보자에게 적합하다.
  • 클라이언트와 서버 간 통신이 정상적으로 이루어지는지 확인할 때 유용하다.

 

TCP 기반 Echo Server

#include<winsock2.h>
#pragma comment(lib, "ws2_32")

int main()
{
    // 윈속 초기화
    WSADATA wsa = { 0 };
    if(::WSAStartup(MAKEWORD(2, 2), &wsa) != 0)
    {
        puts("ERROR: 윈속을 초기화 할 수 없습니다.");
        return 0;
    }
    
    // 1. 접속대기 소켓을 생성한다.
    SOCKET hSocket = ::socket(AF_INET, SOCK_STREAM, 0);
    if(hSocket == INVALID_SOCKET)
    {
        puts("ERROR: 접속 대기 소켓을 생성할 수 없습니다.");
        return 0;
    }
    
    // 2. 포트 바인딩
    SOCKADDR_IN svraddr = { 0 };
    svraddr.sin_family = AF_INET;
    svraddr.sin_port = htons(25000);
    svraddr.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
    if(::bind(hSocket, (SOCKADDR*)&svraddr, sizeof(svraddr)) == SOCKET_ERROR)
    {
        puts("ERROR: 소켓에 IP주소와 포트를 바인드 할 수 없습니다.");
        return 0;
    }
    
    // 3. 접속대기 상태로 전환
    if(::listen(hSocket, SOMAXCONN) == SOCKET_ERROR)
    {
        puts("ERROR: 리슨 상태로 전환할 수 없습니다.");
        return 0;
    }
    
    // 4. 클라이언트 접속 처리 및 대응
    SOCKADDR_IN clientaddr = { 0 };
    int nAddrLen = sizeof(clientaddr);
    SOCKET hClient = 0;
    char szBuffer[128] = { 0 };
    int nReceive = 0;
    
    // 4.1 클라이언트 연결을 받아들이고 새로운 소켓을 생성한다. (개방)
    while((hClient = ::accept(hSocket,
        (SOCKADDR*)&clientaddr,
        &nAddrLen)) != INVALID_SOCKET)
    {
        puts("새 클라이언트가 연결되었습니다."); fflush(stdout);
        // 4.2 클라이언트로부터 문자열을 수신한다.
        while((nReceive = ::recv(hClient, szBuffer, sizeof(szBuffer, 0)) > 0)
        {
            // 4.3 수신한 문자열을 그대로 전송한다.
            ::send(hClient, szBuffer, sizeof(szBuffer), 0);
            puts(szBuffer); fflush(stdout);
            memset(szBuffer, 0, sizeof(szBuffer));
        }
        
        // 4.4 클라이언트가 연결을 종료한다.
        ::shutdown(hClient, SD_BOTH);
        ::closesocket(hClient);
        puts("클라이언트 연결에 끊겼습니다."); fflush(stdout);
    }
    
    // 5. 리슨 소켓 닫기
    closesocket(hSocket);
    
    // 윈속을 해제 한다.
    ::WSACleanup();
    return 0;
}

 

 

TCP 기반 Echo Client

#include <winsock2.h>
#pragma comment(lib, "ws2_32")


int main()
{
	// 윈속 초기화
	WSADATA wsa = { 0 };
	if (::WSAStartup(MAKEWORD(2, 2), &wsa) != 0)
	{
		puts("ERROR: 윈속을 초기화 할 수 없습니다.");
		return 0;
	}

	//1. 접속대기 소켓 생성
	SOCKET hSocket = ::socket(AF_INET, SOCK_STREAM, 0);
	if (hSocket == INVALID_SOCKET)
	{
		puts("ERROR: 소켓을 생성할 수 없습니다.");
		return 0;
	}

	//2. 포트 바인딩 및 연결
	SOCKADDR_IN	svraddr = { 0 };
	svraddr.sin_family = AF_INET;
	svraddr.sin_port = htons(25000);
	svraddr.sin_addr.S_un.S_addr = inet_addr("192.168.77.2");
	if (::connect(hSocket,
		(SOCKADDR*)&svraddr, sizeof(svraddr)) == SOCKET_ERROR)
	{
		puts("ERROR: 서버에 연결할 수 없습니다.");
		return 0;
	}

	//3. 채팅 메시지 송/수신
	char szBuffer[128] = { 0 };
	while (1)
	{
		//사용자로부터 문자열을 입력 받는다.
		gets_s(szBuffer);
		if (strcmp(szBuffer, "EXIT") == 0)
        {
            break;
        }

		//사용자가 입력한 문자열을 서버에 전송한다.
		::send(hSocket, szBuffer, strlen(szBuffer) + 1, 0);
		//서버로부터 방금 보낸 문자열에 대한 에코 메시지를 수신한다.
		memset(szBuffer, 0, sizeof(szBuffer));
		::recv(hSocket, szBuffer, sizeof(szBuffer), 0);
		printf("From server: %s\n", szBuffer);
	}

	//4. 소켓을 닫고 종료.
	//::shutdown(hSocket, SD_BOTH);
	::closesocket(hSocket);
	//※윈속 해제
	::WSACleanup();
	return 0;
}

 

🔹 Wire shark로 연결 상태 확인

앞서 배운 내용 처럼 서버를 실행하고 클라를 실행한후 port를 25000으로 지정하면 

3 Way Handshake를 수행한 결과를 위 그림처럼 와이어 샤크로 확인할 수 있다. [SYN], [SYN, ACK], [ACK]

 

 

일반적인 데이터 주고 받을때

 

 

일반적인 종료 상황에서의 4 Way Handshake

+ Recent posts