Archived Forum Post

Index of archived forum posts

Question:

Socket is locked internally while waiting for some data and we are not able to send data at the same time from another thread

Dec 18 '14 at 17:02

Hi, We have been using CkSockets for about couple years on different systems (iOS, Windows, Android).

Due to new Apple's requirements (x64 version of application is must have for latest version of iOS) we were forced to upgrade our 9.4.0 version to 9.5.0.46 version on the iOS side. After changes of the syntax due to new interfaces we found out that all our functionality is broken with the latest version of CkSocket.

How do we usually used sockets?

We used synchronous sockets for communication. Async solution was implemented by using separate threads that access the same socket. For e.q. We have "ThreadReceive" for waiting and handling any data. In this thread we used "ReceiveBytesN" function. The time for waiting data is different from time to time. Sometime we can wait couple milliseconds and sometimes we can wait dozen of seconds. During waiting for data we should be able to send some messages through the same socket. For sending data we have another thread like "ThreadSend" and use there any "SendBytes" function.

On the version 9.4.0 everything worked perfectly. But after upgrade to 9.5.0.46 looks like the function "ReceiveBytesN" in thread "ThreadReceive" locks the socket for any other operations. We want to send data from "ThreadSend" while we are waiting for new data.

We tested similar functionality on different libraries (Mac, iOS, Windows) and results are the same. Socket is locked and we are not able to send data while waiting on "ReceiveBytesN" function.

We hoped that AsyncReceiveBytesN will not lock the socket, but unfortunately unsuccessful.

We only found such solution: while (!pSocket->PollDataAvailable()) { sleep(1); } and allow to call "ReceiveBytesN" only if data is available.

As we are doing high performance communication between different parts of our application, we've got huge performance problems.

So the question: Is that correct behavior? or just a bug?

In case it is a new feature, how can we solve our problem (sending and receiving data from different threads at the same time). We wouldn't like to change the library, it is time consuming for development and very risky from QA prospective.


Answer

Volodymyr,

I'm sorry for the delay -- I finally got to a position where I could work on the problem. I thought for sure this was a bug in Chilkat, but I was wrong.

Bidirectional communication (simultaneous reading/writing) is supported w/ the asynchronous calls.

Bidirectional communication is ALSO SUPPORTED with the synchronous read/write methods, however, the Chilkat Socket object (CkSocket / CkoSocket) must remain thread-safe. This means that for a given CkSocket object instance, only one call at a time may be active. If one thread calls a method, the other will be blocked until the 1st thread's call completes.

The way to get around this is via the CloneSocket method (which was undocumented, and I'm sorry for that). The CloneSocket method returns a copy of the socket object that shares the same underlying TCP (or SSL/TLS) connection. Now each thread can have it's own object, and each object instance is thread-safe. You'll have to update your code to use CloneSocket.

Asynchronous calls don't have a need for CloneSocket because they return immediately while the read/write proceeds in a background thread.

The reference documentation will be updated (soon) and I also noticed that CloneSocket was not made available for .NET and ActiveX. With v9.5.0.47, CloneSocket will become available for those platforms.


Answer

Volodymyr, thanks for reporting the problem with highly-useful information. :)

It sounds like a bug to me. I'll work on the problem, but the work cannot begin until an ongoing task is finished, which may take another day. I'll post a download link when I have a new build for you to test.


Answer

Thanks for your answer. Will look forward for any updates from you.

Just small update with pieces of code. Server's code:

#include <iostream>
#include <CkSocket.h>
#include <CkMultiByteBase.h>
#include <unistd.h>

int main(int argc, const char * argv[]) {

    CkSocket listenSocket;

   //******************
   //Code to unlock the socket and prepare communications

    success = listenSocket.BindAndListen(5555,25);
    if (success != true) {
        printf("%s\n",listenSocket.lastErrorText());
        return 0;
    }

        CkSocket *connectedSocket = 0;

    while (true)
    {

        connectedSocket = listenSocket.AcceptNextConnection(20000);

        if (connectedSocket == 0 )
        {
            int errorCode = listenSocket.get_ConnectFailReason();

            //Check to see if this was just a accept timeout, which isn't a problem.
            if (errorCode == 0)
                continue;

            printf("%s\n",listenSocket.lastErrorText());

            return 0;
        }
        break;
    }

    //send something to client
    success = connectedSocket->SendCount(100);
    if (success != true) {
        printf("%s\n",connectedSocket->lastErrorText());
        delete connectedSocket;
        return 0;
    }

    //just wait and print all received data
    do{
        int d = connectedSocket->ReceiveCount();
        std::cout << d <<std::endl;
    }while (true);

    connectedSocket->Close(20000);

    return 0;
}

Client's code:

    #include <iostream>
    #include <CkSocket.h>
    #include <thread> 
    #include <unistd.h>
    #include <CkMultiByteBase.h>

    // just a socket holder that wraps CkSocket. This class was introduced due to some 
    // problems with access to raw socket that was a global var on mac osx and xCode.

    class MySocketWrapper{
    public:
        CkSocket* socket(){return &s;}
        CkSocket s;
    };
       MySocketWrapper sw;

    // Receive thread function
    void receive()
    {
        do{
           int d = sw.socket()->ReceiveCount();
            std::cout << d <<std::endl;
        }while (true);
    }

    // Send thread function
    void send()
    {
        sleep(5);
        for (int i=0; i < 1000000; ++i) {
            std::cout << "trying to send " << i ;
            sw.socket()->SendCount(i);
            std::cout << "Sent " << i;
            sleep(100);
        }
    }

    int main(int argc, const char * argv[]) {

        bool success;

       //******************
       //Code to unlock the socket and prepare communications

        bool useSsl;
        useSsl = false;

        const char * remoteHost;
        remoteHost = "127.0.0.1";
        long remotePort;
        remotePort = 5555;

        //  Connect to the remote host asynchronously in a background thread.
        success = sw.socket()->Connect(remoteHost,5555,useSsl,20000);
        if (success != true) {
            printf("%s\n",sw.socket()->lastErrorText());
            return 0;
        }

        //just start send and receive threads and wait forever
        std::thread first(receive);
        std::thread second(send);

        first.detach();
        second.detach();

        while (true) {
            sleep(1000);
        }

        sw.socket()->Close(20000);

        return 0;
    }

The code looks pretty weird, but it is enough to prove what I've written.


Answer

Thanks for info, Could you please briefly explain who should close cloned socket when main socket is closing.


Answer

The cloned socket is just another object instance that shares the same underlying TCP socket. If you close the socket from one CkSocket object instance, it's closed for the other -- because it's the same underlying socket..