⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 modbusclient.cpp

📁 Convert Interface on a embedded System
💻 CPP
📖 第 1 页 / 共 2 页
字号:
#include "StdAfx.h"
#include "ModbusClient.h"
/*
 *	ClientThread() callback function
 *
 *	Called as the client handler thread.
 *	Handle of the CONNECTION to be used by thread is passed-in as thread's argument.
 */
UINT ClientThread( LPVOID lpParam )
{
	HANDLE hConn = (HANDLE) lpParam;

	BYTE    buffer[LEN_MAXADU];
	REQUEST request;
	WORD    wRcvd;
	WORD    wSend;
    BYTE    idError;

	// Get connection's socket ID
	SOCKET socket = CModbusConnection::GetConnSocket( hConn );
   	
	// Get connection's 'stop' event object
	HANDLE hStop = CModbusConnection::GetConnStopEvent( hConn );
   	
	// Setup WSAOVERLAPPED for overlapped read operations
	WSAOVERLAPPED wsaover;
	wsaover.hEvent = WSACreateEvent();

	// Continue to receive client's requests until disconnect, stop signal, or error
	while( (wRcvd = CModbusClient::ReceiveRequest( socket, buffer, &wsaover, hStop )) > 0 )
    {
		// Parse the request
		idError = CModbusClient::ParseRequest( buffer, wRcvd, &request );
		
		if( idError == 0 )
	    {
            // Process the request (transfer values to/from 'Registers' data area)
			idError = CModbusClient::ProcessRequest( &request );
		}

	    if( idError == 0 )
	    {
			// Make a positive response to send to the client
			wSend = CModbusClient::MakePositiveResponse( &request, buffer );
		}
		else
		{
			// Make an error response to send to the client
			wSend = CModbusClient::MakeErrorResponse( idError, request.bFnCode, buffer );
		}

		// Send response to client
		if( !CModbusClient::SendResponse( socket, buffer, wSend ) )
		{
			// Error on socket while sending response -- terminate connection
			break;
		}

		// Keep statistics
		CModbusConnection::IncrementConnCounters( hConn, (idError == 0), wRcvd, wSend );
    }

	// Close the connection being used by this client thread
	CModbusConnection::CloseConnection( hConn );

	// Clean up
    WSACloseEvent( wsaover.hEvent );
	return 0;
}

// The array of words that constitutes the 'Registers' data area
WORD awRegs[N_REGISTERS] = { 0 };

CRITICAL_SECTION csRegs;

CModbusClient::CModbusClient(void)
{
	InitializeCriticalSection( &csRegs );
}

CModbusClient::~CModbusClient(void)
{
	DeleteCriticalSection( &csRegs );
}

void CModbusClient::StartClientThread( HANDLE hConn )
{
	HANDLE hClientThread;
	DWORD  dwThreadAddr;

	// Start a client thread to handle this connection
	hClientThread = AfxBeginThread(ClientThread, (LPVOID)hConn);
	//hClientThread = CreateThread( NULL,            // Security
	//					          0,		       // Stack size
	//					          ClientThread,    // Function address
	//					          hConn,  	       // Argument
	//					          0,		       // Init flag
	//					          &dwThreadAddr);  // Thread address
	if( hClientThread == NULL )
	{
		CLogger::GetInstance()->Log( LOG_ERROR, _T("Unable to create client thread"), GetLastError() );
		CModbusConnection::CloseConnection( hConn );
	}
	else
	{
		// Won't be using the thread handle, so close it now. (Thread will continue to run)
		CloseHandle( hClientThread );
	}
}



/*
 *	ReceiveRequest() function
 *
 *	Receives the client's Modbus/TCP request.  Calling thread will be blocked
 *	until the complete request is received or until 'stop' signal is set.
 *
 *	Parameters:
 *		[in]  socket     The socket to be used for receiving request
 *		[out] buffer     The buffer for receiving the request
 *		[in]  lpOverlap  Pointer to WSAOVERLAPPED struct (used for overlapped input)
 *		[in]  hStop      The 'stop' event object
 *
 *	Returns zero if error, disconnect or stop signal; otherwise, 
 *	returns the total length (in bytes) of the request
 */
WORD CModbusClient::ReceiveRequest( SOCKET socket, LPBYTE buffer, LPWSAOVERLAPPED lpOverlap, HANDLE hStop )
{
	WORD wLenPDU;
	WSABUF wsabuf;

	// Receive request's Modbus/TCP Header
	wsabuf.buf = (char*)buffer;
	wsabuf.len = LEN_HEADER;
	if( !ReceiveBytes( socket, &wsabuf, lpOverlap, hStop ) )
		return 0;

	// Get the length of request's PDU from the MBAP Header
	wLenPDU = ParseHeader( buffer );
	if( wLenPDU == 0 )
		return 0;

	// Receive request's PDU (filling buffer immediately following the header)
	wsabuf.buf = (char*)buffer + LEN_HEADER;
	wsabuf.len = wLenPDU;
	if( !ReceiveBytes( socket, &wsabuf, lpOverlap, hStop ) )
		return 0;

	// Return total length of request
	return LEN_HEADER + wLenPDU;
}


/*
 *	ReceiveBytes() function
 *
 *	Receives a specified number of bytes from a client.  Calling thread will be blocked
 *	until the number of requested bytes is received or until 'stop' signal is set.
 *
 *	Parameters:
 *		[in] socket     The socket to be used for receiving the bytes
 *		[in] lpBuffer   Pointer to WSABUF struct (preset with number of bytes to receive)
 *		[in] lpOverlap  Pointer to WSAOVERLAPPED struct (used for overlapped input)
 *		[in] hStop      The 'stop' event object
 *
 *	Returns FALSE if error, disconnect or stop signal; otherwise, returns TRUE
 */
BOOL CModbusClient::ReceiveBytes( SOCKET socket, LPWSABUF lpBuffer, LPWSAOVERLAPPED lpOverlap, HANDLE hStop )
{
	INT    nRet;
	DWORD  dwRcvd;
	DWORD  dwFlags;
	HANDLE ahEvents[2];

	// Loop until all bytes are received from client
	while( lpBuffer->len > 0 )
	{
		dwFlags = 0;
		nRet = WSARecv( socket,		// Socket
						lpBuffer,	// WSABUF
						1,			// Number of buffers
						&dwRcvd,	// Bytes received
						&dwFlags,	// Flags
						lpOverlap,	// WSAOVERLAPPED
						NULL );		// Completion function
		if( nRet != 0 )
		{
			if( WSAGetLastError() == WSA_IO_PENDING )
			{
				// Wait for the receive to complete or stop signal
				ahEvents[0] = hStop;
				ahEvents[1] = lpOverlap->hEvent;
				if( WaitForMultipleObjects( 2, ahEvents, FALSE, INFINITE ) == WAIT_OBJECT_0 )
				{
					// Stop signal received
					return FALSE;
				}

				// Get I/O result
				if( !WSAGetOverlappedResult( socket, lpOverlap, &dwRcvd, FALSE, &dwFlags ) )
				{
					CLogger::GetInstance()->Log(LOG_ERROR, _T("WSAGetOverlappedResult()"), WSAGetLastError() );
					return FALSE;
				}
			}
			else
			{
				CLogger::GetInstance()->Log(LOG_ERROR, _T("WSARecv()"), WSAGetLastError() );
				return FALSE;
			}
		}

		// Check for client disconnect (zero-length request received without error)
		if( dwRcvd == 0 )
		{
			CLogger::GetInstance()->Log( LOG_INFO, _T("Client on socket %hu disconnected"), socket );
			return FALSE;
		}
		
		// Adjust WSABUF for actual number of bytes read
		lpBuffer->len -= dwRcvd;
		lpBuffer->buf += dwRcvd;
	}

	return TRUE;
}


/*
 *	ParseHeader() function
 *
 *	Parses and validates the MBAP Header of the client's request.
 *
 *	Parameters:
 *		[in] buffer  The buffer containing the client's request
 *
 *	Returns zero if invalid header; otherwise, 
 *	returns the length (in bytes) of the request's PDU
 */
WORD CModbusClient::ParseHeader( LPBYTE buffer )
{
	int iLenPDU;

	// Check that Protocol ID (second word of header) indicates Modbus (zero)
	if( MAKEWORD(buffer[3], buffer[2]) != 0 )
	{
		CLogger::GetInstance()->Log(LOG_INFO, _T("The request from client on socket had invalid Protocol ID"));
		return 0;
	}

	// Use Command Length (third word of header) to compute length of request's PDU
	iLenPDU = MAKEWORD(buffer[5], buffer[4]) - LEN_DESTID;

	// Check that Command Length is an acceptable value
	if( iLenPDU < LEN_MINPDU || iLenPDU > LEN_MAXPDU )
	{
		CLogger::GetInstance()->Log(LOG_INFO, _T("The request from client on socket had invalid Command Length"));
		return 0;
	}

	return (WORD) iLenPDU;
}


/*
 *	ParseRequest() function
 *
 *	Parses and validates the PDU of the client's request.
 *
 *	Parameters:
 *		[in]  buffer    The buffer containing the client's request
 *		[in]  wRcvd     The length (in bytes) of the client's request

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -