Click to See Complete Forum and Search --> : socket problem


cabasm
April 8th, 2003, 07:07 AM
I wrote some TCP\IP clients that runs in an UNIX environment and a TCP\IP server that runs on Windows. When multiple calls (10- 15 calls simultaneous) cames on the TCP\IP server, the clients got an error message "socket connection refused ...". Is there a limitation on the number of connections open on the Unix side? Can somebody help me?

Horst
April 8th, 2003, 07:19 AM
You used winsock for your server? What implementation did you use? The unix like approach? Or the event handled one?

Don't hide information if you want help.

But frankly I think either your server did not repond or your client didn't connect correctly.

And no at 15 sockets (even simultaneous) the number of sockets should be sufficient (UNIX or Windows). The socket specification has limits to the number of sockets but those are far beyond 15 connections.

Horst

cabasm
April 8th, 2003, 08:01 AM
I'm using winsockets with WSAAsyncSelect(). The server works fine with 4-7 connections made at the same time but if I increase the number of connections, it will return the famous error. Belowis the window procedure I have used:

long WINAPI WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
sockaddr_in addr;
WSADATA ws;

static SOCKET s = INVALID_SOCKET;

switch(msg)
{
case WM_CREATE:
WSAStartup(MAKEWORD(2, 2), &ws);

s = socket(AF_INET, SOCK_STREAM, 0);

addr.sin_family = AF_INET;
addr.sin_port = htons(8001);
addr.sin_addr.s_addr = INADDR_ANY;
bind(s, (sockaddr*)&addr, sizeof(addr));

WSAAsyncSelect(s, hwnd, WM_USER+1, FD_ACCEPT | FD_READ);

if(listen(s, 128/*SOMAXCONN*/) == SOCKET_ERROR)
closesocket(s);

break;

case WM_USER+1:
if(LOWORD(lParam) == FD_ACCEPT)
{
int len = sizeof(addrs);
if(accept(s, (sockaddr*)&addrs, &len) == INVALID_SOCKET)
MessageBox(NULL, "Accept request failed", "Socket Error", MB_ICONERROR);
}
else if(LOWORD(lParam) == FD_READ)
{
char buf[96] = { 0 };
if(recv(wParam, buf, 96, 0) > 0)
{
char *str = "some data";
send(wParam, str, lstrlen(str)+1, 0);
}
shutdown(wParam, SD_BOTH);
closesocket(wParam);
}
break;
}

return DefWindowProc(hwnd, msg, wParam, lParam);
}

Mick
April 8th, 2003, 08:14 AM
What is the backlog set on your listen()

How many connections are open at the time, netstat -n...netstat -a

Under Windoze connection refused will be returned if there is no more sockets (note it's an erroneous error) available. The default is 5000 (total system wide) and can be tweaked by the registery setting of MaxUserPorts.

cabasm
April 8th, 2003, 09:46 AM
Originally posted by Mick_2002
What is the backlog set on your listen()


as you can see from the code from the previous message, backlog is set to 128.

Horst
April 8th, 2003, 10:03 AM
Perhaps it's just your server that gets confused.

I saw that you set only a WM_USER + 1 to identify your server socket messages. I personally prefer to set a new message id for every new worker socket and to use the value (MessageId - WM_USER) to get the ID of my connection in the array I use to administer the connections. That means however that every worker needs his own WSAAsyncSelect command to be used on the new worker on accept.

How do you administer your clients?

Works perfectly well for me.

Hope that helped.

Horst

cabasm
April 8th, 2003, 10:53 AM
i have only one client application but i start more instances of it. the client looks like below:

int main()
{
int s = socket(AF_INET, SOCK_STREAM, 0);

sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(8001);
addr.sin_addr.s_addr = inet_addr("some_ip");

if(connect(s, (sockaddr*)&addr, sizeof(addr)) < 0)
{
close(s);
return -1;
}

char *pszData = "some data", pszRecv[256];
if(send(s, pszData, strlen(pszData)+1, 0) > 0)
{
recv(s, pszRecv, 256, 0);
// other operations
}

return 0;
}

Horst
April 9th, 2003, 01:37 AM
Ok, what I wanted to know is:
How do you keep information about the clients connected to your server?

Your problem seem to me coming from the fact that you cannot identify your clients correctly.

Perhaps you made the same mistake as a colleage of mine. He didn't realize, that at accept every client connection on the server got its own (worker) socket and that only via this (worker) socket the communication to the client takes place. So for the sake of my own understanding I myself give those client (worker) sockets an own event via WSAAsyncSelect. I think you do not do something like that and thus your server gets confused with the connections, which seemingly leads to missing responds at startup when more clients are connected.
It's absolutely not interesting how many clients are started from one machine. I did some performance tests lately where I started more than a hundred thread that had their own socket connection, with my own network layer. And that worked perfectly well.

In because of this I have to ask:
How do you store information about your clients?
Do you do it at all?

Another question:
To which address do you bind your, client? Witout bind the connect of a streaming socket cannot work properly.
Normally the port address of a client is given from the system by using port 0 at bind (This grants that every client socket has a unique and valid port).
That might be a reason why your code fails, as well.

Hope that helped!

Horst

cabasm
April 9th, 2003, 02:16 AM
As client do I have to bind to a socket? I thought it is enough to create the socket and to connect to it before sending and receiving data. Look at the client and server code I have posted earlier. I done some corrections in on the server side but the behavior is the same. I'm using the port 8001 (a free port on my machine). I'm using a single socket variable declared as static in the window procedure (see a previous message with source code posted). If you have a suggestion, please don't hesitate.

Horst
April 9th, 2003, 02:48 AM
First of all I don't know about UNIX (Never programmed in that area). But msdn says:

The bind function is used on an unconnected socket before subsequent calls to the connect or listen functions. It is used to bind to either connection-oriented (stream) or connectionless (datagram) sockets. When a socket is created with a call to the socket function, it exists in a name space (address family), but it has no name assigned to it. Use the bind function to establish the local association of the socket by assigning a local name to an unnamed socket.

Name in that context is the address/port combination used to identify a socket.

So yes if using streaming sockets you have to use bind on a socket even if it's only a client (at leat that with winsocks).

And as the ports should not always be created double with streaming sockets (That causes lots of problems in windows and probably even in UNIX) you have usually a fixed port for server sockets (as they seldom get shut down) that serve as sommunications hub. These normally only handle connect events. In the accept event a new worker socket is opened (you don't have to create that it's handle is given to you at accept). You use the socket in your server code to test for invalid accept's. You should store it for use in further communication with your client.
The worker sockets handle ALL communication with the clients, not the server socket.
The workers (by the way) have a unique port number given by the system.

Client socket are bound with port number 0 (that tell the system with winsocks that this is a port number to be set by the system).
After connect you are connected to the servers worker socket.

The problem is: If you do not bind a socket the bets are off what the system does. (As I said I don't know what UNIX does in that case). Windows normally sends an error when trying to connect an unbound streaming socket.

The process works in the rough like this:

SERVER:
socket call
bind to loacl address and fixed port
listen
accept -> here you get client socket
first recv
first send
... .
further commnication
...
closesocket of worker socket

CLIENT:
socket call
bind to local address and variable port
connect
send
recv
...
further communication
...
close socket

Hope that helped!

Horst

cabasm
April 9th, 2003, 04:50 AM
OK, and what is wrong on the TCP\IP server code I have posted earlier? I have also modified the client to bind to the socket but the behaviour is the same: for less connections it works fine but for more than 10-15 connections it's not working anymore because of the server.

Horst
April 9th, 2003, 06:10 AM
First of all I would propose to check for error's during network operations. That can be done with a statement like this:

if (WSAGETSELECTERROR (lParam) != 0)


in the server event. The macro gives you an error number of an error that might have occured on the network. It gives back the HIWORD of wParam.

Next: You could register for the FD_CLOSE event in some cases you can get information about closed sockets (It didn't work all times with me.)

The other thing is: Why do make a recv in FD_ACCEPT?
If the client doesn't send data this might block the application.
The server does only need to register FD_ACCEPT anyway. He won't ever get a FD_READ in because of the workers.

Just use recv if you got a FD_READ event. (Hey that's the advantage of messaging). But as I said before to do that you would need to identify your socket connection. I use an array with a structure that holds some important data for open connections, most importantly the socket handle. Then I use the array index to calculate a new event id (e. g. BASEEVENT + index).

Then I start WSAAsyncSelect for the new worker socket for the events FD_READ and FD_CLOSE.

Thereby I can analyse by the event id which socket created new events.

As I never know how many socket might be opened I use the default statement of the message handlers switch and check for valid sockets in my array.

This looks like this:


ArrayPos = (Msg - WS_SOCKETBASE);
if (Msg >= WS_SOCKETBASE && Msg < (WS_SOCKETBASE + MAXCLIENTS) )


Ok I use an array thereby I have to check if it's filled up already. But this is the easiest way and i know somewhat how many clients I have to expect in my apps, maybe you have a better idea.

Hopefully I didn't cofuse you now.

Horst

cabasm
April 9th, 2003, 06:26 AM
I'm a bit confused. Can you post a small TCP\IP server code sample?

Horst
April 9th, 2003, 06:45 AM
Ok here some cooked down versions of my code:

Creating of the server socket:

pTcpSocket = GetFreeSlot(&Number); // Check out the array for a free slot
if (pTcpSocket == NULL)
{
return 0;
}
// Here i initialize the element of the array
ZeroMemory (pTcpSocket, sizeof(WS_SOCKET) );
...
pTcpSocket->LocalAddress.sin_addr = Addresses[ConnArray[ConnType].AddressesPos];
pTcpSocket->LocalAddress.sin_port = htons (*ServerPort);
pTcpSocket->LocalAddress.sin_family = AF_INET;
...
// start datagram socket
pTcpSocket->Socket = socket(AF_INET, SOCK_STREAM, 0);
// error occured at startup of socket
if (pTcpSocket->Socket == INVALID_SOCKET)
{
err = WSAGetLastError();
ZeroMemory (pTcpSocket, sizeof (WS_SOCKET) );
return 0;
}
// Set Socket to be reuseable
reuse = TRUE;
err = setsockopt (pTcpSocket->Socket, SOL_SOCKET, SO_REUSEADDR, (void*) &reuse, sizeof(BOOL));
if (err == SOCKET_ERROR)
{
closesocket(pTcpSocket->Socket);
ZeroMemory (pTcpSocket, sizeof (WS_SOCKET) );
return 0;
}
// set socket into nonblocking mode. This is also done by WSAAsyncSelect I'm just overcareful
Blocking = NONBLOCKING;
err = ioctlsocket(pTcpSocket->Socket, FIONBIO, &Blocking);
// error occured while setting into nonblocking mode
if (err == SOCKET_ERROR)
{
closesocket(pTcpSocket->Socket);
ZeroMemory (pTcpSocket, sizeof (WS_SOCKET) );
return 0;
}
// bind socket to Address and port
err = bind(pTcpSocket->Socket,(struct sockaddr *) &pTcpSocket->LocalAddress,sizeof(struct sockaddr));
if (err == SOCKET_ERROR)
{
closesocket(pTcpSocket->Socket);
ZeroMemory (pTcpSocket, sizeof (WS_SOCKET) );
pTcpSocket->Socket = INVALID_SOCKET;
return 0;
}
// start listening
err = listen(pTcpSocket->Socket, SOMAXCONN);
if (err == SOCKET_ERROR)
{
closesocket(pTcpSocket->Socket);
ZeroMemory (pTcpSocket, sizeof (WS_SOCKET) );
pTcpSocket->Socket = INVALID_SOCKET;
return 0;
}
// Start Messaging
err = WSAAsyncSelect(pTcpSocket->Socket, hIWnd, WS_SOCKETBASE + Number, FD_ACCEPT);
// error occured in initialization of message sending
if (err == SOCKET_ERROR)
{
closesocket(pTcpSocket->Socket);
ZeroMemory (pTcpSocket, sizeof (WS_SOCKET) );
pTcpSocket->Socket = INVALID_SOCKET;
return 0;
}
LeaveCriticalSectionIpEx (&TcpStopSection,kroz);
#ifdef IP_TEST
length = sprintf (Text, "Leave %d at StartTcpServer \r\n", TcpStopSection);
WriteFile (FileHandle, Text, length, &written, NULL);
FlushFileBuffers (FileHandle);
#endif
return (unsigned int)pTcpSocket;
}


The Event Handler:
[code]
switch (Msg)
{
case WM_COMMAND:
....
default:
ArrayPos = (Msg - WS_SOCKETBASE);
if (Msg >= WS_SOCKETBASE && Msg < (WS_SOCKETBASE + MAXCLIENTS) )
{

WriteIPnetProtokoll(" \r\n");
SocketEvent (wParam, lParam, &SocketArray[ArrayPos], ArrayPos);
return 0;
}

}

unsigned int SocketEvent(WPARAM wParam, LPARAM lParam, LP_WS_SOCKET EventSocket, unsigned int ArrayPos)
{

// Check for error of event
if (WSAGETSELECTERROR (lParam) != 0)
{
SendMessage(hIWnd, WS_EVENT_ERRORS, (unsigned int)&EventError, 0);
return 0;
}
// distribute messages to events
switch (WSAGETSELECTEVENT(lParam))
{
case FD_READ:
FD_READ:

Horst
April 9th, 2003, 06:55 AM
Ok here some cooked down versions of my code:

Creating of the server socket:

pTcpSocket = GetFreeSlot(&Number); // Check out the array for a free slot
if (pTcpSocket == NULL)
{
return 0;
}
// Here i initialize the element of the array
ZeroMemory (pTcpSocket, sizeof(WS_SOCKET) );
...
pTcpSocket->LocalAddress.sin_addr = Addresses[ConnArray[ConnType].AddressesPos];
pTcpSocket->LocalAddress.sin_port = htons (*ServerPort);
pTcpSocket->LocalAddress.sin_family = AF_INET;
...
// start datagram socket
pTcpSocket->Socket = socket(AF_INET, SOCK_STREAM, 0);
// error occured at startup of socket
if (pTcpSocket->Socket == INVALID_SOCKET)
{
err = WSAGetLastError();
ZeroMemory (pTcpSocket, sizeof (WS_SOCKET) );
return 0;
}
// Set Socket to be reuseable
reuse = TRUE;
err = setsockopt (pTcpSocket->Socket, SOL_SOCKET, SO_REUSEADDR, (void*) &reuse, sizeof(BOOL));
if (err == SOCKET_ERROR)
{
closesocket(pTcpSocket->Socket);
ZeroMemory (pTcpSocket, sizeof (WS_SOCKET) );
return 0;
}
// set socket into nonblocking mode. This is also done by WSAAsyncSelect I'm just overcareful
Blocking = NONBLOCKING;
err = ioctlsocket(pTcpSocket->Socket, FIONBIO, &Blocking);
// error occured while setting into nonblocking mode
if (err == SOCKET_ERROR)
{
closesocket(pTcpSocket->Socket);
ZeroMemory (pTcpSocket, sizeof (WS_SOCKET) );
return 0;
}
// bind socket to Address and port
err = bind(pTcpSocket->Socket,(struct sockaddr *) &pTcpSocket->LocalAddress,sizeof(struct sockaddr));
if (err == SOCKET_ERROR)
{
closesocket(pTcpSocket->Socket);
ZeroMemory (pTcpSocket, sizeof (WS_SOCKET) );
pTcpSocket->Socket = INVALID_SOCKET;
return 0;
}
// start listening
err = listen(pTcpSocket->Socket, SOMAXCONN);
if (err == SOCKET_ERROR)
{
closesocket(pTcpSocket->Socket);
ZeroMemory (pTcpSocket, sizeof (WS_SOCKET) );
pTcpSocket->Socket = INVALID_SOCKET;
return 0;
}
// Start Messaging
err = WSAAsyncSelect(pTcpSocket->Socket, hIWnd, WS_SOCKETBASE + Number, FD_ACCEPT);
// error occured in initialization of message sending
if (err == SOCKET_ERROR)
{
closesocket(pTcpSocket->Socket);
ZeroMemory (pTcpSocket, sizeof (WS_SOCKET) );
pTcpSocket->Socket = INVALID_SOCKET;
return 0;
}
LeaveCriticalSectionIpEx (&TcpStopSection,kroz);
#ifdef IP_TEST
length = sprintf (Text, "Leave %d at StartTcpServer \r\n", TcpStopSection);
WriteFile (FileHandle, Text, length, &written, NULL);
FlushFileBuffers (FileHandle);
#endif
return (unsigned int)pTcpSocket;
}


The Event Handler:

switch (Msg)
{
case WM_COMMAND:
....
default:
ArrayPos = (Msg - WS_SOCKETBASE);
if (Msg >= WS_SOCKETBASE && Msg < (WS_SOCKETBASE + MAXCLIENTS) )
{

WriteIPnetProtokoll(" \r\n");
SocketEvent (wParam, lParam, &SocketArray[ArrayPos], ArrayPos);
return 0;
}

}

unsigned int SocketEvent(WPARAM wParam, LPARAM lParam, LP_WS_SOCKET EventSocket, unsigned int ArrayPos)
{

// Check for error of event
if (WSAGETSELECTERROR (lParam) != 0)
{
SendMessage(hIWnd, WS_EVENT_ERRORS, (unsigned int)&EventError, 0);
return 0;
}
// distribute messages to events
switch (WSAGETSELECTEVENT(lParam))
{
case FD_READ:
//READ handling
case FD_CLOSE:
//CLOSE handling
case FD_ACCEPT:
// accept socket
i = sizeof (struct sockaddr);
Socket = accept(EventSocket->Socket, (struct sockaddr *) &ExtAddress, &i );
if (Socket == INVALID_SOCKET)
{
if (EventSocket->Connection != 0)
{
SendMessageEx(EventSocket->Connection, WS_ACCEPT_FAILED, (unsigned int)EventSocket,0);
}
return 0;
}
// Start Worker
WorkerSocket = StartTcpWorker(Socket, (struct sockaddr *) &ExtAddress, lParam, EventSocket->Connection);
return 0;



And at last the startup code of the worker:


unsigned int StartTcpWorker(SOCKET Socket, struct sockaddr* ExternalAddress, unsigned int ParentPos, DWORD ConnType)
{
// Allocation of Storage
pTcpWorkerSocket = GetFreeSlot (&Number);
if (pTcpWorkerSocket == NULL)
{
return 0;
}
ZeroMemory (pTcpWorkerSocket, sizeof(WS_SOCKET) );
// save position in socket array
pTcpWorkerSocket->Number = Number;
pTransceiver->ExternalAddress.sin_addr.S_un.S_addr = pExternalAddress->sin_addr.S_un.S_addr;
pTransceiver->ExternalAddress.sin_family = pExternalAddress->sin_family;
pTransceiver->ExternalAddress.sin_port = pExternalAddress->sin_port;
// Start messaging
WSAAsyncSelect(Socket, hIWnd, WS_SOCKETBASE + Number, FD_READ|FD_CLOSE);
return (unsigned int)pTcpWorkerSocket;
}


Ok, that's it. I hope you understand this code, for I cut a lot out that's just specific for my app.

Horst

P.S. The previous post was accidental

cabasm
April 9th, 2003, 08:20 AM
Thanks a lot ;)