Recv function would keep blocking when TCP server shutdown

I use a blocking FSocket in client-side that connected to tcp server, if there’s no message from server, socket thread would block in function FScoket::Recv(), if TCP server shutdown, socket thread is still blocking in this function. but when use blocking socket of BSD Socket API, thread would pass from recv function and return errno when TCP server shutdown, so is it the defect of FSocket?

What platform are you on? Also, how are you constructing your FSocket object?

Either way, try querying the socket state using


HasPendingData 

before calling


Recv

.

I’m on windows. this’s code that constructed FSocket:


void HTcpClient::Init()
{
	if (ClientSock)
	{
		ClientSock->Close();
		ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM)->DestroySocket(ClientSock);
	}
	ClientSock = ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM)->CreateSocket(NAME_Stream, TEXT("default"), false);
	ClientSock->SetNonBlocking(false);
}

bool HTcpClient::Connect(const char* IP, int Port, PushMsg_Fun fun)
{
	if (Connected_)
	{
		return true;
	}
	FString address(IP);
	int32 port = 3001;

	FIPv4Address ip;
	FIPv4Address::Parse(address, ip);

	TSharedRef<FInternetAddr> addr = ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM)->CreateInternetAddr();
	addr->SetIp(ip.Value);
	addr->SetPort(Port);

	RecvThread.Start(ClientSock, fun, this);

	Connected_ = ClientSock->Connect(*addr);
	return Connected_;
}

if use non-blocking mode, should use HasPendingData before Recv, but when use blocking mode, HasPendingData would not works correctly, it always return false even romote server sent msg to client.

So there are few questions about the code in Connect that I need to ask first which may be cause for issues in your code.

First off, you are passing into the function an iny called “Port”, however, you also instantiated a function member called “port” of type int32. “port” is assigned 3001, but “Port” has a variable that you pass into the function. When you construct the “addr” object, your call to SetPort is using “Port”. Is this intentional? You never use “port” which is set to 3001. I would remove any unused code to remove confusion for yourself and any future developers.

Do you have the code for the RecvThread function? And is Connect returning true for you? Are you able to debug your server code to verify a connection is being established? The reason I ask those last two question is you spawn the thread before actually connecting, so you will be trying to read from the socket whether you successfully connected or not.

So I would clean it up to be like this in the Connect function:



bool HTcpClient::Connect(const char* IP, int Port, PushMsg_Fun fun)
{
	if (Connected_)
	{
		return true;
	}
	FString address(IP);

	FIPv4Address ip;
	FIPv4Address::Parse(address, ip);

	TSharedRef<FInternetAddr> addr = ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM)->CreateInternetAddr();
	addr->SetIp(ip.Value);
	addr->SetPort(Port);

	Connected_ = ClientSock->Connect(*addr);
        if(Connected_)
        {
	     RecvThread.Start(ClientSock, fun, this);
        }
	return Connected_;
}

you can look carefully, Port was used in logic.
these code work correctly for me, communication between server and client works very well in non-blocking mode.
my only question is that why blocking mode socket can’t sense when remote server shutdown, BSD socket API can sense that when server closed.

Yes, I understand “Port” was used. But the code was not clear in that that was the intention because you had two variables for the same thing. As far as the code, from what I can see in the source on github, this is what happens when in Blocking mode:


//would block is not an error

So, from that we can see is that the developers in the Engine code purposely state that a blocking error is not a true error in their implementation of the BSD socket wrappers. Which is why you do not receive an error when the socket is closed or there is no data to consume. As both of those are blocking methods and you are set to blocking, but would block errors are ignored, your code will think it is in a good state. So, this is defined behaviour in the Engine for the Connect method of the Socket implementation.

What does a call to GetLastErrorCode() from the SocketSubsystem return?
What does SetNonBlocking return? True or False?

Also, you could use the Wait method to wait for a good state to read data from the socket. I guess another question for you is, if non-blocking sockets work well for you, but you are trying to use blocking sockets, what is the specific purpose of using blocking sockets that non-blocking sockets can achieve for you?

thx all your help! answer your question:
1, I did a test with using GetLastErrorCode(), but this function return no error when server closed.
2, SetNonBlocking() return true when I used it.
3, Maybe I’m wrong, blocking or non-blocking mode isn’t main problem for me, I just want to get an error return when remote server close.
I will use BSD socket instead of FSocket, because I can’t found any way to achieve my purpose with FSocket. thx very much again!