?
Solved

Winsock programming with Delphi

Posted on 2003-03-15
4
Medium Priority
?
2,539 Views
Last Modified: 2012-08-13
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
Comment
Question by:HaL
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 3
4 Comments
 
LVL 2

Accepted Solution

by:
steve_hsk earned 300 total points
ID: 8143534
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
 

Author Comment

by:HaL
ID: 8143610
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
 
LVL 2

Expert Comment

by:steve_hsk
ID: 8143853
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
 
LVL 2

Expert Comment

by:steve_hsk
ID: 8143859
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

Technology Partners: We Want Your Opinion!

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

A lot of questions regard threads in Delphi.   One of the more specific questions is how to show progress of the thread.   Updating a progressbar from inside a thread is a mistake. A solution to this would be to send a synchronized message to the…
Hello everybody This Article will show you how to validate number with TEdit control, What's the TEdit control? TEdit is a standard Windows edit control on a form, it allows to user to write, read and copy/paste single line of text. Usua…
Michael from AdRem Software explains how to view the most utilized and worst performing nodes in your network, by accessing the Top Charts view in NetCrunch network monitor (https://www.adremsoft.com/). Top Charts is a view in which you can set seve…
This is my first video review of Microsoft Bookings, I will be doing a part two with a bit more information, but wanted to get this out to you folks.
Suggested Courses

752 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question