Introduction
Communication is a problem I have had plenty of dealings with in work related projects and projects that are just for fun. .NET remoting makes communication easier but if you have ever used it and received one of their "Remoting Exception" errors, you can understand why I don't like it. Hopefully WCF corrects some of these problems. Either case, if you want to communicate P2P (Peer to Peer) then you either have to deal with something like UPNP, VPN, or proxy of some kind. This is my implementation of a Web proxy.
Background
P2P communication is a problem I've run into several times with my applications. I've made several Messenger type applications that communicate with TcpClients
and a TcpListener
. The only problem with using them is that your communication is limited to your local area network. Recently, I made another Messenger application that communicates with several Web services and it polls them periodically to get a new list of users, messages, etc. This works fine but having communication that is based on a bunch of clients polling a Web server is not so good. Below is an outline of my solution to this problem. The lines represent WebRequests
that are made to an HttpHandler
Server.ashx that are waiting for another client to send data to their end point. I modelled this after TcpClient
and TcpListener
and stuck a Proxy in between their communication. Problem solved, for the most part.
Using the Code
Before you try these objects out, I am assuming you have basic knowledge of socket communication and you have used TcpClients
and TcpListeners
. Their Proxy counterparts I've made are very similar except that ProxyClient
has a SendData
method and a Receiving
event instead of a Stream
that you can read/write to.
The first thing you need to do is get the Proxy Web Server up and running. If you open the Proxy solution, I am talking about the ProxyService
Web site. If you modify this at all, make sure the web.config lines below stay put. This registers the HttpHandler
Server.ashx.
<system.web>
<httpHandlers>
<add verb="*" path="*.ashx" type="ProxyServer.Server, ProxyServer"/>
</httpHandlers>
Next, you need to make some connections. Make a ProxyListener
that will start Listening and a ProxyClient
that connects to it. To make things easier, any object you want to send from ProxyClient
to ProxyClient
needs to inherit from DataMessage
in the ProxyCommon
project. This gives your object a SendEndPoint
, and RecieverEndPoint
. If you wanted to do a client / server app, your server side code would look something like this.
App.Config
<appSettings>
<add key="ProxyServer" value="http://localhost:52318/ProxyService/Server.ashx"/>
<add key="ProxyServerName" value="Test Server"/>
</appSettings>
Host Communication
string ServerName = ConfigurationManager.AppSettings["ProxyServerName"];
ProxyListener Listener = new ProxyListener(ServerName);
Listener.Start();
ProxyClient Client = Listener.AcceptProxyClient();
string Txt = "Some message I want to send";
StringMessage Msg = new StringMessage(Txt, Client.ClientEndPoint, Client.ServerEndPoint);
Client.SendData(Msg.ToByteArray());
Now for the client side. A ProxyListener
starts listening based on a ServerName
. The proxy server uses this as key and makes an EndPoint
for it. When you create a ProxyClient
to communicate with the listener, it is connecting to the same ServerName
. The proxy server maps the EndPoints
and then both ProxyClients
are ready to communicate.
ProxyClient Client = new ProxyClient();
Client.Recieving += new EventHandler<RecievingEventArgs>(Client_Recieving);
string ServerName = ConfigurationManager.AppSettings["ProxyServerName"];
Client.Connect(ServerName);
string Txt = "Some message I want to send";
StringMessage Msg = new StringMessage(Txt, Client.ClientEndPoint, Client.ServerEndPoint);
Client.SendData(Msg.ToByteArray());
void Client_Recieving(object sender, RecievingEventArgs e)
{
StringMessage Msg = new StringMessage(e.Data);
}
Points of Interest
- The major difference between the
ProxyClient
class and the TcpClient
class which you may be used to is that you can't get to a single Stream
to read / write to
. This is because the underlying stream
s are coming from HttpWebRequest
and WebResponse
. - This idea has a few flaws in it that I haven't worked out. Since the proxy exists on a Web server and has a message queue, endpoints, and such stored in the app cache, if the Web server ever restarts, your connections are lost and need to be reestablished. This can be a bigger problem if you are using a shared hosting service such as GoDaddy, which seems to restart on me once a day. ;-)
- This method will definitely work sending P2P information that is small. I would consider sending small files or serialized objects over it but that's about it. Your communication will be slower than connecting directly with sockets because the streams are having to go through the Web server. If you want to really stream lots of data P2P, this is probably not going to be fast enough.
History
- 20th April, 2008: Initial post
This is my first attempt at writing Client
and Listener
wrappers for P2P communication. It's a work in progress, so I am open for suggestions. After I get this communication somewhat perfected, I'm planning on writing another communication wrapper that would be better suited for network gaming, video streaming, etc. If anyone has suggestions on how to do P2P communication with UPNP / port forwarding / tunnelling, please let me know.