• Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 2868
  • Last Modified:

Winsock programming with Delphi

Im working on a chat client/server as project in Delphi and have decided to rewrite the thing from scratch... without using Delphi's pre-made socket components. I want to acheive a better understanding of how winsock programming should be done 'properly'.

So far i have managed to create a socket and connect (within a seperate execution thread) but i am unsure how exactly to read data from it, and for that matter.. when !

A simple sample application would be very usefull as packages such as F.Piettes ICS seem far too advanced for me to grasp the concept.

Thanks in advance.
0
HaL
Asked:
HaL
  • 3
1 Solution
 
steve_hskCommented:
Hi Hal ...

There are so many variations with winsock programming that we can't really list them all here. I've shown an example that I use for a UDP Client, I hope this helps give you a better idea - Apologies for the length, I think I've removed most of the non-specific stuff !

Here are some links you may find useful :

1) This is an AMAZING tutorial programme that allows you to individually test the different Winsock API funcitons calls one by one to analyse what happens at each stage, and learn all about clients and servers !!!
http://www.siddiqi.org/~waseem/dev.html

2) General Sockets resource site with FAQ and tutorial
http://www.sockets.com/

3) Another great tutorial site - Invaluable !
http://tangentsoft.net/wskfaq/

Doing a web search for Delphi : Winsock brings up 100's of other good sites with loads of examples too !!!

UNIT ATIA_UDPSockControl;
INTERFACE // Interface and Class Declaration
USES Windows, WinSock2, Classes;

TYPE TComms = CLASS (TThread)
PUBLIC
      // Declare Member Variables
      m_aSocketHandle : TWOHandleArray;

      // Declare Class Member Functions
      CONSTRUCTOR Create (VAR bSuccess : Boolean);
      DESTRUCTOR  Destroy;    OVERRIDE;
      PROCEDURE   Execute;    OVERRIDE;
PRIVATE
      // Declare Member Variables
      m_tsSecAttrib : _SECURITY_ATTRIBUTES;
      m_tpAddress   : tSockAddrIn;
      m_tpEvent     : PWSANETWORKEVENTS;
      m_tUDPSock    : TSocket;

      // Standard Socket Events
      PROCEDURE DecipherEvent (dwEventTrig : DWORD);
      FUNCTION  InitialiseWinSock : Integer;
      FUNCTION  ConfigureSocket   : Integer;
      PROCEDURE ReadData;
END;

IMPLEMENTATION // Interface and Class Definition

CONSTRUCTOR TComms.Create (VAR bSuccess : Boolean);
BEGIN  
    // 1.0 Inherit parent properties
    INHERITED Create(TRUE);

    // 1.1 Initialise Member Variables (Thread)
    FreeOnTerminate := FALSE;
    Priority        := tpHigher;

    // 1.2 Create the 'WM_QUIT' and Socket Events
    m_aSocketHandle[0] := CreateEvent (NIL, TRUE, FALSE, NIL);
    m_aSocketHandle[1] := WSACreateEvent;

    // 1.3 Configure the Security Attributes Structure
    m_tsSecAttrib.bInheritHandle       := TRUE;
    m_tsSecAttrib.lpSecurityDescriptor := NIL;
    m_tsSecAttrib.nLength              := sizeof(m_tsSecAttrib);

    // 1.4 Create a new Network Events Structure
    new (m_tpEvent);

    // 1.5 Initialise WinSock, Configure Socket, and 'Connect'
    m_dwErrorCode := InitialiseWinSock;

    // 1.6 Analyse Initialisation Result and Action
    IF (m_dwErrorCode = SUCCESS) THEN
    BEGIN
         bSuccess := TRUE;
    END
    ELSE
    BEGIN
         bSuccess := FALSE;
    END;
END;

DESTRUCTOR TComms.Destroy;
BEGIN
    // 1.0 Close all sockets and finish WSA process
    WSACleanup;

    // 1.1 Inherit standard parent Destroy Process
    INHERITED Destroy;
END;

PROCEDURE TCOMMS.Execute;
VAR
   dwWaitReturn : DWORD;
BEGIN
    // 1.0 Whilst the Thread is not to be terminated ...
    WHILE NOT (Terminated) DO
    BEGIN
        // 1.1 Look for one Events in the Event queue for this thread, and
        // await here (set a trigger) until one Event is received ...
        dwWaitReturn := WaitForMultipleObjects (2, @m_aSocketHandle, FALSE,INFINITE);

        // 1.2 An alyse the Event that has been triggered
        IF (dwWaitReturn = WAIT_FAILED) THEN
        BEGIN
             // 1.4 Terminate this Thread
             Terminate;        
        END
        ELSE
        BEGIN
             // 1.5 Now one Event has been retreived, attempt to Decode it
             DecipherEvent(dwWaitReturn - WAIT_OBJECT_0);
        END;
    END;
END;

PROCEDURE TComms.DecipherEvent (dwEventTrig : DWORD);
BEGIN
    // 1.0 Interpret the Message as a SOCKET EVENT
    IF (dwEventTrig = 1 ) THEN
    BEGIN
         // 1.1 Convert the Event Object Handle to FD_Event for interpretation
         IF NOT (WSAENUMNetworkEvents (m_tUDPSock,
                                       m_aSocketHandle[1],
                                       m_tpEvent) = SOCKET_ERROR) THEN
         BEGIN
              // 1.2 If the Socket Event indicates that it contains data
              IF (m_tpEvent.lNetworkEvents = FD_READ) THEN
              BEGIN
                   // 1.3 Call function to Read Data appropriately
                   ReadData;
              END
              // 1.4 If the Socket Event indicates that it has been closed
              ELSE IF (m_tpEvent.lNetworkEvents = FD_CLOSE) THEN
              BEGIN
                   // 1.6 Terminate this Thread
                   Terminate;

                   // 1.7 Ensure that the socket is closed before further actioning
                   CloseSocket(m_tUDPSock);
              END;
         END;
    END
    // 1.8 Interpret the Message as an WM_QUIT
    ELSE IF (dwEventTrig = 0 ) THEN
    BEGIN
         // 1.9 Ensure that the socket is closed
         CloseSocket(m_tUDPSock);

         // 2.0 Terminate this thread now !
         Terminate;
     END;
END;

FUNCTION TComms.InitialiseWinSock : Integer;
VAR
   wVersionRequested : WORD;
   wsaData           : TWSAData;
BEGIN
    // 1.0 Construct the Version Data to Request from WinSock
    wVersionRequested := MAKEWORD(2,2);

    // 1.1 The WSAStartup function must be first    
    IF (WSAStartup(wVersionRequested, wsaData) <> 0) THEN
    BEGIN    
        // 1.2 Inform calling function that Winsock Initialisation Failed
        Result := WSAGetLastError;
    END
    ELSE
    BEGIN
         // 1.3 Confirm that the WinSock DLL supports 2.2.
         IF (LOBYTE(wsaData.wVersion) <> 2)   AND
            (HIBYTE(wsaData.wVersion) <> 2)  THEN
         BEGIN
              // 1.4 Inform calling function that Winsock Initialisation Failed
              Result := WSAGetLastError;

              // 1.5 Delete the Socket Initialisation
              WSACleanup();
         END
         ELSE
         BEGIN
              // 1.6 Create and Configure the Listening Socket
              Result := ConfigureSocket;
         END;
    END;
END;

FUNCTION TComms.ConfigureSocket : Integer;
VAR      bAddrReuse : PChar;
BEGIN
    // 1.0 Create the Socket Object
    m_tUDPSock := socket(AF_INET, SOCK_DGRAM, 0);

    // 1.1 Ensure that the socket was created successfully
    IF (m_tUDPSock = INVALID_SOCKET) THEN
    BEGIN
        // 1.2 Inform calling function that Winsock Initialisation Failed
        Result := WSAGetLastError;

        // 1.3 Delete the Socket Initialisation
        WSACleanup();

        // 1.4 Exit from this Procedure
        Exit;
    END;

    // 1.5 Configure the Socket Parmaters
    m_tpAddress.sin_family      := AF_INET;
    m_tpAddress.sin_port        := htons(8601);
    m_tpAddress.sin_addr.S_addr := INADDR_ANY;

    // 1.6 Ensure that two sockets bound to the same port can exist together
    bAddrReuse := '1';

    // 1.7 Set this Re_Use_Addr Option true for this socket
    IF (setsockopt( m_tUDPSock,
                    SOL_SOCKET,
                    SO_REUSEADDR,
                    bAddrReuse,
                    Sizeof(Boolean)) = SOCKET_ERROR) THEN
    BEGIN
         // 1.8 Inform calling function that Winsock Initialisation Failed
         Result := WSAGetLastError;

         // 1.9 Delete the Socket Initialisation
         WSACleanup();

         // 2.0 Exit from this procedure
         Exit;
    END;

    // 2.1 The Windows Sockets bind function associates a local address and port number with a socket object
    IF (bind (m_tUDPSock,
              @m_tpAddress,
              sizeof(tSockAddrIn)) = SOCKET_ERROR) THEN
    BEGIN
         // 2.2 Inform calling function that Winsock Initialisation Failed
         Result := WSAGetLastError;

         // 2.3 Delete the Socket Initialisation
         WSACleanup();

         // 2.4 Exit from this procedure
         Exit;
    END;

    // 2.5 The Windows Sockets WSAASyncSelect function specifies a Msg object to be associated with the supplied set of FD_XXX network events
    IF (WSAEventSelect (m_tUDPSock,
                        m_aSocketHandle[1],
                        FD_READ OR FD_CLOSE) = SOCKET_ERROR) THEN
    BEGIN
         // 2.6 Inform calling function that Winsock Initialisation Failed
         Result := WSAGetLastError;

         // 2.7 Delete the Socket Initialisation
         WSACleanup();

         // 2.8 Exit from this procedure
         Exit;
    END
    ELSE
    BEGIN
         // 2.9 Operation Successful - Sockets Initialised
         Result := SUCCESS;
    END;
END;

PROCEDURE TComms.ReadData;
VAR
   tMsgRecord : TPDataRecord;
BEGIN
     // 1.0 Initialise Memory Data
     New (tMsgRecord);
     tMsgRecord.iMsgType := 0;
     tMsgRecord.Output := '';
     tMsgRecord.iDataSize := 0;

     // 1.1 Read the captured data into the prepared buffer
     tMsgRecord^.iDataSize := recv(m_tUDPSock, tMsgRecord^.tDataBuff, 500, 0);

     // 1.2 Analyse the receive socket call .....
     IF (tMsgRecord^.iDataSize = SOCKET_ERROR) THEN
     BEGIN
          // 1.3 Report Back to Control HQ and GUI the status
          tMsgRecord^.iDataSize := WSAGetLastError;

          // 1.4 Terminate this Thread
          Terminate    
     END;

          // ***** NOTE ***** This section will have your own specifics here .....
     
     // 1.5 Initialise the thread by passing captured raw data to decode
     PostThreadMessage (m_tFASTDEC.m_iThreadID,
                        WM_DECODE_DATA,
                        WPARAM(tMsgRecord),
                        LPARAM(PLATFORM));
END;
END.

Hope this is helpful,
STeve
0
 
HaLAuthor Commented:
Wow, first time ive used this site, i didnt expect an answer so soon. Thanks for the expert advice :)

Hopefully i will have a working socket class of my own in a few days time.


Thanks again.
0
 
steve_hskCommented:
Hal ...

Happy to help, as I said there are numerous examples on the web for TCP, or servers, or IOCP's etcetc ... just takes a little searching. Be aware that not all of them include good coding styles or best practices, but should give a far idea at least  ;-)

Good luck !
STeve
0
 
steve_hskCommented:
Hal ...

Happy to help, as I said there are numerous examples on the web for TCP, or servers, or IOCP's etcetc ... just takes a little searching. Be aware that not all of them include good coding styles or best practices, but should give a far idea at least  ;-)

Good luck !
STeve
0

Featured Post

VIDEO: THE CONCERTO CLOUD FOR HEALTHCARE

Modern healthcare requires a modern cloud. View this brief video to understand how the Concerto Cloud for Healthcare can help your organization.

  • 3
Tackle projects and never again get stuck behind a technical roadblock.
Join Now