views:

955

answers:

3

Hello,

I'm trying to learn how sockets works in C#. My idea was to program a simple http proxy: Here's my code:

using System;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;

class Program
{
    static void Main(string[] args)
    {
        ThreadPool.SetMaxThreads(1000, 500);
        ThreadPool.SetMinThreads(500, 250);

        TcpListener listener = new TcpListener(IPAddress.Any, 8282);
        listener.Start();

        while (true)
        {
            Socket client = listener.AcceptSocket();
            ThreadPool.QueueUserWorkItem(ProcessSocket, client);
        }
    }

    private static readonly string patternHostPort = @"(Host:\s)(\S+)(:)(\d+)";
    private static readonly string patternHost = @"(Host:\s)(\S+)";
    private static Regex regexHostPort = new Regex(patternHostPort);
    private static Regex regexHost = new Regex(patternHost);

    static void ProcessSocket(object request)
    {
        string requestString = string.Empty;
        MemoryStream mStream = new MemoryStream();
        int bytesReceived;
        int bytesSended;
        byte[] buffer;
        byte[] byteOriginalRequest;

        Socket socketClient = (Socket)request;
        Console.WriteLine("Incoming connection: " + socketClient.RemoteEndPoint.ToString());

        buffer = new byte[4096];

        bytesReceived = socketClient.Receive(buffer, 0, buffer.Length, SocketFlags.None);
        mStream.Write(buffer, 0, bytesReceived);
        while (socketClient.Available > 0)
        {
            bytesReceived = socketClient.Receive(buffer, 0, buffer.Length, SocketFlags.None);
            mStream.Write(buffer, 0, bytesReceived);
        }

        mStream.Close();

        byteOriginalRequest = mStream.ToArray();
        requestString = Encoding.ASCII.GetString(byteOriginalRequest);
        //Console.WriteLine(requestString);

        #region Get requested Host and Port
        string srvHost = string.Empty;
        string srvPort = string.Empty;

        Match matchHostPort = regexHostPort.Match(requestString);
        if (matchHostPort.Success)
        {
            srvHost = matchHostPort.Groups[2].Value;
            srvPort = matchHostPort.Groups[4].Value;
        }
        else
        {
            Match matchHost = regexHost.Match(requestString);
            if (matchHost.Success)
            {
                srvHost = matchHost.Groups[2].Value;
                srvPort = "80";
            }
            else
            {
                Console.WriteLine("Invalid request?");
            }
        }
        #endregion

        Console.WriteLine(string.Format("Request to {0} on port {1}", srvHost, srvPort));

        IPAddress[] ipAddress = Dns.GetHostAddresses(srvHost);
        IPEndPoint endPoint = new IPEndPoint(ipAddress[0], int.Parse(srvPort));

        using (Socket socketProxy = new Socket(endPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp))
        {
            socketProxy.Connect(endPoint);

            bytesSended = socketProxy.Send(byteOriginalRequest, byteOriginalRequest.Length, SocketFlags.None);

            MemoryStream m2Stream = new MemoryStream();
            bytesReceived = 1;
            while (bytesReceived > 0)
            {
                bytesReceived = socketProxy.Receive(buffer, 0, buffer.Length, SocketFlags.None);
                m2Stream.Write(buffer, 0, bytesReceived);
            }

            m2Stream.Close();
            byte[] finalResponse = m2Stream.ToArray();
            string stringFinalResponse = Encoding.ASCII.GetString(finalResponse);

            bytesSended = socketClient.Send(finalResponse, finalResponse.Length, SocketFlags.None);

            socketProxy.Close();
        }

        socketClient.Close();
    }
}

Here's some questions:

1) Why if I replace this

bytesReceived = 1;
while (bytesReceived > 0)
{
    bytesReceived = socketProxy.Receive(buffer, 0, buffer.Length, SocketFlags.None);
    m2Stream.Write(buffer, 0, bytesReceived);
}

with this

while (socketProxy.Available > 0)
{
    bytesReceived = socketProxy.Receive(buffer, 0, buffer.Length, SocketFlags.None);
    m2Stream.Write(buffer, 0, bytesReceived);
}

I only get an empty page (in the browser)?

2) Maybe is related with 1) but ... with the current code it takes a while to load any simple page (like google), debugging i've found that the problem may be in socketProxy.Receive ... but I don't know why.

3) Is there any difference between

socketProxy.Send(byteOriginalRequest, byteOriginalRequest.Length, SocketFlags.None);

or

socketProxy.Send(byteOriginalRequest);

? (More parameters is not a valid answer :)

4) Any recommended reading? book, tutorial, ... ?

5) Any other suggestion to learn sockets?

Thanks for your time. Best regards.

+2  A: 

1) Socket.Available returns zero if there is no data available in the buffer at the instant you call it. Socket.Read will block (wait) for data to arrive if there is none available yet. So that's the difference. By calling Read, you make it wait for data. By examining Available, you don't make it wait. So if you happen to look at Available before the data arrives, it will be zero.

2) Not sure why its slow, but you don't need to use a memory buffer for the data you are sending back to the client, because you don't examine it. Just read from one socket and write directly to the other.

3) The two calls are identical.

As for 4) and 5), the CLR socket API is very close to the original C API so you can look at any tutorials or info on good socket programming in C to get more tips, if C# tutorials are hard to find.

Also, you should call Shutdown before calling Close. When you call Close, the socket is disconnected, which means that the other end loses the connection and cannot read any data that they had not yet read - the buffer on the other end is destroyed by the OS and the data is discarded. Calling Shutdown(SocketShutdown.Send) causes the other end to get zero bytes when they call Read (after they've read the remaining data). So then you call Read until it returns zero, which tells you that the other end has got all the data and has also called Shutdown. Then you can call Close.

Finally, when you call Send for the last time to send all the data back to the client, it may not send all of the data in one shot. So you should loop and keep sending whatever is left to send until it has all been sent.

Daniel Earwicker
+1  A: 

If this is your first Sockets venture, I would highly recommend starting out with a "Chat" application. Either way, one valuable tool for testing is also a little tool called WinsockTool.

Don't know if you can post links directly to an .MSI file here but if this doesn't work:

http://www.isatools.org/tools/winsocktool.msi

Google for site:isatools.org WinsockTool

This is awesome for connecting to your "server" socket and sending raw text and seeing the raw responses without having to create a client app.

routeNpingme
oh!, that's a really nice tool! Thanks!
Matías
A: 

Hi,

I tried the same scenario in my system. I'm UnAuthorised error from server. My task to create a proxy server between client ( .Net app or Java app ) and server application hosted in IIS ( asp.net application ).

basically if we trace a request response between client and server, physically we know its only one Req/resp happening between server& client. But internally 20+ Req/resp happening between server& client.

for example :

Client : Req - Sending WebHttpRequest to Web appli. hosted in IIS.

Server : Res - HTTP/1.1 100 Ok continue

Client : Req - Send Post Data

Server : Res - Unauthorised.

Client : Req - Send Credentials

Server : Res - OK continue

Client : Req - Send Post Data

Server : Res - HTTP/1.1 200 OK with Result from the web application hosted in IIS

This is the normaly happens internaly between client and server. My proxy server receive all request from client and send to server, and return the response from server to client.

So the client doesn't know abt original server ip.

I tried with a logic of my own, its not working. its giving bad request, while i run my application without Breakpoint. If i keep Breakpoint and run application i received "UnAuthorised".

Then i got your code while i googled my problem, i'm getting same kind of issue while your applicaiton also.

can you help me to solve my issue please.

Thanks in advance S. Ramkumar