0

I was writing a threaded tcp server and within this thread i wrote this:

private void HandleClientComm(object client)
    {
        TcpClient tcpClient = (TcpClient)client;
        NetworkStream clientStream = tcpClient.GetStream();
        byte[] message = new byte[4096];
        int bytesRead;
        byte[] buffer = null;
        string answer = null;

        while (true)
        {
            bytesRead = 0;

            try
            {
                bytesRead = clientStream.Read(message, 0, 4096);
            }
            catch(Exception ex)
            {   

                write("Error: " + ex.Message);

                break;
            }

            if (bytesRead == 0)
            {
                write("Client connection lost!");
                break;
            }

            txtLogger.Text += "Command accepted\n";
            ASCIIEncoding encoder = new ASCIIEncoding();

            clientreq = encoder.GetString(message, 0, bytesRead);
            clientreq = clientreq.ToUpper();

            if (clientreq == "CLIENTIP")
            {
                //THIS PART
                answer = ((IPEndPoint)tcpClient.Client.RemoteEndPoint).Address.ToString();
                buffer = encoder.GetBytes(answer);
            }
//some more if's
clientStream.Write(buffer, 0, buffer.Length);
            clientStream.Flush();
            write("Server finished work.");
            }
//some more code

Now i would like the THIS PART to be a method that will be called if the input is CLIENTIP, as requested from client. how i would be doind that. Thanks in advance :)

By the way, every client req should be handled with new tcp connection. I've tried this, with pretty bad result: the client freezes and NOTRespondin occured

public void IPKLIENTI()
    {
        TcpClient client = this.TCPMonitoruesi.AcceptTcpClient();
        TcpClient tcpClient = (TcpClient)client;
        NetworkStream clientStream = tcpClient.GetStream();
        byte[] Bufer = null;
        string answer = null;
        ASCIIEncoding encoder = new ASCIIEncoding();


        answer = ((IPEndPoint)tcpClient.Client.RemoteEndPoint).Address.ToString();
        Bufer = encoder.GetBytes(answer);

    }
neonsign
  • 3
  • 4
  • It isn't clear what you mean by "a method that will be called if the input is CLIENTIP, as requested from client" - do you mean you want to move the code in the if into a separate method which you call from inside the if statement? Please clarify and we will be better able to help you out. – VisualMelon Apr 29 '13 at 16:39
  • 2
    What if `bytesRead` is, for example, 2 and `message` contains `CL`? – I4V Apr 29 '13 at 16:49
  • So is the IPKLIENTI method the one being called? What it is doing at the moment is accepting a new TCP connection, and filling a local buffer with the appropriate response - you don't ever send this response to the requesting client because you don't appear to pass the values back to the caller function. Could you describe exactly the purpose of the CLIENTIP request, and how you expect the server to handle it (currently I don't follow the logic of accepting a new client in the IPKLIENTI method). – VisualMelon Apr 29 '13 at 16:50
  • @VisualMelon If any Stream reports zero in a read operation, it's a well established convention in .Net that this represents the end of a stream. If a NetworkStream has no bytes to read when (synchronous) Read is called, it necessarily blocks until it can supply at least a single byte (because zero bytes would represent the end of stream condition). The docs are just plain wrong. Try it. http://stackoverflow.com/a/6958290/14357 – spender Apr 29 '13 at 17:04
  • i want to get the client ip address through that method – neonsign Apr 29 '13 at 17:09
  • @spender Did not know that... and I'll have to try it out as you suggest. Thanks for the heads up. I've spent my life reading individual bytes when I do networking so I've never come across this behavior. (Former comment deleted to avoid confusion) – VisualMelon Apr 29 '13 at 17:10
  • @neonsign If you want the address of the Client that send the "CLIENTIP" command then you need to [pass](http://msdn.microsoft.com/en-us/library/8f1hz171.aspx) that `TcpClient` object to the IPKLIENTI method - currently you are accepting a new client, so it won't be the same one (if it actually finds one to accept). You are also filling a separate buffer to the one you eventually send to the client, so you need to find a way to return the generated response too. If you want some example code in a more substantial answer (assuming I'm not completely missing the point) do say. – VisualMelon Apr 29 '13 at 17:13
  • @VisualMelon: indeed. If you look at the base class docs for [`Read`](http://msdn.microsoft.com/en-us/library/system.io.stream.read.aspx), the explanation is clear : "The implementation will block until at least one byte of data can be read, in the event that no data is available. Read returns 0 only when there is no more data in the stream and no more is expected (such as a closed socket or end of file). " – spender Apr 29 '13 at 17:14
  • @spender Thanks for the info and link, I can imagine myself getting very confused by that later down the line – VisualMelon Apr 29 '13 at 17:20
  • @VisualMelon if its not much for you :D, please show me some tips doing that – neonsign Apr 29 '13 at 17:21

1 Answers1

0

To break up your if statement to handle the "CLIENTIP" request, you need to pass the TcpClient you are connected to to the method which does all the work (the IPKLIENTI method) as below.

if (clientreq == "CLIENTIP")
{
    // call to IPCLIENTI retrives the RemoteEndPoint of the given TcpClient and encodes a response to be sent
    buffer = IPKLIENTI(client, encoder);
}

A revised IPKLIENTI implementation is shown below. The key changes are:

  • Takes a TcpClient as a paramater rather than accepting a new TcpClient (this behavior lets you work with the TcpClient you recieved the request from and will later respond to, the old behavior attempted to accept another client all together)

  • Takes an ASCIIEncoding object to do the encoding with - you could re-build it as you used to, but this is tidier and ensures consistency in the future

  • We don't need to access the NetStream in this function, so we can do away with the call to GetStream

  • We return an encoded byte array to use as the response to the client when we call clientStream.Write() later in the HandleClientComm function

Revised IPKLIENTI method:

// Method takes a TcpClient Object and Encoder, returning a response to the CLIENTIP request
public byte[] IPKLIENTI(TcpClient tcpClient, ASCIIEncoding encoder)
{
    string answer = ((IPEndPoint)tcpClient.Client.RemoteEndPoint).Address.ToString();
    byte[] buffer = encoder.GetBytes(answer);
    return buffer;
}

Essentially it just stores the intended response in a string (the RemoteEndPoint of the given TcpClient) and then encodes it via the given encoder, returning a byte array of the encoded response to be sent to the client.

VisualMelon
  • 662
  • 12
  • 23