본문 바로가기

Robotics/Software Tech.

TCP/IP 동기통신(boost, winsock 버젼)


동기(sync)통신이라하면, Server와 Client간에 데이터의 송수신이 악수와 같이, 테니스 코트에서 공을 주고 받는 것과 같이 서버는 클라이언트가 요청하면 보내고, 클라이언트는 서버가 보내줄때까지 커넥션 상태를 holding하고, 다른 데이터로부터의 수신을 blocking한다. 통신 상태에 따라 bottleneck으로 작용하거나 비효율적인 부분이 있지만, 동기를 맞춰야 하는 네트워크 시스템에서는 반드시 필요할 것이다.


boost의 ASIO로 코드를 만들어뒀다가, 문제가 생겨서 winsock으로 다시 만들었다. 보통 window상에서 visual studio 2012를 사용하는데, 사용하는 boost 1.51버젼이 아직 visual studio 2012에 잘 붙지 않는다. visual studio 2010은 가능하지만, 네트워크 기능때문에 다른 프로젝트들을 VS2010으로 바꾸는게 효율적이지 않아보였다.(property에서 platform설정을 'V100' 으로 바꾸면 가능하지만, 이상하게도 IDE2012에서 설정을 바꾸니 Property정보가 사라지던데.. 이유는 모르겠다.)


일단 두가지 버젼으로 만들었으니, interface를 둬서 두 버젼을 쉽게 교체가능하게 만들어 보는것이 좋겠다.

참고로 송수신하는 데이터는 string이다.


<interface class : iCommunication.h>


#pragma once

#include <string>


using namespace std;

class iCommunicaton

{


public:

iCommunicaton() {};

virtual ~iCommunicaton(void) {};


virtual bool isOpened() = 0; /*connnection check*/

virtual string getInfo(const char* query, int size) = 0; /*정보 검색*/

};



<boost version : TCP_boost.h>

#pragma once

#include "iCommunication.h"


#include <iostream>


#include <boost\asio.hpp>

#include <cstring>

#include <cstdlib>


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

using namespace std;


class TCP_boost : public iCommunication

{


public:

TCP_boost (const char* hostaddr, const char* port);

virtual ~TCP_boost (void);


/*base interface function*/

string getInfo(const char* query, int size);     /*정보 검색*/

bool isOpened();


private:

boost::asio::io_service _io_service;

boost::asio::streambuf _read_buffer;

tcp::resolver _resolver;

tcp::socket _socket;

tcp::resolver::query _query;

tcp::resolver::iterator _iterator;

};


<winsock2 version : TCP_winsock.h>

#pragma once

#include "iCommunication.h"

#include <WinSock2.h>

#include <iostream>

#include <cstring>

#include <cstdlib>


#pragma comment(lib, "ws2_32.lib")


using namespace std;

class TCP_winsock : public iCommunication

{


public:

TCP_winsock (const char* hostaddr, const char* port);

virtual ~TCP_winsock (void);


/*base interface function*/

string getInfo(const char* query, int size);     /*정보 검색*/

bool isOpened();


private:

WSADATA _wsaData;

SOCKET* _socket;

SOCKADDR_IN _sockAddr;

};


<boost version : TCP_boost.cpp>

#include "TCP_boost.h"

#pragma warning(disable:4996)



TCP_boost::TCP_boost(const char* hostaddr, const char* port)

:_resolver(_io_service),_socket(_io_service),_query(tcp::v4(), hostaddr, port)

{

_iterator = _resolver.resolve(_query);

boost::asio::connect(_socket, _iterator);

}


TCP_boost::~TCP_boost(void)

{

if(_socket.is_open())

{

boost::asio::write(_socket, boost::asio::buffer("quit", 4)); //quit command

_socket.close();

}


_resolver.cancel();

}



bool TCP_boost::isOpened()

{

return _socket.is_open();

}


string TCP_boost::getInfo(const char* query, int size)

{

string output;


try

{

boost::asio::write(_socket, boost::asio::buffer(query, size));

boost::asio::read_until(_socket,_read_buffer,"[END]"); //[END]라는 데이터가 수신될때까지 block하며 수신유지.


// 여기에 수신된 데이터 처리 루틴 추가

//....


}

catch (std::exception& e)

{

std::cerr << "Exception: " << e.what() << "\n";

}


return output;

}


<winsock version : TCP_winsock.cpp>

#include "TCP_winsock.h"

#pragma warning(disable:4996)



TCP_winsock::ontolTCP_winsockgy_v1(const char* hostaddr, const char* port)

:_socket(nullptr)

{

if(WSAStartup(MAKEWORD(2,2),&_wsaData)!=0)

{

std::cout << "Winsock error - Winsock initialization failed\n";

WSACleanup();

}


_socket = new SOCKET(socket(AF_INET,SOCK_STREAM,IPPROTO_TCP));


if(*_socket == INVALID_SOCKET)

{

std::cout << "Winsock error - Socket creation Failed!\n";

WSACleanup();

}

_sockAddr.sin_family = AF_INET;

_sockAddr.sin_port = htons(atoi(port));

_sockAddr.sin_addr.s_addr = inet_addr(hostaddr);

// Attempt to connect to server

if(connect(*_socket,(SOCKADDR*)(&_sockAddr),sizeof(_sockAddr))!=0)

{

std::cout << "Failed to establish connection with server\n";

WSACleanup();

}

}


TCP_winsock::~TCP_winsock(void)

{

if(_socket!=nullptr)

{

send(*_socket, "quit", 4, 0); //quit command

WSACleanup();

shutdown(*_socket,SD_SEND);

closesocket(*_socket);

_socket = nullptr;

}

}



bool TCP_winsock::isOpened()

{

if(_socket==nullptr)

return false;

else return true;

}


string TCP_winsock::getInfo(const char* query, int size)

{

bool ret = true;


string output;


if(_socket!=nullptr)

{

send(*_socket, query, size, 0);


char buffer[8192];

memset(buffer,0,8192);

string read;


int len = SOCKET_ERROR;

while(1)

{

len = recv(*_socket,buffer,8192,0);

if(len>0)

{

read = read + string(buffer);


/*if not found*/

if(read.find("NULL")!=string::npos && read.find("[END]")!=string::npos)

{

break;

}


if(read.find("[END]")!=string::npos && ret)

{

//여기에 데이터(read) 처리 루틴 추가

//...

break;

}


len = SOCKET_ERROR;

memset(buffer,0,8192);

}

Sleep(10);

}

if(len == SOCKET_ERROR)

{

std::cout << "recv() error!\n";

ret = false;

}

}


return output;

}



주의 할 사항은 winsock버젼에서는 내부에 while로 체크하기 때문에 thread로 처리해야한다. 프로세스가 수신대기때문에 멈춰있을지도 모르니..

위 코드는 string으로 데이터를 주고받는 구조에서 만들어진것이다. 서버에서는 송신데이터의 맨 마지막에 [END] string을 추가해서 보내줘서 자신이 보내야 할 데이터가 모두 다 전송되었음을 표시하였다.


위처럼 두가지 버젼을 만들어놓고난뒤에, 사용할때는 아래와 같이 사용하면 된다.


<Example>


#include "iCommunication.h"

//#include "TCP_winsock.h"

#include "TCP_boost.h"


void main()

{

//boost 버젼 사용시

iCommunication* _com = new TCP_boost("127.0.0.1", "60000");


//winsock version 사용시

//iCommunication* _com = new TCP_winsock("127.0.0.1", "60000");


std::cout << _com->getInfo("test",4);


delete _com;

}