-1

I have some modbus ethernet tcp communications that I'm attempting to do in a DLL. I get numerous TCP Retransmissions from the target device, as seen in WireShark.

enter image description here

(In this image, 192.168.1.5 is the Modbus device. 192.168.1.72 is the computer)

However, when the same code is inserted directly into an application, there are no communication errors.

I'm wondering if DLLs have some sort of lower priority that can cause slower communications, or if anyone may have any insight as to why this code would run without TCP issue in an application, but not in a DLL.

Here is the dll header:

#ifndef __MAIN_H__
#define __MAIN_H__

#include <windows.h>

typedef void *eioTHandle;


#ifdef __cplusplus
extern "C"
{
#endif

__declspec(dllexport) int __stdcall eioConnect( unsigned short ModelId, char *Ip, eioTHandle *Handle );

#ifdef __cplusplus
}
#endif

#endif

And here is the source file:

#include "main.h"
#include <winsock2.h>
#include <ws2tcpip.h>

#include <stdint.h>

#define EIO500_S                        0
#define EIO500_MS                       1000

#define eioERROR    -1
#define eioSUCCESS   0

static uint8_t m_UnitId = 0xff;
static SOCKET m_Sock;


BOOL WINAPI DllMain(HINSTANCE hinstDLL,  DWORD fdwReason, LPVOID lpReserved )
{
    // Perform actions based on the reason for calling.
    switch( fdwReason )
    {
        case DLL_PROCESS_ATTACH:
         // Initialize once for each new process.
         // Return FALSE to fail DLL load.
            break;

        case DLL_THREAD_ATTACH:
         // Do thread-specific initialization.
            break;

        case DLL_THREAD_DETACH:
         // Do thread-specific cleanup.
            break;

        case DLL_PROCESS_DETACH:
         // Perform any necessary cleanup.
            break;
    }
    return TRUE;  // Successful DLL_PROCESS_ATTACH.
}


int __stdcall eioConnect( unsigned short ModelId, char *Ip, eioTHandle *Handle )
{

    WSADATA Wsa;
    struct sockaddr_in Server;

    int Result;
    char Buffer[256];
    char InBuffer[256];

    // CONNECTION --------------------------------------------------------------
    if (WSAStartup(MAKEWORD(2,2), &Wsa) != 0)
    {
        return eioERROR;
    }

    m_Sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

    if (m_Sock == INVALID_SOCKET)
    {
        WSACleanup();
        return eioERROR;
    }

    Server.sin_addr.s_addr = inet_addr(Ip);
    Server.sin_family = AF_INET;
    Server.sin_port = htons(502);

    if (connect(m_Sock, (struct sockaddr *)&Server, sizeof(Server))
        == SOCKET_ERROR)
    {
        closesocket(m_Sock);
        m_Sock = INVALID_SOCKET;
        WSACleanup();
        return eioERROR;
    }
    // -------------------------------------------------------------------------

    for (int Ctr = 0; Ctr < 50000; Ctr++)
    {


        // SEND COMMAND --------------------------------------------------------
        // 5 bytes in a Send Read Multiple Coils command.
        int NumBytes = 5;

        Buffer[0] = 0;
        Buffer[1] = 0;
        Buffer[2] = 0;
        Buffer[3] = 0;
        Buffer[4] = 0;
        Buffer[5] = NumBytes + 1;   // 1 for unit id.
        Buffer[6] = m_UnitId;

        // 0 = Function code.
        Buffer[7] = 0x01;

        // 1+2 = Address.
        Buffer[8] = 0;
        Buffer[9] = 8;

        // 3+4 = Number of bits to read.
        Buffer[10] = 0;
        Buffer[11] = 8;

        if (send(m_Sock, Buffer, NumBytes + 7, 0) == SOCKET_ERROR)
        {
            continue;
        }

        // ---------------------------------------------------------------------


        // WAIT FOR RECEIVE ----------------------------------------------------
        WSAEVENT RecvEvent;
        int Ret;

        RecvEvent = WSACreateEvent();

        WSAEventSelect( m_Sock, RecvEvent, FD_READ );

        Ret = WSAWaitForMultipleEvents(1, &RecvEvent, TRUE, 1000, FALSE);

        WSAResetEvent(RecvEvent);

        if (Ret == WSA_WAIT_TIMEOUT)
            continue;
        // -------------------------------------------------------------------------



        // Check for any reply.
        recv(m_Sock, InBuffer, 256, 0);


    }


    // DISCONNECT --------------------------------------------------------------
    Result = shutdown(m_Sock, SD_SEND);

    if (Result == SOCKET_ERROR)
    {
        closesocket(m_Sock);
        WSACleanup();
        m_Sock = INVALID_SOCKET;
        return eioERROR;
    }

    // Receive until the peer closes the connection.
    while (recv(m_Sock, Buffer, 256, 0) > 0);

    closesocket(m_Sock);
    WSACleanup();

    m_Sock = INVALID_SOCKET;
    // ------------------------------------------------------------------------

    return eioSUCCESS;

}

I've simplified the code as much as possible. The communication is in a loop for testing. The original application would poll this data from the device.

Mike Gibson
  • 342
  • 1
  • 3
  • 14

1 Answers1

0

No. From the network's perspective there's no difference in TCP segments sent some way or other. There may be a protocol prioritation though (QoS) that may cause packet drops when links are saturated.

A more likely cause could be a problem with the checksums: invalid checksums cause packet drops which in turn cause retransmissions. Possibly the API works slightly different when called from a DLL, so the checksums are calculated (correctly).

Zac67
  • 2,761
  • 1
  • 10
  • 21
  • But why would the API work any differently through a DLL than it does through an application? – Mike Gibson Apr 12 '18 at 13:24
  • I had forgotten to mention that in both cases (app and dll), this is a one-to-one connection. The only devices on the network are the computer and the modbus device. There is no traffic other than modbus traffic. And as can be seen in the code, this is unthreaded, so the entire packet cycle is linear. – Mike Gibson Apr 12 '18 at 13:25
  • No QoS policies are configured. – Mike Gibson Apr 12 '18 at 14:39
  • Sorry, I'm no Windows developer, but I know that some Win APIs are somewhat weird. You can rule out the network part though, it's in the API. Do verify the checksums on the packets being retransmitted. – Zac67 Apr 12 '18 at 14:56
  • Checksums are calculated by the API. I don't think I have access to them. – Mike Gibson Apr 12 '18 at 17:03