roberto.alonso Posted June 15, 2019 Share Posted June 15, 2019 (edited) 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 June 15, 2019 by roberto.alonso Link to comment
karpych11 Posted June 17, 2019 Share Posted June 17, 2019 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
roberto.voxelfarm Posted June 17, 2019 Share Posted June 17, 2019 (edited) 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 June 17, 2019 by roberto.voxelfarm Link to comment
roberto.alonso Posted June 17, 2019 Author Share Posted June 17, 2019 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
roberto.alonso Posted June 17, 2019 Author Share Posted June 17, 2019 (edited) 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 June 17, 2019 by roberto.alonso Link to comment
alexander Posted June 18, 2019 Share Posted June 18, 2019 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
roberto.alonso Posted June 18, 2019 Author Share Posted June 18, 2019 (edited) 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 June 18, 2019 by roberto.alonso Is working the nonblock() solution Link to comment
roberto.alonso Posted June 18, 2019 Author Share Posted June 18, 2019 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
Recommended Posts