上位机以太网异步通信之CSocketEx(客户端)

Socket

Socket也成为套接字,套接字是支持TCP/IP网络通信的基本操作单元,具有链接服务器,绑定,监听,发送及接受函数。但是在工业控制的上位机软件中,有些应用场景必须对其进行封装以满足软件的某些特殊需求,这里介绍一种CSocketEx类,封装了Socket的基本功能,并在其发送函数中加了线程锁,以确保不同线程访问发送函数时保证接收字符信息的一致性及正确性。

下面先看一下,Socket具有哪些功能

SOCKET socket(int af,int type,int protocol)

socket函数返回值创建一个套接字

int connect(SOCKET s,const sockaddr* name,int namlen)

connect函数形参1表示创建的套接字,形参2const sockaddr是指链接服务器的sock地址信息,namelen=sizeof(sockaddr_in)

返回如果等于INVALID_SOCKET则表示链接失败,否则即表示链接成功。

int recv(SOCKET s,char* pBuff,int len,int flags)

SOCKET s表示创建的套接字s,char* pBuff表示接受的字符,int len表示接受字符缓存大小,flags默认0

返回值表示实际接收的字符长度

int send(SOCKET s,char* pBuff,int len,int flags)

SOCKET s表示创建的套接字s,char* pBuff表示发送的字符,int len表示发送字符长度,flags默认0

返回值表示实际发送成功的字符长度

CSokcetEx

以上介绍了SOCKET套接字常用的4个函数,分别为创建,链接,发送以及接收函数(服务端程序还多绑定及监听函数,本文暂时只讲客户端程序)。

我们知道C++是一门OOP语言,SOCKET套接字本身不是一个类,所以我们需要对其进行封装。

因为我们需要实现以太网异步通信,所以我们还需要在CSocketEx类中新增一个m_hRecvEvent的事件句柄表示消息是否接收完毕,还需要添加两个CRITICAL_SECTION变量对关键代码区做线程保护。再通过LaunchCommand函数实现发送及接收功能,一个函数实现发送与接收可以确保通信的同步性。

LaunchCommand函数最后一个形参定义了一种LCFLAG的结构体,该结构体定义了与服务器的通讯协议,本文给出了工控领域常用的Modbus和SCPI协议,还定义了发送超时时间以及重复发送次数。

下面是完整的CSocketEx源代码,分别给出头文件(CSocketEx.h)和源文件(CSocketEx.cpp)

CScoketEx.h 

#pragma once
#include <WinSock2.h>

/********************** Asynchronous Ethernet TCP/IP Process **********************

History
        1st Edition        Created by Zhou in 2018.02.05 
        2nd Edition        Modified by Zhou in 2018.05.03 (Modify closesocket function)
        3rd Edition        Modified by Zhou in 2018.05.09 (Add the type of LCFLAG struct eg.Modbus and SCPI)
        4th Edition        Modified by Zhou in 2018.12.11 (Add the type of LCFLAG struct eg.NULL and Julabo)

**********************************************************************************/

#define NULL 0
#define MODBUS 1
#define SCPI 2
#define SCPI_UNIT 3
#define JULABO 4


typedef struct
{
    int type;
    int len;
    char buff[32];
    int timeout;
    int retry;
}LCFLAG;

class CSocketEx
{
public:
    CSocketEx();
    ~CSocketEx();
public:
    BOOL InitSocketLib();
    BOOL UnInitSocketLib();
public:
    BOOL SocketEx(int af, int type, int protocol); 
    BOOL BindEx(const struct sockaddr* pName, int nameLen); 
    BOOL ListenEx(int log); 
    SOCKET AcceptEx(struct sockaddr* pAddr, int* pAddrlen); 
    int ConnectEx(const struct sockaddr *pName, int namelen); 
    BOOL CloseSocketEx();
    int RecieveChar(char* pBuff, int len, int flag); 
    int Write2LocalPort(const char* pBuff, int len, int flag);
    int LaunchCommand(unsigned char* pBuff, int len, unsigned char* pRecvBuff, LCFLAG* pFlag);
    static UINT RecvThread(LPVOID pParam);
    BOOL StartMonitoring();
    BOOL RestartMonitoring();
    BOOL StopMonitoring();
public:
    SOCKET m_sock; 
    CRITICAL_SECTION m_csCommunicationSync;
    CRITICAL_SECTION m_csIFSync;
    HANDLE    m_hRecvEvent;
    int m_RecvLen;
    unsigned char m_pRecvBuff[1024];
    CWinThread* m_pThread;
};

CSocketEx.cpp

#include "StdAfx.h"
#include "SocketEx.h"

CSocketEx::CSocketEx()
{
    m_sock = INVALID_SOCKET;
    m_hRecvEvent = NULL;
    m_RecvLen = 0;
}

CSocketEx::~CSocketEx()
{
}

/* Load the SockLib */
BOOL CSocketEx::InitSocketLib()
{
    WORD wVersionRequested; 
    WSADATA wsaData; 
    int err;

    wVersionRequested = MAKEWORD(2, 2); 

    err = WSAStartup(wVersionRequested, &wsaData); 
    if (err != 0)
    {
        /* Tell the user that we could not find a usable */
        /* WinSock DLL.*/

        AfxMessageBox(_T("WSAStartup:err"));
        return FALSE;
    }

    /* Confirm that the WinSock DLL supports 2.2.*/
    /* Note that if the DLL supports versions greater    */
    /* than 2.2 in addition to 2.2, it will still return */
    /* 2.2 in wVersion since that is the version we      */
    /* requested.                                        */

    if (LOBYTE(wsaData.wVersion) != 2 ||
        HIBYTE(wsaData.wVersion) != 2) {
        /* Tell the user that we could not find a usable */
        /* WinSock DLL.                                  */
        WSACleanup();
        return FALSE;
    }

    /* The WinSock DLL is acceptable. Proceed. */
    return TRUE;

}

/* Clear the SockLib */
BOOL CSocketEx::UnInitSocketLib()
{
    return ::WSACleanup();
}

/* Create the socket */
BOOL CSocketEx::SocketEx(int af, int type, int protocol)
{
    m_sock = socket(af, type, protocol); 
    if (m_sock == INVALID_SOCKET)
    {
        return FALSE;
    }
    else
    {
        InitializeCriticalSection(&m_csCommunicationSync);
        InitializeCriticalSection(&m_csIFSync);
        return TRUE;
    }
}

/* Bind the local port */
BOOL CSocketEx::BindEx(const struct sockaddr* name, int namelen)
{
    ASSERT(INVALID_SOCKET != m_sock);

    BOOL bRet = bind(m_sock, name, namelen);

    if (bRet == SOCKET_ERROR) return FALSE;
    else return TRUE;
}

/* Only used in server mode */
BOOL CSocketEx::ListenEx(int log)
{
    ASSERT(INVALID_SOCKET != m_sock);
    BOOL bRet = listen(m_sock, log);

    if (bRet == SOCKET_ERROR)    return FALSE;
    else return TRUE;
}

/* Only used in server mode */
SOCKET CSocketEx::AcceptEx(struct sockaddr* pAddr, int* pAddrlen)
{
    ASSERT(INVALID_SOCKET != m_sock);
    SOCKET socket = accept(m_sock, pAddr, pAddrlen);
    return socket;
}

/* Recieve chars in local port */
int CSocketEx::RecieveChar(char* pBuff, int len, int flags)
{
    ASSERT(INVALID_SOCKET != m_sock);
    return recv(m_sock, pBuff, len, flags);
}

/* Write chars to local port */
int CSocketEx::Write2LocalPort(const char* pBuff, int len, int flags)
{
    ASSERT(INVALID_SOCKET != m_sock);
    return send(m_sock, pBuff, len, flags);
}

/* Connect the server */
int CSocketEx::ConnectEx(const struct sockaddr *pName, int nameLen)
{
    ASSERT(INVALID_SOCKET != m_sock);

    if (m_hRecvEvent != NULL)
        ResetEvent(m_hRecvEvent);
    m_hRecvEvent = CreateEvent(NULL, TRUE, FALSE, NULL); // 手动复位信号且初始无信号的事件

    return connect(m_sock, pName, nameLen);
}

/* CommunicationSync */
int CSocketEx::LaunchCommand(unsigned char* pBuff, int len, unsigned char* pRecvBuff, LCFLAG* pFlag)
{
    EnterCriticalSection(&m_csIFSync);

    int recvlen = 0;
    BOOL bSucc = FALSE;
    int retry = pFlag->retry;

    do
    {
        EnterCriticalSection(&m_csCommunicationSync);
        m_RecvLen = 0;        
        ResetEvent(m_hRecvEvent); // No-signal
        LeaveCriticalSection(&m_csCommunicationSync);
        Write2LocalPort((const char*)pBuff, len, 0);
        

        while (1)
        {
            if (retry) retry--;
            if (WAIT_OBJECT_0 == WaitForSingleObject(m_hRecvEvent, pFlag->timeout))
            {
                EnterCriticalSection(&m_csCommunicationSync);
                if (pFlag->type == MODBUS)
                {
                    if (m_RecvLen >= pFlag->len) 
                    {
                        memcpy(pRecvBuff, m_pRecvBuff, recvlen = m_RecvLen);
                        bSucc = TRUE;
                    }
                    LeaveCriticalSection(&m_csCommunicationSync);
                    ResetEvent(m_hRecvEvent);
                }
                else if (pFlag->type == SCPI_UNIT)
                {
                    if (m_pRecvBuff[m_RecvLen - 1] == 0x0A) // End with 0x0A
                    {
                        memcpy(pRecvBuff, m_pRecvBuff, recvlen = m_RecvLen - 2); // with UNIT
                        bSucc = TRUE;
                    }
                    LeaveCriticalSection(&m_csCommunicationSync);
                    ResetEvent(m_hRecvEvent);
                }
                else if (pFlag->type == SCPI)
                {
                    if (m_pRecvBuff[m_RecvLen - 1] == 0x0A) // End with 0x0A
                    {
                        memcpy(pRecvBuff, m_pRecvBuff, recvlen = m_RecvLen - 1); // without UNIT
                        bSucc = TRUE;
                    }
                    LeaveCriticalSection(&m_csCommunicationSync);
                    ResetEvent(m_hRecvEvent);
                }
                else if (pFlag->type == JULABO)
                {
                    if ((m_pRecvBuff[m_RecvLen-1]==0x0A)&&(m_pRecvBuff[m_RecvLen-2]==0x0D)) // End with 0x0d&0x0a
                    {
                        memcpy(pRecvBuff, m_pRecvBuff, recvlen = m_RecvLen - 2); 
                        bSucc = TRUE;
                    }
                    LeaveCriticalSection(&m_csCommunicationSync);
                    ResetEvent(m_hRecvEvent);
                }
                else if (pFlag->type == NULL)
                {
                    Sleep(50); // wait for a while
                    memcpy(pRecvBuff, m_pRecvBuff, recvlen = 0);
                    bSucc = TRUE;
                    LeaveCriticalSection(&m_csCommunicationSync);
                    ResetEvent(m_hRecvEvent);
                }

                if (bSucc)
                {
                    retry = 0;
                    break;
                }
            }
            else 
                break;
        }

    } while (!bSucc && retry);

    LeaveCriticalSection(&m_csIFSync);

    return recvlen;
}

/* Start the monitoring thread */
BOOL CSocketEx::StartMonitoring()
{
    if (!(m_pThread = AfxBeginThread((AFX_THREADPROC)RecvThread, this, THREAD_PRIORITY_HIGHEST)))
        return FALSE;
    TRACE("Thread started\n");
    return TRUE;
}

/* Restart the RecvThread */
BOOL CSocketEx::RestartMonitoring()
{
    TRACE("Thread resumed\n");
    m_pThread->ResumeThread();
    return TRUE;
}

/* Suspend the RecvThread */
BOOL CSocketEx::StopMonitoring()
{
    TRACE("Thread suspended\n");
    m_pThread->SuspendThread();
    return TRUE;
}

/* Create thread to recieve char */
UINT CSocketEx::RecvThread(LPVOID pParam)
{
    CSocketEx* pSock = (CSocketEx*)pParam;
    while (1)
    {
        Sleep(1);
        pSock->m_RecvLen = pSock->RecieveChar((char*)pSock->m_pRecvBuff, 1024, 0);
        if (pSock->m_RecvLen)
        {
            SetEvent(pSock->m_hRecvEvent);
        }        
    }
    
    return 0;
}

/* Close socket */
BOOL CSocketEx::CloseSocketEx()
{
    return closesocket(m_sock);
}

 

 

 

 

 

转载于:https://www.cnblogs.com/pyInter/p/10625993.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值