Click here to Skip to main content
15,886,095 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more:
Hi,

I am experimenting with writing a small TCP Server.
This is the very first step of a very long journey.

I understand fully that a fully fledged comercial quality server will need to be multithreaded in various ways. However, this is about getting a basic comms chanel between a single server, and up to two clients, all runing on the One Computer! One could call it an 'embrioic Server' All it is expected to do is, echo the text sent by a client, back to the client, until the client sends the text '<stop>'
I have the following Code:-
C++
int WINAPI AcceptCondition(WSABUF* pk_CallerId, WSABUF* pk_CallerData, QOS* pk_SQOS, QOS* pk_GQOS, 
										 WSABUF* pk_CalleeId, WSABUF* pk_CalleeData, UINT* pu32_Group, DWORD p_Param)
{
	return CF_ACCEPT;
}

void CSimpleSocketDlg::Run()
{
	WSADATA wsData;
	sockaddr_in SA_IN;
	DWORD u32_Error = WSAStartup(MAKEWORD(2,0), &wsData);
	if (u32_Error)
		return;

    SOCKET hSock=socket(AF_INET, SOCK_STREAM,0);

    SA_IN.sin_family=AF_INET;
    SA_IN.sin_port = htons(2000);
    SA_IN.sin_addr.s_addr =INADDR_ANY;

    int err=bind(hSock,(struct sockaddr *) &SA_IN,sizeof(SA_IN));
	if(err!=0)return;

	// Here we start the Endless Loop
	for(;;){
		listen(hSock,100);
		struct sockaddr ClientIP;
		SOCKET h_sockClient=WSAAccept ( hSock, &ClientIP, NULL,AcceptCondition,(DWORD)this);
		if(h_sockClient==INVALID_SOCKET){
			err=WSAGetLastError();
			return;
		}
		char Buf[4096]={0};
		recv(h_sockClient,Buf,4096,0);

		//Process the Received Data HERE
		CString Result;
		BOOL bShouldTerminate=ProcessData(Buf,Result);

		//Send the Data,even a Command from a Client to Stop the Server
		if(send(h_sockClient,Result,Result.GetLength(),0)==SOCKET_ERROR ){
			err=WSAGetLastError();
			closesocket(h_sockClient);
			return;
		}
		//closesocket(h_sockClient);
		if(bShouldTerminate)return;
	}


}

//This is the Place Holder for the Processing Function.
//
BOOL CSimpleSocketDlg::ProcessData(LPCSTR Input, CString& Result)
{
	if(stricmp(Input,"<Stop>")==0){
		Result="Terminating Server...";
		return TRUE;
	}
	Result="<Echo:...>";
	Result+=Input;
	return FALSE;
}

The Problem is that this works fine for One Client Request and response. After that 'hSock' is 'DEAF', It is also not 'DEAD', the client complains that the server disconnected. if I close 'hSockClient' at the bottom of the loop. What should I do to restore the listening capability of the Master socket, when the Client Socket routine comes around the loop. I am new to the Subject, and suspect I am leaving out some magical WSA incantation Somewhere.

Regards, :)
Posted
Updated 12-Apr-12 14:30pm
v2
Comments
Bram van Kampen 12-Apr-12 15:37pm    
Well, One thing at a time. I'm at experimental stage 0, and the first concern is what type of incantations are required to ensure that after the 'send(h_sockClient...)' , and going round the loop, 'listen(hSock,100)' works properly again. I'm debuging this with one client, and efficiency does not (yet) come in to it. I would have thought that for this testing purpose, one thread should suffice.

:)
Bram.
Bram van Kampen 12-Apr-12 20:41pm    
QQQ

How can a Single threaded app endup creating an unlimited number of sockets! I do not understand this at all! Surely a single threaded function can count the number of created sockets in a single global variable. (Does not need protection, because there is only one thread.) The CallBack of WSAAccept can reject the new socket, if too many are open.
At any rate, this is a latter day issue. For now I just want to establish basic connectivity.

Regards,

Bram.

It was Simple afterall!

It needed an extra for(;;) loop inside, and a slight modification of the Logic!
the Client Socket produced by WSAAccept cannot be re-cycled, or re-generated! The User processing also needs three in stead of two Result States:-
C++
enum{
	CONTINUE_RUNNING=0,
	END_CONVERSATION=1,
	TERMINATE_SERVER=2,
};


The Dummy User Action is re_written as Follows:-

C++
BOOL CSimpleSocketDlg::ProcessData(LPCSTR Input, CString& Result)
{
	int RetVal=CONTINUE_RUNNING;
	if(stricmp(Input,"<term>")==0){
		Result="Terminating Server...";
		RetVal= TERMINATE_SERVER;
	}
	if(stricmp(Input,"<end>")==0){
		Result="Ending Conversation...";
		RetVal= END_CONVERSATION;
	}
	else{
		Result="<echo:... xmlns:echo="#unknown">";
		Result+=Input;
	}
	return RetVal;
}
</echo:...></end></term>


The Main body ends up as follows:-
C++
void CSimpleSocketDlg::Run()
{
	WSADATA wsData;
	sockaddr_in SA_IN;
	DWORD u32_Error = WSAStartup(MAKEWORD(2,0), &wsData);
	if (u32_Error)
		return;

    SOCKET hSock=socket(AF_INET, SOCK_STREAM,0);

    SA_IN.sin_family=AF_INET;
    SA_IN.sin_port = htons(2000);
    SA_IN.sin_addr.s_addr =INADDR_ANY;

    int err=bind(hSock,(struct sockaddr *) &SA_IN,sizeof(SA_IN));
	if(err!=0)return;

	// Here we start the Endless Loop
	for(;;){
		listen(hSock,100);
		struct sockaddr ClientIP;
		SOCKET h_sockClient=WSAAccept ( hSock, &ClientIP, 
			NULL,AcceptCondition,(DWORD)this);
		if(h_sockClient==INVALID_SOCKET){
			err=WSAGetLastError();
			return;
		}
		// Converse with the Client, untill the Client
		// types "<end>"
		// The Client must also be able to Close Down the Server
		// To do this, type "<term>"
		for(;;){
			char Buf[4096]={0};
			recv(h_sockClient,Buf,4096,0);
			err=WSAGetLastError();
			if(err==WSAECONNABORTED){
				closesocket(h_sockClient);//?? Needed??
				break;
			}

			//Process the Received Data HERE
			CString Result;
			int NextAction=ProcessData(Buf,Result);

			//Send the Data,even a Command from a Client to Stop the Server
			if(send(h_sockClient,Result,Result.GetLength(),0)==SOCKET_ERROR ){
				err=WSAGetLastError();
				closesocket(h_sockClient);
				break;
			}
			if(NextAction==CONTINUE_RUNNING)continue;

			else if(NextAction==END_CONVERSATION){
				closesocket(h_sockClient);
				break;
			}
			else if(NextAction==TERMINATE_SERVER){
				closesocket(h_sockClient);
				return;
			}
		}
	}
}
</term></end>

The above works extra fine in establishing connectivity and proving basic principles!

Notes.:
Single vs Multiple threads.

Any but the most basic Servers will need multiple threads. Otherwise, the server is blocked for the duration of a connection. However, for the purpose of debugging both Client and Server code, one would typically have One Client App, and One Server App,bouncing of eachother, on your own machine. Multiple threads have no advantage there, So I include a Single Threaded option, purely for debugging.

Thanks to all that took out the time to read my question, and to 'SA'who took the time to write a response.

:)
 
Share this answer
 
One thread per connection is certainly bad; it can generate uncontrolled number of sockets. On the server side, as a minimum, you need one thread for listening and one for reading/writing from/to network stream.

My past answer (even though it was originally written in response to a .NET question) can give you some ideas:
Multple clients from same port Number[^].

—SA
 
Share this answer
 
Comments
Bram van Kampen 11-May-12 19:28pm    
Well I have One thread per Connection, but use WSAAccept to limit it to a reasonable number. I also have (on a Seperate thread) a Garbage Collection Service, that kills stale sockets. Any Sockets, not Kicked within a period, get Closed, the thread running it also gets closed.
The Socket (or Thread) gets kicked when traffic passes tru it, and a new period starts.

Each WSAAccept(...) is not sent off into the dark on a New thread, and a New Socket. The situation is carefully monitored, and the Callback in WSAAccept() will return 'CF_REJECT' if Too Many Sockets are open already.
Sergey Alexandrovich Kryukov 11-May-12 20:13pm    
OK, but this situation might limit number of connections, if I understood your correctly. You should also understand that, from certain moment, adding threads does not improve performance (let's say, per client), but degrades it. Sharing a thread between several connections is much more reasonable.
--SA

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900