C++ TCP/IP 네트워킹 맛 보기

Date:     Updated:

카테고리:

태그:

인프런에 있는 홍정모 교수님의 홍정모의 따라 하며 배우는 C++ 강의를 듣고 정리한 필기입니다. 😀
🌜 [홍정모의 따라 하며 배우는 C++]강의 들으러 가기!


chapter 20.5 TCP/IP 네트워킹 맛 보기

서버

클라이언트와 서버가 서로 데이터를 주고 받음

  • 서버가 처음 하는 일
    1. 서버를 키고
    2. 클라이언트를 기다림
  • 서버와 클라이언트가 서로 주고 받는게 빈번한 게임같은 경우에는 TCP 말고 UDP를 쓰기도 한다.
  • 클라이언트와 서버를 ‘소켓’ 으로 연결


서버 코드

#include <iostream>	
#include <vector>
#include <utility>
#include <boost/asio.hpp>

using boost::asio::ip::tcp;

int main()
{
	try
	{
		boost::asio::io_service io_service;

		tcp::endpoint endpoint(tcp::v4(), 13);			// 통신을 하는 양 끝점 endpoint. 13은 port # 임의의 숫자 가능
		tcp::acceptor acceptor(io_service, endpoint);	// endpoint에서 io_service를 accept할 acceptor를 생성

		std::cout << "Server started" << std::endl;

		for (;;)	// 무한 루프
		{
			const std::string message_to_send = "Hello From Server";

			boost::asio::ip::tcp::iostream stream;	// 일반 iostream이 아니라 오버로드된 tcp::iostream이다.

			std::cout << "check 1" << std::endl;	// 네트워킹에서도 cout을 쓸 수 있다.

			boost::system::error_code ec;
			acceptor.accept(*stream.rdbuf(), ec);	// 클라이언트 접속을 받아들인다.

			std::cout << "check 2" << std::endl;

			if (!ec)	//TODO: How to take care of multiple clients? Multi-threading?
			{	// 클라이언트가 제대로 접속이 됐다면,

				// receive message from client 
				std::string line;
				std::getline(stream, line);			// 입출력 스트림에서 클라이언트로부터 메세지를 받는다.
				std::cout << line << std::endl;		// 클라이언트 입장에서는 여기서 데이터를 보내야 서버가 받는다.

				// send message to client
				stream << message_to_send;
				stream << std::endl;				// 클라이언트 입장에서는 여기서 데이터를 읽어야 서버가 보낸다.
			}
		}
	}
	catch (std::exception& e)						// 예외 발생시
	{
		std::cout << e.what() << std::endl;
	}
}
  • #include <boost/asio.hpp>
  • 서버는 자기 자신이 구동이 되면서 자기 자신의 ip주소를 가지고 시작한다.
    • 서버 스스로 자기 ip를 알고 있기 때문에 서버 시작을 준비하는 아래 과정에선 ip다루는 부분 없다.
      • using boost::asio::ip::tcp;
        • 네임 스페이스 👉 boost::asio::ip::tcp;
          • boost 안에 있는 asio 안에 있는 ip 안에 있는 tcp
      • try - catch
        • 네트워크 프로그래밍을 할 때에는 예외처리를 기본으로 해놔야한다. 불안정적인 상황 고려.
      • boost::asio::io_service io_service;
      • tcp::endpoint endpoint (tcp::v4(), 13);
        • 통신을 하는 양 끝점. 종이컵 전화기의 양 끝 종이컵이라고 생각하며 됨. endpoint.
        • tcp::v4()
          • tcp 버전 4 를 사용
        • 13
          • 포트 넘버
            • 필수
            • 한 회선 안에서 마치 여러개의 회선이 있는 것 처럼 채널로 나눠주는 것
              • TV 회선은 1개지만 채널이 여러개인 것처럼
      • tcp::acceptor acceptor(io_service, endpoint);
        • 어셉터 를 만든다.
        • endpoint에서 io_service를 accept할 acceptor를 생성
  • for (;;)
    • 서버는 무한 루프로 돌린다.
      • const std::string message_to_send = “Hello From Server”;
        • 서버인 나에게 접속해 온 클라이언트에게 이 string을 보여줄 것!
      • boost::asio::ip::tcp::iostream stream;
        • 일반 iostream이 아니라 오버로드된 tcp::iostream이다.
        • std::cout « “check 1” « std::endl;
          • 네트워킹에서도 cout을 쓸 수 있다.
          • « »가 오버로딩 되어있어서 !
        • stream은 매 반복마다 다시 선언되므로 for문 끝에 도달하면 비워진다 ! 지역변수니까. 매 반복마다 새로 생성됨
      • boost::system::error_code ec;
        • 에러코드로 사용할 객체
      • acceptor.accept(*stream.rdbuf(), ec);
        • 클라이언트 접속을 받아들인다.( = appecptor가 appept 하다.)
        • acceptor는 좀전에 위에서 io_service와 endpoint를 이용해 만들었다.
        • *stream.rdbuf()
          • 클라이언트가 접속을 해오면 stream으로부터 rdbuf를 읽어 옴.
        • ec
          • 받아 들여진게 없거나 에러가 나면 ec = NULL 로 만들어준다.
      • if (!ec)
        • 에러코드가 NULL 값이 아니라면
        • accpector로부터 서버에 클라이언트가 들어온게 확인되면
        • 즉 클라이언트가 서버에 제대로 접속이 됐다면
          • receive message from client 클라이언트로부터 메세지 받기
            • std::string line;
            • std::getline(stream, line);
              • 한 줄 통째로 받기
              • 입출력 스트림에서 클라이언트로부터 메세지를 받는다.
              • 클라이언트 입장에서는 여기서 데이터를 보내야 서버가 받는다.
              • 여기서 stream은 아까 tcp::iostream
            • std::cout « line « std::endl;
              • 입력 받은걸 그대로 출력
          • send message to client 클라이언트에게 메세지 보내기
            • stream « message_to_send;
              • “Hello From Server”;
            • stream « std::endl;
              • getline으로 클러이언트에게서 메세지를 받고 있기 때문에
                • 개행 문자를 버퍼에서 없애기 위해 꼭 필요한 과정


클라이언트

클라이언트 솔루션과 서버코드의 솔루션은 다르다. 별개의 파일.

#include <iostream>
#include <string>	
#include <boost/asio.hpp>

using boost::asio::ip::tcp;

int main(int argc, char** argv)
{
	try
	{
		if (argc != 2)
		{
			std::cerr << "Usage : Client <host>\n";
			return EXIT_FAILURE;
		}
		
		tcp::iostream stream(argv[1], std::to_string(int(13))); // port num = 13
		if (!stream)
		{
			std::cout << "No address. Unable to connect: " << stream.error().message() << std::endl;
			return EXIT_FAILURE;
		}

		// send message to server
		stream << "Hello from client";
		stream << std::endl;

		// receive message from server
		std::string line;
		std::getline(stream, line);
		std::cout << line << std::endl;
	}
	catch(std::exception & e)
	{
		std::cout << e.what() << std::endl;
	}
}
  • int main(int argc, char** argv)
    • 서버 주소를 명령 인수에서 입력을 받는다.
      • 프로젝트 - 우클 - 속성 - 디버깅 - 명령 인수
        • image
          • 여기에 서버 주소를 입력한다.
            • ex) 127.0.0.1(내 PC안에서 두개 다돌림) , 다른 컴퓨터의 ip주소
  • if (argc != 2)
    • 즉 서버 주소를 명령 인수에 입력 안했다면 !
    • 그냥 종료 return EXIT_FAILURE;
  • tcp::iostream stream(argv[1], std::to_string(int(13)));
    • tcp::iostream 타입인 stream 객체를 생성한다. ( 매개변수 2개 생성자 )
      • argv[1]
        • 입력한 서버 주소
      • std::to_string(int(13))
        • 포트 넘버를 문자열로 변환
  • if (!stream)
    • stream이 안만들어지는 오류가 발생했다면
    • 그냥 종료 return EXIT_FAILURE;
  • send message to server 서버에게 메세지 보내기
    • stream « “Hello from client”;
      • 서버에게 보낼 스트림에 메세지 넣기
    • 서버는 getline으로 stream을 받기 때문에 개행 문자 꼭 넣어주기
      • stream « std::endl;
  • receive message from server 서버로부터 메세지 받기
    • std::string line;
      • line에 받을 것
    • std::getline(stream, line);
      • stream으로부터 메세지를 받는다.
    • std::cout « line « std::endl;
      • 받은거 출력


실행해보기

  1. 서버를 먼저 실행하였다.
    • check1 밑에서 깜빡거리며 클라이언트가 accept 되기를 기다리는 중이다.
    • image
  2. 이때 클라이언트를 실행하면
    • image
    • 왼쪽 창 → 클라이언트 / 오른쪽 창 → 서버
    • 클라이언트가 실행되자마자 서버로부터 “Hello From Server” 메세지를 받는다.
    • 동시에 서버창에서도 check2가 출력되고 “Hello From Server” 가 출력됐다.
      • 무한 루프에 따라 다시 새로운 클라이언트가 실행되어 accpect되기를 기다린다. (check 1)
    • 서로 stream을 주고 받고 하는 과정 !


채팅 프로그램 구현시 고려해볼 점

  1. 사용자 둘이서 언제나 번갈아가면서 얘기하진 않음
  2. How to take care of multiple clients? → Multi-threading 사용하기


🌜 개인 공부 기록용 블로그입니다. 오류나 틀린 부분이 있을 경우 
언제든지 댓글 혹은 메일로 지적해주시면 감사하겠습니다! 😄

맨 위로 이동하기

Cpp Network 카테고리 내 다른 글 보러가기

댓글 남기기