본문 바로가기
프로그래밍/boost

boost::asio 비동기 TCP + echo 서버 / 클라이언트

by neive 2011. 10. 27.
728x90
 

asio 라이브러리를 이용해 비동기 TCP 서버와 클라이언트를 만들어 봤습니다
예제에 있는거 그대로 이용을 했으니 뭐 특별할건 없고

컴파일 옵션에서 /Mtd 와 클라 소스에서 MY_IP 부분 수정이 필요합니다

# 소스 퍼가실 때 소스 부분을 더블클릭하면 선택 됩니다 드래그 하면 라인 정리가
안되니까 더블클릭으로 선택하고 카피하세요
# 소스에 대한 더 좋은 의견이나 개선사항은 비밀덧글로 주세요 ^^
# 소스 퍼가실 때 한마디 남겨주시면 힘이 됩니다 @_@

서버 소스


#include "stdafx.h"


#include <ctime>
#include <iostream>
#include <string>
#include <boost/asio.hpp>
#include <ctime>
#include <iostream>
#include <string>
#include <boost/array.hpp>
#include <boost/bind.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/enable_shared_from_this.hpp>
#include <boost/asio.hpp>

#include <boost/date_time/posix_time/posix_time.hpp>


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

// 날짜시간 메세지를 만든다. 
std::string make_daytime_string() 
{ 
	using namespace std; // For time_t, time and ctime; 
	time_t now = time(0); 
	return ctime(&now); // ctime_s 권장. 예제니까 그냥 쓰고 넘어가지만 
} 

class CConnection : public boost::enable_shared_from_this<CConnection> 
{ 
private:
	enum EEnum
	{
		eBuffSize = 128,
	};

	tcp::socket m_Socket; 
	int m_nID; 

	boost::array<BYTE, eBuffSize> m_RecvBuf; 
	std::string m_sMessage; 

	CConnection(boost::asio::io_service& io) : m_Socket(io), m_nID(-1) 
	{ 
	} 

	void handle_Accept(const boost::system::error_code& /*error*/, size_t /*bytes_transferred*/) 
	{ 
	} 

	void handle_Wait_Recv(const boost::system::error_code& error, size_t /*bytes_transferred*/) 
	{ 
		if(!error)  // 0 이 성공 나머지는 오류 플러그 
		{ 
			std::cout << "Waiting..." << std::endl; 
			m_Socket.async_read_some(boost::asio::buffer(m_RecvBuf), 
				boost::bind(&CConnection::handle_Send, shared_from_this(), 
				boost::asio::placeholders::error, 
				boost::asio::placeholders::bytes_transferred)); 
		} 
		else
			std::cout << m_nID << " Disconnect(Read) : " << error.message() << std::endl; 
	} 

	void handle_Send(const boost::system::error_code& error, size_t /*bytes_transferred*/) 
	{ 
		if(!error)  // 0 이 성공 나머지는 오류 플러그 
		{ 
			// 선처리 후 
			if(m_RecvBuf.size()) 
			{                
				std::cout << "Recv : " << (*(char*)(m_RecvBuf.data())) << std::endl; 
				m_RecvBuf.assign(NULL); // 버퍼 초기화 
			} 

			if(true)    // 보낼게 있으면 보내고 
			{ 
				boost::asio::async_write(m_Socket, boost::asio::buffer("handle_read", 32), 
					boost::bind(&CConnection::handle_Wait_Recv, shared_from_this(), 
					boost::asio::placeholders::error, 
					boost::asio::placeholders::bytes_transferred)); 
				std::cout << "Send " << std::endl; 
			} 
			else        // 없으면 말고 
			{ 
				boost::array<BYTE, eBuffSize> buf; 
				m_Socket.async_read_some(boost::asio::buffer(buf), 
					boost::bind(&CConnection::handle_Send, shared_from_this(), 
					boost::asio::placeholders::error, 
					boost::asio::placeholders::bytes_transferred)); 
			} 
		} 
		else
			std::cout << m_nID << " Disconnect(Write) : " << error.message() << std::endl; 
	} 

public: 
	typedef boost::shared_ptr<CConnection> pointer; 

	static pointer create(boost::asio::io_service& io) 
	{ 
		return pointer(new CConnection(io)); 
	} 

	tcp::socket& socket() 
	{ 
		return m_Socket; 
	} 

	void start(int nID) 
	{ 
		std::cout << "new connect id : "<< nID << " ::: Welcome !" << std::endl; 
		m_nID = nID; 
		m_sMessage = make_daytime_string(); 

		boost::asio::async_write(m_Socket, boost::asio::buffer(m_sMessage), 
			boost::bind(&CConnection::handle_Accept, shared_from_this(), 
			boost::asio::placeholders::error, 
			boost::asio::placeholders::bytes_transferred)); 

		boost::array<BYTE, eBuffSize> buf; 
		m_Socket.async_receive(boost::asio::buffer(buf), 
			boost::bind(&CConnection::handle_Send, shared_from_this(), 
			boost::asio::placeholders::error, 
			boost::asio::placeholders::bytes_transferred)); 
	} 
}; 

class CTCP_Server 
{ 
private: 
	tcp::acceptor m_Acceptor; 
	int m_nAcceptCnt; 

	void WaitAccept() 
	{ 
		++m_nAcceptCnt; 
		CConnection::pointer new_connection = 
			CConnection::create(m_Acceptor.get_io_service()); 

		m_Acceptor.async_accept(new_connection->socket(), 
			boost::bind(&CTCP_Server::handle_Accept, this, new_connection, 
			boost::asio::placeholders::error)); 
	} 

	void handle_Accept(CConnection::pointer new_connection, const boost::system::error_code& error) 
	{ 
		if (!error) 
		{ 
			new_connection->start(m_nAcceptCnt); 
			WaitAccept(); 
		} 
	} 

public: 
	CTCP_Server(boost::asio::io_service& io) : m_Acceptor(io, tcp::endpoint(tcp::v4(), 13)), m_nAcceptCnt(0) 
	{ 
		WaitAccept(); 
	} 
}; 

int _tmain() 
{ 
	try
	{ 
		boost::asio::io_service io; 

		CTCP_Server server(io); 
		io.run(); 
	} 
	catch (std::exception& e) 
	{ 
		std::cerr << e.what() << std::endl; 
	} 

	return 0; 
}




클라 소스
#include "stdafx.h"


#include <ctime>
#include <iostream>
#include <string>
#include <boost/asio.hpp>
#include <boost/array.hpp>
#include <boost/thread.hpp>
#include <boost/bind.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>


const std::string _MY_IP("192.168.0.1");


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

class CProtocol 
{
private:

	enum EEnum
	{
		eBuffSize = 128,
	};	

	tcp::socket m_Socket; 
	bool m_bConnect; 

	int m_nTestCount; 

public: 

	CProtocol(boost::asio::io_service& io) : m_Socket(io) 
	{ 
		m_bConnect = false; 
		m_nTestCount = 0; 
	} 

	~CProtocol() {} 

	void Connect() 
	{ 
		// 입력 받은 host을 resolving한다. 
		tcp::resolver resolver(m_Socket.get_io_service()); 
		tcp::resolver::query query(_MY_IP, "daytime"); 
		tcp::resolver::iterator endpoint_iterator = resolver.resolve(query); 
		tcp::resolver::iterator end; 

		// resolving된 endpoint로 접속을 시도한다. 
		boost::system::error_code error = boost::asio::error::host_not_found; 
		while (error && endpoint_iterator != end) 
		{ 
			m_Socket.close(); 
			m_Socket.connect(*endpoint_iterator++, error); 
		} 

		// 접속 실패인지 확인 
		if (error) 
			throw boost::system::system_error(error); 

		m_bConnect = true; 

		// 읽어올 데이터를 저장할 array를 만든다. 
		boost::array<CHAR, eBuffSize> buf; 
		size_t len = m_Socket.read_some(boost::asio::buffer(buf), error); 
		if (error == boost::asio::error::eof) 
		{ 
			m_bConnect = false; 
			return; 
		} 
		else if (error) 
			throw boost::system::system_error(error); 

		// 받은 데이터를 cout로 출력한다. 
		std::cout.write(buf.data(), len); 
	} 

	bool IsRun() { return m_bConnect; } 
	bool IsSocketOpen() 
	{ 
		if(!m_Socket.is_open() && m_bConnect)   // 커넥트 된 이후 소켓이 닫혀버렸다면 
		{ 
			m_bConnect = false;                 // 커넥트도 끊김 판정 
			return false; 
		} 

		return true; 
	} 

	void handle_Recive() 
	{ 
		while(m_bConnect) 
		{ 
			if(!IsSocketOpen()) 
				break; 

			try
			{ 
				boost::array<BYTE, eBuffSize> buf; 
				int len = m_Socket.receive(boost::asio::buffer(buf)); 
				if(len > 0) 
				{ 
					std::cout << "> Recv(len " << len << ") "; 
					std::cout.write((CHAR*)buf.data(), len) << std::endl;  
				} 
			} 
			catch (std::exception& e) 
			{ 
				m_bConnect = false; 
				std::cerr << e.what() << std::endl; 
			} 
		} 

	} 

	void handle_Send() 
	{ 
		while(m_bConnect) 
		{ 
			if(!IsSocketOpen()) 
				break; 

			try
			{ 
				boost::array<BYTE, eBuffSize> buf = { boost::lexical_cast<BYTE>(m_nTestCount) }; 
				boost::system::error_code error; 
				int len = boost::asio::write(m_Socket, boost::asio::buffer(buf, buf.size()), error); 
				if(len > 0) 
					std::cout << "> Send " << m_nTestCount << std::endl; 
				m_nTestCount++;  
			} 
			catch (std::exception& e) 
			{ 
				m_bConnect = false; 
				std::cerr << e.what() << std::endl; 
			} 

			Sleep(3000); 
		} 
	} 
}; 

int _tmain(int argc, char* argv[]) 
{ 
	try
	{ 
		boost::asio::io_service io_service; // io_service를 하나 생성한다. 

		CProtocol Ptc(io_service); 
		boost::thread t(boost::bind(&boost::asio::io_service::run, &io_service)); 
		Ptc.Connect(); // 접속 시도 
		boost::thread Recv(boost::bind(&CProtocol::handle_Recive, &Ptc)); 
		boost::thread Send(boost::bind(&CProtocol::handle_Send, &Ptc)); 
		io_service.run(); 

		while(Ptc.IsRun()) 
		{ 
		} 

		Recv.join(); 
		Send.join(); 

		t.join();   // 쓰레드가 종료될 때까지 메인 함수의 종료를 막는다 
	} 
	catch (std::exception& e) 
	{ 
		std::cerr << e.what() << std::endl; 
	} 

	int in; 
	std::cout << "END"; 
	std::cin >> in ; 

	return 0; 
}
728x90

'프로그래밍 > boost' 카테고리의 다른 글

boost::function 으로 callback map 만들기  (0) 2011.11.03
boost::asio::buffer  (0) 2011.10.28
boost::asio 쓰레드, 타이머, Strand 의 사용법 예제  (0) 2011.10.27
boost::asio  (0) 2011.10.27
boost 라이브러리  (0) 2011.10.27

댓글