-1

I have written a client in C# and a server in PHP both on localhost and should communicate data through a socket.

I use The following C# code to read from a PHP server:

string getResponse(string request)
{
    string response = "";

    byte[] bytes = new byte[10024];

    byte[] msg = Encoding.ASCII.GetBytes(request);
    // Send the data through the socket.
    int bytesSent = Sender.Send(msg);


    // Receive the response from the remote device.
    int bytesRec = Sender.Receive(bytes);
    response = Encoding.ASCII.GetString(bytes, 0, bytesRec);


    return response;
}

And Sender, defined and initiated as follows

Socket Sender = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
IPAddress ipAddress = IPAddress.Parse("127.0.0.1");               
IPEndPoint remoteEP = new IPEndPoint(ipAddress,8888);           
Sender.Connect(remoteEP);

The code works fine in the first call of getResponse, but in the second call it generates the following error on Sender.Receive call:

SocketException was unhandled An established connection was aborted by the software in your host machine

Sam
  • 7,252
  • 16
  • 46
  • 65
Sali Hoo
  • 743
  • 2
  • 8
  • 22

6 Answers6

2

I noticed that in PHP 5, we can use stream_sockets when I use stream_socket instead of socket_create, it works like a charm .... It's really strange, how does this work?! what's the difference?

<?php
    # server.php

    $server = stream_socket_server("tcp://127.0.0.1:1337", $errno, $errorMessage);

    if ($server === false) {
        throw new UnexpectedValueException("Could not bind to socket: $errorMessage");
    }
    for (;;) {
        $client = @stream_socket_accept($server);

    if ($client) {
        stream_copy_to_stream($client, $client);
        fclose($client);
    }
}
Sali Hoo
  • 743
  • 2
  • 8
  • 22
  • It won't block the c# or cause the error in c#, but it won't be doing what you need it to do at the PHP end. What happens is that you create the handle `$client` and then overwrite the `$client` variable. So your `fclose()` doesn't close the socket (as it will fail as it's trying to close something else) and the socket remains open. c# is happy, sure, but this code won't work. Change the second parameter `$client` to a different name in `stream_copy_to_stream` and you'll see you actually have exactly the same problem - i.e. you'll close the socket each loop which is what you don't want. – Robbie Aug 15 '14 at 05:31
  • What do you mean by changing the second parameter's name? What's the difference? I have tested the code on Ubuntu 12.0 and it works fine. – Sali Hoo Aug 15 '14 at 22:21
  • Sorry - I just checked the parameters for `stream_copy_to_stream`; my above assessment is wrong. You are closing the stream - so my comment above is wrong, but it still shouldn't work because you close the socket. But if it's good for you, great! Run with it - just be careful if pushing to other servers. – Robbie Aug 17 '14 at 22:29
0

Sometimes you have to do receive multiple times for single send.

Try receive with this:

_socket.Receive(Buffer, 0, Buffer.Length, SocketFlags.None);

while (_socket.Available != 0)
{
   _socket.Receive(Buffer, readByte, Buffer.Length - readByte, SocketFlags.None);

}
Sam
  • 7,252
  • 16
  • 46
  • 65
SanDeep
  • 44
  • 1
  • I tried this code, _socket.Available is always 0. I've also noticed that Sender.send does not work although there is no exception, nothing is being sent to proxy? :( – Sali Hoo Aug 05 '14 at 13:13
0

For your C# client, it would probably be better to use the System.Net.WebClient class rather than the lower level Socket class. For example, WebClient.DownloadString method does pretty much what your code is trying to do, and has been thoroughly tested.

Burt_Harris
  • 6,415
  • 2
  • 29
  • 64
0

From this question:

That is a boiler-plate error message, it comes out of Windows. The underlying error code is WSAECONNABORTED. Which really doesn't mean more than "connection was aborted". You have to be a bit careful about the "your host machine" part of the phrase. In the vast majority of Windows application programs, it is indeed the host that the desktop app is connected to that aborted the connection. Usually a server somewhere else.

The roles are reversed however when you implement your own server. Now you need to read the error message as "aborted by the application at the other of the wire". Which is of course not uncommon when you implement a server, client programs that use your server are not unlikely to abort a connection for whatever reason. It can mean that a fire-wall or a proxy terminated the connection but that's not very likely since they typically would not allow the connection to be established in the first place.

It seems to me that what is happening is that the client (C#) is trying to connect to the server (PHP). The client sends a request, and then receives a response from PHP; then, on the second attempt to send a request through the same socket, attempts to send/receive data again.

Remember that PHP is a language processor that decorates the HTTP protocol, such that it basically interprets PHP code and then returns plain HTTP through the HTTP layer (Apache?) to the requester.

I am not an expert on HTTP and far from knowledgable on PHP but I would assume that this is happening because PHP is receiving your data, sending your response and then closing the connection. This would make sense because generally we do not keep sockets open in HTTP - we just fire a request, get a response and that's it.

What you are looking at doing is something called long-polling, which is where the socket for the HTTP connection stays open for a longer duration so that the server can continue to send data to the client. I do not know a whole lot about this topic but my Google-Fu leads me to SignalR, which might point you in the right direction.

In the event you aren't looking for long polling (i.e, you aren't interested in creating a socket that stays open for a long time), all you need to do is simply re-connection your socket when you write to the server if it's not already open.

This might, of course, be totally wrong. But it seems to make sense based on the phenomena and what I know about HTTP.

TLDR: You're trying to write to a connection that is already closed because HTTP (and thus PHP) only expects a single request per single response. You need to either re-connect the connection to the server or you can alternatively implement long-polling.

Community
  • 1
  • 1
Dan
  • 10,282
  • 2
  • 37
  • 64
  • Thanks a lot for your detailed response Dan. – Sali Hoo Aug 14 '14 at 02:05
  • I'm not PHP expert too, but as you mentioned, in my PHP code I close socket after I send response. Thanks for your suggesting SignalR. It an ASP.NET library, I should find its counterpart in PHP. am I right? – Sali Hoo Aug 14 '14 at 02:11
  • Unfortunately I can't answer that as I don't know the answer to it. I don't use any PHP so I have no idea how to tell you how to implement long polling in it. I would just recommend re-creating the socket/re-connecting to the server. It will certainly be easier. Given your current code I can see that being a better solution - long polling is mainly used when you want to repeatedly communicate with an open connection via HTTP, basically – Dan Aug 14 '14 at 07:50
0

looks like you are iniating the socket and the connection only once. i am not too sure but i think when the web response ends it will close the connection. so it means that your second read attempt will fail. you can try putting your socket connection code in the get response method. so you are making connection every time you want a response.

Gurpreet
  • 1,382
  • 11
  • 24
0

The issue will be with your PHP code, not the c# you posted. For some reason, it is closing the connection or it thinks there is an error so is aborting.

You already say the PHP code is looping, so I'm guessing it's one of the three:

  1. you are renegotiating the header each loop; you only need to negotiate header each new connect; once connected you just read data
  2. you keep closing the socket
  3. you have the socket (in PHP) in blocking mode.

Your php code should be:

  1. Open the socket server, put in listen mode. (Should be in non-blocking as well. Handle that through socket_recv() - more later.)
  2. Loop infinitely
  3. Use command line, press Ctrl-C/Cmd-C to break.

Then inside the loop:

  1. Look for new socket connections (socket_accept()). If you get one, socket_read() the header, handshake once and remember the socket in an array.
  2. Then run through all your open sockets stored at step 1, and read (socket_recv()) into buffer.
    • if socket_recv() is "false" (use === to compare) then drop the socket, it's disconnected.
    • if socket has data, you will need to unmask it and process.
  3. Repeat

Tip for using "non-blocking" mode, when you send data, also send an "End Of Command" character. This helps PHP know it's got a full instruction and not part of one.

If PHP is the socket server, only close the sockets if socket_recv() === false or you're ending the program. Otherwise they stay active and loop.

Another pointer is to also make sure you re-use the IP adddress. socket_set_option($socket, SOL_SOCKET, SO_REUSEADDR, 1);

(Note: different rules apply if PHP is a socket client, this only applies as you're using PHP as a socket server.)

Robbie
  • 17,605
  • 4
  • 35
  • 72
  • while(1) { socket_listen($this->sock, 100); $client = socket_accept($this->sock); $this->rawRequest = socket_read($client, 2048); $respond = $this->respondToHttp(); socket_write($client, $respond, strlen($respond)); socket_close($client); } – Sali Hoo Aug 14 '14 at 17:09
  • OK, you've confirmed it - that's wrong. Look at what you've written versus what I've got above. You must KEEP the $client (socket) and not overwrite it with a socket_accept each loop. This is where your problem is as you essentially keep closing reference to the socket. Follow the structure I've described above to fix. – Robbie Aug 14 '14 at 22:13