장비제어에서 소켓통신이 필요한 순간...

2021. 1. 6. 13:01장비제어개발관한이야기

반응형

신규장비를 하면은 한달이 어떻게 지나가는지 모를정도로 시간이 빨리 지나갑니다.

힘들기도 하고, 보람되기도 합니다.. 요즘들어서는 코딩보다 멍때리는 시간이 많아 지는거 같아 슬프기도 합니다.

 

자동화 장비 관련 일을 하다 보면, 변위센서, 각종 모터드라이브들을 사용하다 보면은 소켓 통신을 사용할 일이 많습니다.

 

이 소켓통신은 무엇인가?

 

소켓통신은 말그대로 소켓을 기반으로 한 통신입니다.. 뭐, 컴퓨터공학 전공자라면 누구나 아는 내용이지만, 나도 그렇고 다들 7계층 이라던가 그런것 까지 이론으로 공부 했을꺼라 생각됩니다. 간단하게 설명하자면 통신을 위한 일종의 약속으로 보면 이해가 빠를 듯 합니다. 일단, 장비제어를 하기위해서는 많은 개념은 필요없고, 어느 정도 기반지식이면 접근하는데 크게 무리가 없을 듯합니다. 물론 저도 많은 기반지식은 없습니다. ^^*

 

 

장비제어에서 소켓통신은 왜 사용되는가?

 

주로, 시리얼 통신과 비슷한 용도로 쓰이는데 자동화 장비와 다른 장비(바코드,조명,다른PC)을 제어를 하는 용도로 많이 쓰입니다.

제가 입사할 당시만해도, 시리얼 통신이 대부분이 었는데, 카메라도 소켓통신으로 할만큼 많은 유닛들이 소켓통신을 지원하고 있습니다.

다른용도로는 결과 처리 및 작업승인 명령을 받기 위해서도 많이 쓰입니다.

워낙, 소켓통신은 자료도 많아서 소개 하기도 민망할정도로 자료가 많습니다.

 

소켓 어떻게 사용되는가?

기본적으로 소켓통신은 설정 이외에 Read함수와 Write 함수 로만 이루어진 아주 심플합니다.

 

제가 쓰고 있는 소켓관련 기본클래스의 구조는 소켓 연결후에 쓰레드 하나가 데이터가 수신 여부를 확인하고 수신이 되었으면, 바이트단위로 큐에 저장하는 구조로 되어있습니다. 이 기본클래스를 상속구조로 가져가서 큐에서 받아온 내용을 의미있는 구분(패킷) 단위로 나누게 됩니다. 이 패킷은 사용자가 지정한 단위입니다. 여기서 많은 방식으로 사용하는 사람마다 틀린 구조로 가져가게 되는데 저 같은 경우는 XML구조 포맷을 기본으로 합니다.

 

전문적으로 고속으로 데이터를 처리하는 경우는 드물기 때문에 하드웨이유닛에서 원하는 기능과 데이터를 파악을 잘하는것이 중요하다고 할수 있습니다.

일단 제가 쓰고 있는 소켓클래스를 공유합니다.

 

 

SocketComm.h 

 
#pragma once
 
#include <list>
 
#include <stdlib.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#pragma comment(lib, "ws2_32")
 
// Event value
#define EVT_CONSUCCESS      0x0000  // Connection established
#define EVT_CONFAILURE      0x0001  // General failure - Wait Connection failed
#define EVT_CONDROP         0x0002  // Connection dropped
#define EVT_ZEROLENGTH      0x0003  // Zero length message
 
#define BUFFER_SIZE     1024*5
#define HOSTNAME_SIZE   MAX_PATH
#define STRING_LENGTH   40
 
 
struct SockAddrIn : public SOCKADDR_IN {
public:
    SockAddrIn() { Clear(); }
    SockAddrIn(const SockAddrIn& sin) { Copy( sin ); }
    ~SockAddrIn() { }
    SockAddrIn& Copy(const SockAddrIn& sin);
    void    Clear() { memset(this, 0, sizeof(SOCKADDR_IN)); }
    bool    IsEqual(const SockAddrIn& sin) const;
    bool    IsGreater(const SockAddrIn& sin) const;
    bool    IsLower(const SockAddrIn& pm) const;
    bool    IsNull() const { return ((sin_addr.s_addr==0L)&&(sin_port==0)); }
    ULONG   GetIPAddr() const { return sin_addr.s_addr; }
    short   GetPort() const { return sin_port; }
    bool    CreateFrom(LPCTSTR sAddr, LPCTSTR sService, int nFamily = AF_INET);
    SockAddrIn& operator=(const SockAddrIn& sin) { return Copy( sin ); }
    bool    operator==(const SockAddrIn& sin) { return IsEqual( sin ); }
    bool    operator!=(const SockAddrIn& sin) { return !IsEqual( sin ); }
    bool    operator<(const SockAddrIn& sin)  { return IsLower( sin ); }
    bool    operator>(const SockAddrIn& sin)  { return IsGreater( sin ); }
    bool    operator<=(const SockAddrIn& sin) { return !IsGreater( sin ); }
    bool    operator>=(const SockAddrIn& sin) { return !IsLower( sin ); }
    operator LPSOCKADDR() { return (LPSOCKADDR)(this); }
    size_t  Size() const { return sizeof(SOCKADDR_IN); }
    void    SetAddr(SOCKADDR_IN* psin) { memcpy(this, psin, Size()); }
};
 
typedef std::list<SockAddrIn> CSockAddrList;
 
struct stMessageProxy
{
  SockAddrIn address;
  BYTE byData[BUFFER_SIZE];
};
 
class CSocketComm
{
public:
    CSocketComm();
    virtual ~CSocketComm();
 
    bool IsOpen() const;    // Is Socket valid?
    bool IsStart() const;   // Is Thread started?
    bool IsServer() const;  // Is running in server mode
    bool IsBroadcast() const; // Is UDP Broadcast active
    bool IsSmartAddressing() const; // Is Smart Addressing mode support
    SOCKET GetSocket() const;   // return socket handle
    void SetServerState(bool bServer);  // Run as server mode if true
    void SetSmartAddressing(bool bSmartAddressing); // Set Smart addressing mode
    bool GetSockName(SockAddrIn& saddr_in); // Get Socket name - address
    bool GetPeerName(SockAddrIn& saddr_in); // Get Peer Socket name - address
    bool AddMembership(LPCTSTR strAddress);
    bool DropMembership(LPCTSTR strAddress);
    void AddToList(const SockAddrIn& saddr_in); // Add an address to the list
    void RemoveFromList(const SockAddrIn& saddr_in);    // Remove an address from the list
    void CloseComm();       // Close Socket
    bool WatchComm();       // Start Socket thread
    void StopComm();        // Stop Socket thread
 
    // Create a socket - Server side (support for multiple adapters)
    bool CreateSocketEx(LPCTSTR strHost, LPCTSTR strServiceName, int nFamily, int nType, UINT uOptions /* = 0 */);
    // Create a Socket - Server side
    bool CreateSocket(LPCTSTR strServiceName, int nProtocol, int nType, UINT uOptions = 0);
    // Create a socket, connect to (Client side)
    bool ConnectTo(LPCTSTR strDestination, LPCTSTR strServiceName, int nProtocol, int nType);
    bool ConnectToTelnet(LPCTSTR strDestination, LPCTSTR strServiceName, int nProtocol, int nType);
 
// Event function - override to get data
    virtual void OnDataReceived(const LPBYTE lpBuffer, DWORD dwCount);
    virtual void OnEvent(UINT uEvent, LPVOID lpvData);
// Run function - override to implement a new behaviour
    virtual void Run();
 
// Data function
    DWORD ReadComm(LPBYTE lpBuffer, DWORD dwSize, DWORD dwTimeout);
    DWORD WriteComm(const LPBYTE lpBuffer, DWORD dwCount, DWORD dwTimeout);
 
    // Utility functions
    static SOCKET WaitForConnection(SOCKET sock); // Wait For a new connection (Server side)
    static bool ShutdownConnection(SOCKET sock);  // Shutdown a connection
    static USHORT GetPortNumber( LPCTSTR strServiceName );  // Get service port number
    static ULONG GetIPAddress( LPCTSTR strHostName );   // Get IP address of a host
    static bool GetLocalName(LPTSTR strName, UINT nSize);   // GetLocalName
    static bool GetLocalAddress(LPTSTR strAddress, UINT nSize); // GetLocalAddress
// SocketComm - data
protected:
    HANDLE      m_hComm;        // Serial Comm handle
    HANDLE      m_hThread;      // Thread Comm handle
    bool        m_bServer;      // Server mode (true)
    bool        m_bSmartAddressing; // Smart Addressing mode (true) - many listeners
    bool        m_bBroadcast;   // Broadcast mode
    CSockAddrList m_AddrList;   // Connection address list for broadcast
    HANDLE      m_hMutex;       // Mutex object
// SocketComm - function
protected:
    // Synchronization function
    void LockList();            // Lock the object
    void UnlockList();          // Unlock the object
 
    static UINT WINAPI SocketThreadProc(LPVOID pParam);
 
private:
};

 

 SocketComm.cpp

 
#include "stdafx.h"
#include <stdio.h>
#include <tchar.h>
#include <process.h>
#include <crtdbg.h>
#include "SocketComm.h"

const DWORD DEFAULT_TIMEOUT = 100L;

///////////////////////////////////////////////////////////////////////////////
// SockAddrIn Struct

///////////////////////////////////////////////////////////////////////////////
// Copy
SockAddrIn& SockAddrIn::Copy(const SockAddrIn& sin)
{
    memcpy(this, &sin, Size());
    return *this;
}

///////////////////////////////////////////////////////////////////////////////
// IsEqual
bool SockAddrIn::IsEqual(const SockAddrIn& sin) const
{
    // Is it Equal? - ignore 'sin_zero'
    return (memcmp(this, &sin, Size()-sizeof(sin_zero)) == 0);
}

///////////////////////////////////////////////////////////////////////////////
// IsGreater
bool SockAddrIn::IsGreater(const SockAddrIn& sin) const
{
    // Is it Greater? - ignore 'sin_zero'
    return (memcmp(this, &sin, Size()-sizeof(sin_zero)) > 0);
}

///////////////////////////////////////////////////////////////////////////////
// IsLower
bool SockAddrIn::IsLower(const SockAddrIn& sin) const
{
    // Is it Lower? - ignore 'sin_zero'
    return (memcmp(this, &sin, Size()-sizeof(sin_zero)) < 0);
}

///////////////////////////////////////////////////////////////////////////////
// CreateFrom
bool SockAddrIn::CreateFrom(LPCTSTR sAddr, LPCTSTR sService, int nFamily /*=AF_INET*/)
{
    Clear();
    sin_addr.s_addr = htonl( CSocketComm::GetIPAddress(sAddr) );
    sin_port = htons( CSocketComm::GetPortNumber( sService ) );
    sin_family = nFamily;
    return !IsNull();
}


///////////////////////////////////////////////////////////////////////////////
// Construct & Destruct
CSocketComm::CSocketComm() :
    m_bServer(false), m_bSmartAddressing(false), m_bBroadcast(false),
    m_hComm(INVALID_HANDLE_VALUE), m_hThread(NULL), m_hMutex(NULL)
{

}

CSocketComm::~CSocketComm()
{
    StopComm();
}

///////////////////////////////////////////////////////////////////////////////
// Members
///////////////////////////////////////////////////////////////////////////////

///////////////////////////////////////////////////////////////////////////////
// IsOpen
bool CSocketComm::IsOpen() const
{
    return ( INVALID_HANDLE_VALUE != m_hComm );
}


///////////////////////////////////////////////////////////////////////////////
// IsStart
bool CSocketComm::IsStart() const
{
    return ( NULL != m_hThread );
}


///////////////////////////////////////////////////////////////////////////////
// IsServer
bool CSocketComm::IsServer() const
{
    return m_bServer;
}


///////////////////////////////////////////////////////////////////////////////
// IsBroadcast
bool CSocketComm::IsBroadcast() const
{
    return m_bBroadcast;
}


///////////////////////////////////////////////////////////////////////////////
// IsSmartAddressing
bool CSocketComm::IsSmartAddressing() const
{
    return m_bSmartAddressing;
}


///////////////////////////////////////////////////////////////////////////////
// GetSocket
SOCKET CSocketComm::GetSocket() const
{
    return (SOCKET) m_hComm;
}


///////////////////////////////////////////////////////////////////////////////
// LockList
void CSocketComm::LockList()
{
    if (NULL != m_hMutex)
        WaitForSingleObject(m_hMutex, INFINITE);
}


///////////////////////////////////////////////////////////////////////////////
// UnlockList
void CSocketComm::UnlockList()
{
    if (NULL != m_hMutex)
        ReleaseMutex(m_hMutex);
}


///////////////////////////////////////////////////////////////////////////////
// AddToList
void CSocketComm::AddToList(const SockAddrIn& saddr_in)
{
    LockList();
    m_AddrList.insert( m_AddrList.end(), saddr_in );
    UnlockList();
}

///////////////////////////////////////////////////////////////////////////////
// RemoveFromList
void CSocketComm::RemoveFromList(const SockAddrIn& saddr_in)
{
    LockList();
    m_AddrList.remove( saddr_in );
    UnlockList();
}

///////////////////////////////////////////////////////////////////////////////
// SetServerState
void CSocketComm::SetServerState(bool bServer)
{
    if (!IsStart())
        m_bServer = bServer;
}


///////////////////////////////////////////////////////////////////////////////
// SetSmartAddressing : Address is included with message
void CSocketComm::SetSmartAddressing(bool bSmartAddressing)
{
    if (!IsStart())
        m_bSmartAddressing = bSmartAddressing;
}

///////////////////////////////////////////////////////////////////////////////
// OnDataReceived
///////////////////////////////////////////////////////////////////////////////
// DESCRIPTION:
//              This function is PURE Virtual, you MUST overwrite it.  This is
//              called every time new data is available.
// PARAMETERS:
///////////////////////////////////////////////////////////////////////////////
void CSocketComm::OnDataReceived(const LPBYTE lpBuffer, DWORD dwCount)
{
}


///////////////////////////////////////////////////////////////////////////////
// OnEvent
///////////////////////////////////////////////////////////////////////////////
// DESCRIPTION:
//              This function reports events & errors
// PARAMETERS:
//      UINT uEvent: can be one of the event value EVT_(events)
//      LPVOID lpvData: Event data if any
///////////////////////////////////////////////////////////////////////////////
void CSocketComm::OnEvent(UINT uEvent, LPVOID lpvData)
{
}

///////////////////////////////////////////////////////////////////////////////
// GetPortNumber
///////////////////////////////////////////////////////////////////////////////
// DESCRIPTION:
//              Returns a port number based on service name or port number string
// PARAMETERS:
//  LPCTSTR strServiceName: Service name or port string
///////////////////////////////////////////////////////////////////////////////
USHORT CSocketComm::GetPortNumber( LPCTSTR strServiceName )
{
    LPSERVENT   lpservent;
    USHORT      nPortNumber = 0;

    if ( _istdigit( strServiceName[0] ) ) {
        nPortNumber = (USHORT) _ttoi( strServiceName );
    }
    else {
#ifdef _UNICODE
        char pstrService[HOSTNAME_SIZE];
        WideCharToMultiByte(CP_ACP, 0, strServiceName, -1, pstrService, sizeof(pstrService), NULL, NULL );
#else
        LPCTSTR pstrService = strServiceName;
#endif
        // Convert network byte order to host byte order
        if ( (lpservent = getservbyname( pstrService, NULL )) != NULL )
            nPortNumber = ntohs( lpservent->s_port );
    }

    return nPortNumber;
}


///////////////////////////////////////////////////////////////////////////////
// GetIPAddress
///////////////////////////////////////////////////////////////////////////////
// DESCRIPTION:
//      Returns an IP address.
//          - It tries to convert the string directly
//          - If that fails, it tries to resolve it as a hostname
// PARAMETERS:
//  LPCTSTR strHostName: host name to get IP address
///////////////////////////////////////////////////////////////////////////////
ULONG CSocketComm::GetIPAddress( LPCTSTR strHostName )
{
    LPHOSTENT   lphostent;
    ULONG       uAddr = INADDR_NONE;
    TCHAR       strLocal[HOSTNAME_SIZE] = { 0 };

    // if no name specified, get local
    if ( NULL == strHostName )
    {
        GetLocalName(strLocal, sizeof(strLocal));
        strHostName = strLocal;
    }

#ifdef _UNICODE
    char strHost[HOSTNAME_SIZE] = { 0 };
    WideCharToMultiByte(CP_ACP, 0, strHostName, -1, strHost, sizeof(strHost), NULL, NULL );
#else
    LPCTSTR strHost = strHostName;
#endif

    // Check for an Internet Protocol dotted address string
    //20121022 Mahcine Name Tailjin
    CMainFrame* pMainFrame = System.GetMainWnd();    
    int nModelName = pMainFrame->GetMachineModel();

    if ( (INADDR_NONE == uAddr) && (strcmp( strHost, "255.255.255.255" )) )
    {
        // It's not an address, then try to resolve it as a hostname
        if ( lphostent = gethostbyname( strHost ) )
            uAddr = *((ULONG *) lphostent->h_addr_list[0]);
    }
    
    return ntohl( uAddr );
}


///////////////////////////////////////////////////////////////////////////////
// GetLocalName
///////////////////////////////////////////////////////////////////////////////
// DESCRIPTION:
//              Get local computer name.  Something like: "mycomputer.myserver.net"
// PARAMETERS:
//  LPTSTR strName: name of the computer is returned here
//  UINT nSize: size max of buffer "strName"
///////////////////////////////////////////////////////////////////////////////
bool CSocketComm::GetLocalName(LPTSTR strName, UINT nSize)
{
    if (strName != NULL && nSize > 0)
    {
        char strHost[HOSTNAME_SIZE] = { 0 };

        // get host name, if fail, SetLastError is set
        if (SOCKET_ERROR != gethostname(strHost, sizeof(strHost)))
        {
            struct hostent* hp;
            hp = gethostbyname(strHost);
            if (hp != NULL) {
                strncpy_s(strHost, hp->h_name, HOSTNAME_SIZE);
            }

            // check if user provide enough buffer
            if (strlen(strHost) > nSize)
            {
                SetLastError(ERROR_INSUFFICIENT_BUFFER);
                return false;
            }

            // Unicode conversion
#ifdef _UNICODE
            return (0 != MultiByteToWideChar(CP_ACP, 0, strHost, -1, strName, nSize ));
#else
            _tcscpy(strName, strHost);
            return true;
#endif
        }
    }
    else
        SetLastError(ERROR_INVALID_PARAMETER);
    return false;
}


///////////////////////////////////////////////////////////////////////////////
// GetLocalAddress
///////////////////////////////////////////////////////////////////////////////
// DESCRIPTION:
//              Get TCP address of local computer in dot format ex: "127.0.0.0"
// PARAMETERS:
//  LPTSTR strAddress: pointer to hold address string, must be long enough
//  UINT nSize: maximum size of this buffer
///////////////////////////////////////////////////////////////////////////////
bool CSocketComm::GetLocalAddress(LPTSTR strAddress, UINT nSize)
{
    // Get computer local address
    if (strAddress != NULL && nSize > 0)
    {
        char strHost[HOSTNAME_SIZE] = { 0 };

        // get host name, if fail, SetLastError is called
        if (SOCKET_ERROR != gethostname(strHost, sizeof(strHost)))
        {
            struct hostent* hp;
            hp = gethostbyname(strHost);
            if (hp != NULL && hp->h_addr_list[0] != NULL)
            {
                // IPv4: Address is four bytes (32-bit)
                if ( hp->h_length < 4)
                    return false;

                // Convert address to . format
                strHost[0] = 0;

                // IPv4: Create Address string
                sprintf_s(strHost, "%u.%u.%u.%u",
                    (UINT)(((PBYTE) hp->h_addr_list[0])[0]),
                    (UINT)(((PBYTE) hp->h_addr_list[0])[1]),
                    (UINT)(((PBYTE) hp->h_addr_list[0])[2]),
                    (UINT)(((PBYTE) hp->h_addr_list[0])[3]));

                // check if user provide enough buffer
                if (strlen(strHost) > nSize)
                {
                    SetLastError(ERROR_INSUFFICIENT_BUFFER);
                    return false;
                }

            // Unicode conversion
#ifdef _UNICODE
                return (0 != MultiByteToWideChar(CP_ACP, 0, strHost, -1, strAddress,nSize ));
#else
                _tcscpy(strAddress, strHost);
                return true;
#endif
            }
        }
    }
    else
        SetLastError(ERROR_INVALID_PARAMETER);
    return false;
}


///////////////////////////////////////////////////////////////////////////////
// WaitForConnection
///////////////////////////////////////////////////////////////////////////////
// DESCRIPTION:
//              Wait for a network connection.  Only for connection type of socket
//              This function may fail, in this case it returns "INVALID_SOCKET"
// PARAMETERS:
//  SOCKET sock: a socket capable of receiving new connection (TCP: SOCK_STREAM)
///////////////////////////////////////////////////////////////////////////////
SOCKET CSocketComm::WaitForConnection(SOCKET sock)
{
    // Accept an incoming connection - blocking
    // no information about remote address is returned
    return accept(sock, 0, 0);
}


///////////////////////////////////////////////////////////////////////////////
// ShutdownConnection
///////////////////////////////////////////////////////////////////////////////
// DESCRIPTION:
//              Shutdown a connection and close socket.  This will force all
//              transmission/reception to fail.
// PARAMETERS:
//  SOCKET sock: Socket to close
///////////////////////////////////////////////////////////////////////////////
bool CSocketComm::ShutdownConnection(SOCKET sock)
{
    shutdown(sock, SD_BOTH);
    return ( 0 == closesocket( sock ));
}


///////////////////////////////////////////////////////////////////////////////
// GetSockName
///////////////////////////////////////////////////////////////////////////////
// DESCRIPTION:
//              retrieves the local name for a socket
// PARAMETERS:
//  SockAddrIn& saddr_in: object to store address
///////////////////////////////////////////////////////////////////////////////
bool CSocketComm::GetSockName(SockAddrIn& saddr_in)
{
    if (IsOpen())
    {
        int namelen = (int)saddr_in.Size();
        return (SOCKET_ERROR != getsockname(GetSocket(), saddr_in, &namelen));
    }

    return false;
}


///////////////////////////////////////////////////////////////////////////////
// GetPeerName
///////////////////////////////////////////////////////////////////////////////
// DESCRIPTION:
//              retrieves the name of the peer to which a socket is connected
// PARAMETERS:
//  SockAddrIn& saddr_in: object to store address
///////////////////////////////////////////////////////////////////////////////
bool CSocketComm::GetPeerName(SockAddrIn& saddr_in)
{
    if (IsOpen())
    {
        int namelen = (int)saddr_in.Size();
        return (SOCKET_ERROR != getpeername(GetSocket(), saddr_in, &namelen));  
    }

    return false;
}

///////////////////////////////////////////////////////////////////////////////
// AddMembership
///////////////////////////////////////////////////////////////////////////////
// DESCRIPTION:
//              Add membership to a multicast address
// PARAMETERS:
//  LPCTSTR strAddress: ip address for membership
///////////////////////////////////////////////////////////////////////////////
bool CSocketComm::AddMembership(LPCTSTR strAddress)
{
    if ( IsOpen() )
    {
        int nType = 0;
        int nOptLen = sizeof(int);
        SOCKET sock = (SOCKET) m_hComm;
        if ( SOCKET_ERROR != getsockopt(sock, SOL_SOCKET, SO_TYPE, (char*)&nType, &nOptLen))
        {
            if ( nType == SOCK_DGRAM )
            {
                int nTTL = 5;
                if ( SOCKET_ERROR != setsockopt(sock, IPPROTO_IP, IP_MULTICAST_TTL, (const char*)&nTTL, sizeof(nTTL)))
                {
                    ip_mreq mreq;
                    mreq.imr_multiaddr.s_addr = htonl( CSocketComm::GetIPAddress( strAddress ) );
                    mreq.imr_interface.s_addr = htonl( INADDR_ANY );
                    return ( SOCKET_ERROR != setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, (const char*)&mreq, sizeof(mreq)));
                }
            }
        }
    }
    return false;
}


///////////////////////////////////////////////////////////////////////////////
// DropMembership
///////////////////////////////////////////////////////////////////////////////
// DESCRIPTION:
//              Remove membership from a multicast address
// PARAMETERS:
//  LPCTSTR strAddress: ip address for membership
///////////////////////////////////////////////////////////////////////////////
bool CSocketComm::DropMembership(LPCTSTR strAddress)
{
    if ( IsOpen() )
    {
        int nType = 0;
        int nOptLen = sizeof(int);
        SOCKET sock = (SOCKET) m_hComm;
        if ( SOCKET_ERROR != getsockopt(sock, SOL_SOCKET, SO_TYPE, (char*)&nType, &nOptLen))
        {
            if ( nType == SOCK_DGRAM )
            {
                ip_mreq mreq;
                mreq.imr_multiaddr.s_addr = htonl( CSocketComm::GetIPAddress( strAddress ) );
                mreq.imr_interface.s_addr = htonl( INADDR_ANY );
                return ( SOCKET_ERROR != setsockopt(sock, IPPROTO_IP, IP_DROP_MEMBERSHIP, (const char*)&mreq, sizeof(mreq)));
            }
        }
    }
    return false;
}


///////////////////////////////////////////////////////////////////////////////
// CreateSocketEx
///////////////////////////////////////////////////////////////////////////////
// DESCRIPTION:
//              This function creates a new socket for connection (SOCK_STREAM)
//              or an connectionless socket (SOCK_DGRAM).  A connectionless
//              socket should not call "accept()" since it cannot receive new
//              connection.  This is used as SERVER socket
// PARAMETERS:
//  LPCTSTR strHost: Hostname or adapter IP address
//  LPCTSTR strServiceName: Service name or port number
//  int nFamily: address family to use (set to AF_INET)
//  int nType: type of socket to create (SOCK_STREAM, SOCK_DGRAM)
//  UINT uOptions: other options to use
///////////////////////////////////////////////////////////////////////////////
bool CSocketComm::CreateSocketEx(LPCTSTR strHost, LPCTSTR strServiceName, int nFamily, int nType, UINT uOptions /* = 0 */)
{
    // Socket is already opened
    if ( IsOpen() )
        return false;

    // Create a Socket that is bound to a specific service provide
    // nFamily: (AF_INET)
    // nType: (SOCK_STREAM, SOCK_DGRAM)
    SOCKET sock = socket(nFamily, nType, IPPROTO_IP);
    if (INVALID_SOCKET != sock)
    {
        if (uOptions & SO_REUSEADDR)
        {
            // Inform Windows Sockets provider that a bind on a socket should not be disallowed
            // because the desired address is already in use by another socket

            BOOL optval = TRUE;
            if ( SOCKET_ERROR == setsockopt( sock, SOL_SOCKET, SO_REUSEADDR, (char *) &optval, sizeof( BOOL ) ) )
            {
                closesocket( sock );
                return false;
            }
        }

        if (nType == SOCK_DGRAM)
        {
            if (uOptions & SO_BROADCAST)
            {
                // Inform Windows Sockets provider that broadcast messages are allowed
                BOOL optval = TRUE;
                if ( SOCKET_ERROR == setsockopt( sock, SOL_SOCKET, SO_BROADCAST, (char *) &optval, sizeof( BOOL ) ) )
                {
                    closesocket( sock );
                    return false;
                }
            }
        }

        // Associate a local address with the socket
        SockAddrIn sockAddr;
        sockAddr.CreateFrom(strHost, strServiceName, nFamily);

        if ( SOCKET_ERROR == bind(sock, sockAddr,(int)sockAddr.Size()))
        {
            closesocket( sock );
            return false;
        }

        // Listen to the socket, only valid for connection socket
        if (SOCK_STREAM == nType)
        {
            if ( SOCKET_ERROR == listen(sock, SOMAXCONN))
            {
                closesocket( sock );
                return false;
            }
        }

        // Success, now we may save this socket
        m_hComm = (HANDLE) sock;
    }

    return (INVALID_SOCKET != sock);
}

///////////////////////////////////////////////////////////////////////////////
// CreateSocket
///////////////////////////////////////////////////////////////////////////////
// DESCRIPTION:
//              This function creates a new socket for connection (SOCK_STREAM)
//              or an connectionless socket (SOCK_DGRAM).  A connectionless
//              socket should not call "accept()" since it cannot receive new
//              connection.  This is used as SERVER socket
// PARAMETERS:
//  LPCTSTR strServiceName: Service name or port number
//  int nFamily: address family to use (set to AF_INET)
//  int nType: type of socket to create (SOCK_STREAM, SOCK_DGRAM)
//  UINT uOptions: other options to use
///////////////////////////////////////////////////////////////////////////////
bool CSocketComm::CreateSocket(LPCTSTR strServiceName, int nFamily, int nType, UINT uOptions /* = 0 */)
{
    return CreateSocketEx(NULL, strServiceName, nFamily, nType, uOptions);
}


///////////////////////////////////////////////////////////////////////////////
// ConnectTo
///////////////////////////////////////////////////////////////////////////////
// DESCRIPTION:
//              Establish connection with a server service or port
// PARAMETERS:
//  LPCTSTR strDestination: hostname or address to connect (in .dot format)
//  LPCTSTR strServiceName: Service name or port number
//  int nFamily: address family to use (set to AF_INET)
//  int nType: type of socket to create (SOCK_STREAM, SOCK_DGRAM)
///////////////////////////////////////////////////////////////////////////////
bool CSocketComm::ConnectTo(LPCTSTR strDestination, LPCTSTR strServiceName, int nFamily, int nType)
{
    // Socket is already opened
    if ( IsOpen() )
        return false;

    // Create a Socket that is bound to a specific service provide
    // nFamily: (AF_INET)
    // nType: (SOCK_STREAM, SOCK_DGRAM)
    SOCKET sock = socket(nFamily, nType, 0);
    if (INVALID_SOCKET != sock)
    {
        // Associate a local address with the socket
        SockAddrIn sockAddr;
        if (false == sockAddr.CreateFrom(NULL, TEXT("0"), nFamily))
        {
            closesocket( sock );
            return false;
        }

        if ( SOCKET_ERROR == bind(sock, sockAddr,(int)sockAddr.Size() ))
        {
            closesocket( sock );
            return false;
        }

        // Now get destination address & port
        sockAddr.CreateFrom( strDestination, strServiceName );

        // try to connect - if fail, server not ready
        if (SOCKET_ERROR == connect( sock, sockAddr,(int)sockAddr.Size()))
        {
            closesocket( sock );
            return false;
        }

        // Success, now we may save this socket
        m_hComm = (HANDLE) sock;
    }
    return (INVALID_SOCKET != sock);
}

bool CSocketComm::ConnectToTelnet(LPCTSTR strDestination, LPCTSTR strServiceName, int nFamily, int nType)
{
      // Socket is already opened
    if ( IsOpen() )
        return false;

    // Create a Socket that is bound to a specific service provide
    // nFamily: (AF_INET)
    // nType: (SOCK_STREAM, SOCK_DGRAM)
    SOCKET sock = socket(nFamily, nType, 0);
    if (INVALID_SOCKET != sock)
    {
         // Associate a local address with the socket
        SockAddrIn sockAddr;
        if (false ==  sockAddr.CreateFrom( strDestination, strServiceName ))
        {
            closesocket( sock );
            return false;
        }  
        
        // try to connect - if fail, server not ready
        if (SOCKET_ERROR == connect( sock, sockAddr,(int)sockAddr.Size()))
        {
            if (SOCKET_ERROR == connect( sock, sockAddr,(int)sockAddr.Size()))
            {
                closesocket( sock );
                return false;
            }
        }

        // Success, now we may save this socket
        m_hComm = (HANDLE) sock;
    }
    return (INVALID_SOCKET != sock);
}


///////////////////////////////////////////////////////////////////////////////
// CloseComm
///////////////////////////////////////////////////////////////////////////////
// DESCRIPTION:
//      Close Socket Communication
// PARAMETERS:
//      None
///////////////////////////////////////////////////////////////////////////////
void CSocketComm::CloseComm()
{
    if (IsOpen())
    {
        ShutdownConnection((SOCKET)m_hComm);
        m_hComm = INVALID_HANDLE_VALUE;
        m_bBroadcast = false;
    }
}


///////////////////////////////////////////////////////////////////////////////
// WatchComm
///////////////////////////////////////////////////////////////////////////////
// DESCRIPTION:
//      Starts Socket Communication Working thread
// PARAMETERS:
//      None
///////////////////////////////////////////////////////////////////////////////
bool CSocketComm::WatchComm()
{
    if (!IsStart())
    {
        if (IsOpen())
        {
            HANDLE hThread;
            UINT uiThreadId = 0;
            hThread = (HANDLE)_beginthreadex(NULL,  // Security attributes
                                      0,    // stack
                        SocketThreadProc,   // Thread proc
                                    this,   // Thread param
                        CREATE_SUSPENDED,   // creation mode
                            &uiThreadId);   // Thread ID

            if ( NULL != hThread)
            {
                ResumeThread( hThread );
                m_hThread = hThread;
                return true;
            }
        }
    }
    return false;
}


///////////////////////////////////////////////////////////////////////////////
// StopComm
///////////////////////////////////////////////////////////////////////////////
// DESCRIPTION:
//      Close Socket and Stop Communication thread
// PARAMETERS:
//      None
///////////////////////////////////////////////////////////////////////////////
void CSocketComm::StopComm()
{
    // Close Socket
    if (IsOpen())
    {
        CloseComm();
    }

    // Kill Thread
    if (IsStart())
    {
        SleepEx(DEFAULT_TIMEOUT, TRUE);
        if (WaitForSingleObject(m_hThread, 1000L) == WAIT_TIMEOUT)
            TerminateThread(m_hThread, 1L);
        CloseHandle(m_hThread);
        m_hThread = NULL;
    }

    // Clear Address list
    if (!m_AddrList.empty())
    {
        m_AddrList.clear();
    }

    // Destroy Synchronization objects
    if (NULL != m_hMutex)
    {
        CloseHandle( m_hMutex );
        m_hMutex = NULL;
    }

}


///////////////////////////////////////////////////////////////////////////////
// ReadComm
///////////////////////////////////////////////////////////////////////////////
// DESCRIPTION:
//      Reads the Socket Communication
// PARAMETERS:
//      LPBYTE lpBuffer: buffer to place new data
//      DWORD dwSize: maximum size of buffer
//      DWORD dwTimeout: timeout to use in millisecond
///////////////////////////////////////////////////////////////////////////////
DWORD CSocketComm::ReadComm(LPBYTE lpBuffer, DWORD dwSize, DWORD dwTimeout)
{
    _ASSERTE( IsOpen() );
    _ASSERTE( lpBuffer != NULL );

    if (lpBuffer == NULL || dwSize < 1L)
        return 0L;

    fd_set  fdRead  = { 0 };
    TIMEVAL stTime;
    TIMEVAL *pstTime = NULL;

    if ( INFINITE != dwTimeout ) {
        stTime.tv_sec = dwTimeout/1000;
        stTime.tv_usec = (dwTimeout%1000)*1000;
        pstTime = &stTime;
    }

    SOCKET s = (SOCKET) m_hComm;
    // Set Descriptor
    if ( !FD_ISSET( s, &fdRead ) )
        FD_SET( s, &fdRead );

    // Select function set read timeout
    DWORD dwBytesRead = 0L;
    int res = select((int)s+1, &fdRead, NULL, NULL, pstTime );
    if ( res > 0)
    {
        if (IsBroadcast() || IsSmartAddressing())
        {
            SockAddrIn sockAddr;
            sockAddr.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
            int nLen = (int)sockAddr.Size();
            int nOffset = IsSmartAddressing() ? nLen : 0; // use offset for Smart addressing
            if ( dwSize < (DWORD) nOffset)  // error - buffer to small
            {
                SetLastError( ERROR_INVALID_USER_BUFFER );
                return -1L;
            }
            LPSTR lpszData = (LPSTR)(lpBuffer + nOffset);
            res = recvfrom( s, lpszData, dwSize-nOffset, 0, sockAddr, &nLen);

            // clear 'sin_zero', we will ignore them with 'SockAddrIn' anyway!
            memset(&sockAddr.sin_zero, 0, sizeof(sockAddr.sin_zero));

            // Lock the list...
            LockList();
            m_AddrList.remove( sockAddr );

            if ( res >= 0)
            {
                // insert unique address
                m_AddrList.insert(m_AddrList.end(), sockAddr);

                if (IsSmartAddressing())
                {
                    memcpy(lpBuffer, &sockAddr, sockAddr.Size());
                    res += (int)sockAddr.Size();
                }
            }
            else if (WSAGetLastError() == WSAECONNRESET && m_AddrList.size() == 1)
            {
                // recvfrom doesn't always return the connection address for last connection
                m_AddrList.clear();
            }

            UnlockList(); // unlock this object addresses-list
        }
        else
        {
            res = recv( s, (LPSTR)lpBuffer, dwSize, 0);
        }
    }
    dwBytesRead = (DWORD)((res > 0)?(res) : (-1L));

    return dwBytesRead;
}


///////////////////////////////////////////////////////////////////////////////
// WriteComm
///////////////////////////////////////////////////////////////////////////////
// DESCRIPTION:
//      Writes data to the Socket Communication
// PARAMETERS:
//      const LPBYTE lpBuffer: data to write
//      DWORD dwCount: maximum characters to write
//      DWORD dwTimeout: timeout to use in millisecond
///////////////////////////////////////////////////////////////////////////////
DWORD CSocketComm::WriteComm(const LPBYTE lpBuffer, DWORD dwCount, DWORD dwTimeout)
{
    _ASSERTE( IsOpen() );
    _ASSERTE( NULL != lpBuffer );

    // Accept 0 bytes message
    if (!IsOpen() || NULL == lpBuffer)
        return 0L;

    fd_set  fdWrite  = { 0 };
    TIMEVAL stTime;
    TIMEVAL *pstTime = NULL;

    if ( INFINITE != dwTimeout ) {
        stTime.tv_sec = dwTimeout/1000;
        stTime.tv_usec = (dwTimeout%1000)*1000;
        pstTime = &stTime;
    }

    SOCKET s = (SOCKET) m_hComm;
    // Set Descriptor
    if ( !FD_ISSET( s, &fdWrite ) )
        FD_SET( s, &fdWrite );

    // Select function set write timeout
    DWORD dwBytesWritten = 0L;
    int res = select((int)s+1, NULL, &fdWrite, NULL, pstTime );
    if ( res > 0)
    {
        // Send message to peer or broadcast it
        bool bSmartAddressing = IsSmartAddressing();
        if (IsBroadcast() || bSmartAddressing )
        {
            // use offset for Smart addressing
            int nOffset = bSmartAddressing ? sizeof(SOCKADDR_IN) : 0;
            if (bSmartAddressing)
            {
                if ( dwCount < sizeof(SOCKADDR_IN)) // error - buffer to small
                {
                    SetLastError( ERROR_INVALID_USER_BUFFER );
                    return -1L;
                }

                // read socket address from buffer
                SockAddrIn sockAddr;
                sockAddr.SetAddr((PSOCKADDR_IN) lpBuffer);

                // Get Address and send data
                if (sockAddr.sin_addr.s_addr != htonl(INADDR_BROADCAST))
                {
                    LPSTR lpszData = (LPSTR)(lpBuffer + nOffset);
                    res = sendto( s, lpszData, dwCount-nOffset, 0, sockAddr,(int)sockAddr.Size());
                    dwBytesWritten = (DWORD)((res >= 0)?(res) : (-1));
                    return dwBytesWritten;
                }
                else
                {   // NOTE: broadcast will broadcast only to our peers
                    // Broadcast send to all connected-peers
                    LockList(); // Lock this object addresses-list

                    CSockAddrList::iterator iter = m_AddrList.begin();
                    for( ; iter != m_AddrList.end(); )
                    {
                        // Fix v1.3 - nOffset was missing
                        sockAddr = (*iter);
                        res = sendto( s, (LPCSTR)&lpBuffer[nOffset], dwCount-nOffset, 0, sockAddr, (int)iter->Size());
                        if (res < 0)
                        {
                            CSockAddrList::iterator deladdr = iter;
                            ++iter; // get next
                            m_AddrList.erase( deladdr );
                        }
                        else
                            ++iter; // get next
                    }
                    UnlockList(); // unlock this object addresses-list
                }
            }
            // always return success - UDP
            res = (int) dwCount - nOffset;
        }
        else // Send to peer-connection
            res = send( s, (LPCSTR)lpBuffer, dwCount, 0);
    }
    dwBytesWritten = (DWORD)((res >= 0)?(res) : (-1L));

    return dwBytesWritten;
}


///////////////////////////////////////////////////////////////////////////////
// Run
///////////////////////////////////////////////////////////////////////////////
// DESCRIPTION:
//      This function runs the main thread loop
//      this implementation can be overloaded.
//      This function calls CSocketComm::OnDataReceived() (Virtual Function)
// PARAMETERS:
// NOTES:
//      You should not wait on the thread to end in this function or overloads
///////////////////////////////////////////////////////////////////////////////
void CSocketComm::Run()
{
    stMessageProxy stMsgProxy;
    DWORD   dwBytes  = 0L;
    DWORD   dwTimeout = INFINITE;
    LPBYTE  lpData  = (LPBYTE)&stMsgProxy;
    DWORD   dwSize  = sizeof(stMsgProxy);

    bool bSmartAddressing = IsSmartAddressing();
    if ( !bSmartAddressing )
    {
        lpData = stMsgProxy.byData;
        dwSize = sizeof(stMsgProxy.byData);
    }

    // Should we run as server mode
    if (IsServer() && !bSmartAddressing)
    {
        if (!IsBroadcast())
        {
            SOCKET sock = (SOCKET) m_hComm;
            sock = WaitForConnection( sock );

            // Get new connection socket
            if (sock != INVALID_SOCKET)
            {
                ShutdownConnection( (SOCKET) m_hComm);
                m_hComm = (HANDLE) sock;
                OnEvent( EVT_CONSUCCESS, NULL ); // connect
            }
            else
            {
                // Do not send event if we are closing
                if (IsOpen())
                    OnEvent( EVT_CONFAILURE, NULL ); // wait fail
                return;
            }
        }
    }
    else
    {
        GetPeerName( stMsgProxy.address );
    }

    while( IsOpen() )
    {
        // Blocking mode: Wait for event
        dwBytes = ReadComm(lpData, dwSize, dwTimeout);

        // Error? - need to signal error
        if (dwBytes == (DWORD)-1L)
        {
            // Do not send event if we are closing
            if (IsOpen())
            {
                if ( bSmartAddressing )
                {
                    RemoveFromList( stMsgProxy.address );
                }
                OnEvent( EVT_CONDROP, &stMsgProxy.address ); // lost connection
            }

            // special case for UDP, alert about the event but do not stop
            if ( bSmartAddressing )
                continue;
            else
                break;
        }

        // Chars received?
        if ( bSmartAddressing && dwBytes == sizeof(SOCKADDR_IN))
        {
            OnEvent( EVT_ZEROLENGTH, NULL );
        }
        else if (dwBytes > 0L)
        {
            OnDataReceived( lpData, dwBytes);
        }

        //Sleep(0);
    }
}


///////////////////////////////////////////////////////////////////////////////
// SocketThreadProc
///////////////////////////////////////////////////////////////////////////////
// DESCRIPTION:
//     Socket Thread function.  This function is the main thread for socket
//     communication - Asynchronous mode.
// PARAMETERS:
//     LPVOID pParam : Thread parameter - a CSocketComm pointer
// NOTES:
///////////////////////////////////////////////////////////////////////////////
UINT WINAPI CSocketComm::SocketThreadProc(LPVOID pParam)
{
    CSocketComm* pThis = reinterpret_cast<CSocketComm*>( pParam );
    _ASSERTE( pThis != NULL );

    pThis->Run();

    return 1L;
} // end SocketThreadProc

 

 

반응형