Jump to content

[SOLVED] The Socket is blocking


photo

Recommended Posts

Hi

I play with Socket class and  I been tried several scenarios. The class blocks even in unblocking mode.

For sample:

1-The function Socket->gets() is blocking, return only when the remote server close the connection returned all receive as string. Include if you put it as nonblock() the socket.

2-If you use the function Socket->read(...) if the buffer is bigger that the receive Data it's pass right return the the data, but if buffer pass in Socket->read is smaller that the data receive then the second time that you try to read the rest of the data, this will blocking the thread too.  

Some of this scenarios I expose in the UnitTest attached 

Some snippet of the attach are here

-----------------------------------------------------------------------------------------------------------------

        TEST_METHOD(SocketClientTrial)
        {
            Ptr<Socket>  socket = Socket::create(Socket::SOCKET_STREAM);

            if (socket->open("localhost", Port)) 
            {
                if (socket->connect()) 
                {
                    // Pass Ok
                    /*
                    char tmp[100];
                    int len = socket->read(tmp, 49 - 34); // <== Ok, 
                    tmp[len] = 0;
                    Assert::AreEqual(tmp, "I am the server");
                    */

                    // NO PASS, IS BLOKING
                    /*
                    char tmp[100];
                    int len = socket->read(tmp, 5); // <= OK
                    len = socket->read(&tmp[5], 100-5); // <= BLOKING HERE, THE SECOND TIME
                    tmp[len+5] = 0;
                    Assert::AreEqual(tmp, "I am the server");
                    */

                    String res = socket->gets();    // <= BLOKING HERE TOO
                    Assert::AreEqual(res, "I am the server");

      ....

            socket->listen(10);   // Server
            socket->nonblock();   <-- using noblock parameter

            Ptr<Socket> asock = Socket::create(Socket::SOCKET_STREAM);

            while (1) 
            {
                if (socket->accept(asock))
                {
                    asock->puts("I am the server");
                    String res = asock->gets();                  // <= BLOKING HERE

      ....

-----------------------------------------------------------------------------------------------------------------------------------------------

/Roberto

 

Socket01Trial.cpp

Edited by roberto.alonso
Link to comment

Hello Roberto,

In the case of using gets(), this method attempts to read packets one character at a time. Use this method preferably only for File and Blob. When using read(), blocking is unavoidable, and this is correct. In this case, the standard system function recv(Socket s, char *buf, int len, int flags) blocks the thread. You can see the correct use case in the example below. To begin, write all the data in Blob, and then read from it.

BlobPtr blob = Blob::create();
// ....
blob->clear();
socket->readStream(blob->getStream(), max_packet_size);
blob->seekSet(0);
// ...
int i = blob->readInt();
string s = blob->readString();
// ...

 

Link to comment

Hi Karpych11 

Yes the recv function from standard system block in case that the buffer is empty only, but if the buffer have one byte with recv this will return that byte when you do a call, and this not is the case what is happen here.

best

Roberto

Edited by roberto.voxelfarm
Link to comment

Hi karpych11

I try your methodology and continue blocking, is the same as socket->gets(), that is blocked at the first call. 

    // Server part 
                BlobPtr blob = Blob::create();
                if (socket->accept(asock))
                {
                    asock->puts("I am the server");
                    // String res = asock->gets();
                    blob->clear();
                    asock->readStream(blob->getStream(), max_packet_size);    <= get blocked HERE
                    blob->seekSet(0);

                    String res = blob->readString();
                    Assert::AreEqual(res.get(), "Hi server");

                    asock->puts("bye");

....

// Client part

        TEST_METHOD(SocketClientTrial)
        {
            Ptr<Socket>  socket = Socket::create(Socket::SOCKET_STREAM);

            if (socket->open("localhost", Port)) 
            {
                if (socket->connect()) 
                {
                    BlobPtr blob = Blob::create();
                    blob->clear();
                    socket->readStream(blob->getStream(), max_packet_size);   << == CLIENT GET BLOCK HERE
                    blob->seekSet(0);
                    String res = blob->readString();

                    Assert::AreEqual(res, "I am the server");

                    socket->puts("Hi server");

                    blob->clear();
                    socket->readStream(blob->getStream(), max_packet_size);
                    blob->seekSet(0);
                    res = blob->readString();

                    // res = socket->gets();
                    Assert::AreEqual(res, "Bye");

...

Socket01Trial.cpp

Link to comment

Hi

Here is a mixed version doing a directly call to the winsock for receive that work fine, either on Nonblock mode as the normal mode.

// For the server 

...

            socket = Socket::create(Socket::SOCKET_STREAM, Port);
            socket->bind();
            socket->listen(10);
            socket->nonblock(); // <= Nonblock mode

            Ptr<Socket> asock = Socket::create(Socket::SOCKET_STREAM);

            while (socket->getFD()!=INVALID_SOCKET)
            {
                if (socket->accept(asock))
                {
                    asock->puts("I am the server");
                    int sockectHandle = asock->getFD();  // <== Get the Socket Handle
                    char tmp[100];
                    int len = -1;
                    while (len == -1)
                    {
                        len = ::recv(sockectHandle, tmp, sizeof(tmp), 0);     // <== Direct call to the winsock, 
                        if (len == -1)
                        {
                            usleep(100);
                        }
                    }
                    tmp[len] = 0;
                    Assert::AreEqual(tmp, "Hi server");
                    asock->puts("Bye");
                    usleep(100);
                    asock->close();
                }

                usleep(100);
            }
 ....

// Client part 

TEST_METHOD(SocketClientMixedTrial)
        {
            Ptr<Socket>  socket = Socket::create(Socket::SOCKET_STREAM);
            if (socket->open("localhost", Port))
            {
                if (socket->connect())
                {
                    char tmp[100];
                    int len = recvBuf(socket->getFD(), tmp, 5);                // the older version Block in the second read, that is way I split the receive on two read
                    len = recvBuf(socket->getFD(), &tmp[5], 100-5);

                    tmp[len+5] = 0;
                    Assert::AreEqual(tmp, "I am the server");
                    socket->puts("Hi server");

                    len = recvBuf(socket->getFD(), tmp, sizeof(tmp));
                    tmp[len] = 0;
                    Assert::AreEqual(tmp, "Bye");
                }
                socket->close();
            }
        }

        int recvBuf(int shandle, char *buf, int size)
        {
            return ::recv(shandle, buf, size, 0);   // <== direct call to the winsock, Look like your recv function is using a flag MSG_WAITALL, or other logic that block the receive
        }

... 

Of course this is including the "winsock.h" and not will be multiplatform. Please see the complete attachment.

best regards 

/Roberto

 

Socket01Trial.cpp

Edited by roberto.alonso
Link to comment

Hi!

asock->readStream(blob->getStream(), max_packet_size);    <= get blocked HERE
I think you need to use nonblock() function 2 times: socket->nonblock() and asock ->nonblock() (after "socket->accept(asock)" line).
Here is the source code of the Socket::read() function:

int ret = 0;
int flags = 0;
size_t read = 0;
size_t to_read = size;

if (data->type == SOCKET_STREAM)
{
	while (true)
	{
		while (to_read > 0 && (ret = ::recv(data->fd, (char *)ptr, (int)to_read, flags)) > 0)
		{
			ptr = (char *)ptr + ret;
			to_read -= ret;
			read += ret;
		}
#ifdef _WIN32
		if (ret == -1)
		{
			int error = WSAGetLastError();
			if (data->nonblock == 0 && error == WSAEWOULDBLOCK)
				continue;
		}
#endif
		break;
	}
}
// else ...
	
return read;

 

Best regards,
Alexander

Link to comment

Hi 

Thank you alexander, but the ideal solution is remove from this equation the standard function ::recv and use the unigine::Socket class at all for deal  with cross platform.

Thank you for your comment about nonblock()  you are right on that. I test and work fine that I can use this approach.

void process()
		{
			socket = Socket::create(Socket::SOCKET_STREAM, Port);
			socket->bind();
			socket->listen(10);
			socket->nonblock();

			Ptr<Socket> asock = Socket::create(Socket::SOCKET_STREAM);

			while (socket->getFD() != INVALID_SOCKET)
			{
				if (socket->accept(asock))
				{
					asock->nonblock();
					asock->puts("I am the server");
					char tmp[100];
					int len = 0;
					while (len == 0)
					{
						len = asock->read(tmp, 5);  // => I split just for test if I can read complete the buffer send by the client
						if (len != 0)
						{
							len = asock->read(&tmp[5], sizeof(tmp)-5);
							tmp[len + 5] = 0;
						}
						else
						{
							usleep(100);
						}
					}
					Assert::AreEqual(tmp, "Hi server");
					asock->puts("Bye");
					usleep(100);
					asock->close();
				}

				usleep(100);
			}
		}

Best regards 

/Roberto

Edited by roberto.alonso
Is working the nonblock() solution
Link to comment
  • morbid changed the title to [SOLVED] The Socket is blocking

Hi alexander

Thank you for the Socket.Read code source. From that I can deduct that you are doing the same as you pass the flag MSG_WAITALL to the receive function when the socket isn't Nonblock()

from:
  https://docs.microsoft.com/en-us/windows/desktop/api/winsock/nf-winsock-recv
  MSG_WAITALL	The receive request will complete only when one of the following events occurs:
     The buffer supplied by the caller is completely full.
     The connection has been closed.
     The request has been canceled or an error occurred.

On windows implementation when a client or server send a buffer, this buffer is receive as a full block, that mean:

  Client send Buffer1 with 10 bytes and then send Buffer2 with another 10 bytes  ==> server will receive buffer1 10 bytes (it don't matters you say that your buffer is 10MB, and in the next call you do to the ::recv will be receive the next 10 bytes.  That is equivalent if you send two buffer more bigger like 10MB separate and you read by 3MB buffer you will read 3MB+3MB+3MB+1MB then buffer2 will be 3MB+3MB+3MB+1MB the concept block never is loss.

On your implementation of the socket.read you loss that block concept the same way MSG_WAIT do => "The buffer supplied by the caller is completely full".

Best regards 

/Roberto

Link to comment
×
×
  • Create New...