UPDATED: Communicate with Quake2 Servers (ASP.NET: Advanced)

Forum for anything hard- or software related.

Moderator: Moderators

Post Reply
Span
sank like a rock
Posts: 106
Joined: Mon Aug 30, 2004 4:54 am
Location: no
Contact:

UPDATED: Communicate with Quake2 Servers (ASP.NET: Advanced)

Post by Span »

[UPDATE] I've done a few changes to my code. First off, I removed the WatchDog from my initial UDP class, and moved it to an overloaded Socket class. That should make the code more re-usable. Secondly, I decided to use the Socket class instead of UdpClient. [/UPDATE]

This piece of code is taken from a custom web control of mine, so no HTML sry. ;)

Furthermore, if you're inexperienced with the .NET programming platform, this stuff is fairly advanced and basically shows the potential and level of control that you can achieve within an ASP.NET application (or .NET for that sakes). So if you're looking for a place to begin programming ASP.NET applications, this is not the place to begin.

I use C# as my programming language of choice, tho you can just as easily use any of the supported languages for .NET to do exactly the same. In my experience, you can program fairly powerful applications with C# relatively easily. Allthough C++ is probably more powerful on a level-to-level basis, it's an extensively more time-consuming language to program in, if you want to do it right. With managed C++, however, you benefit from many of the features that the .NET platform can provide you with. Still, I find C# the better solution in applications like these.

In this piece of code I've only included the class doing the actual communcation with the server, so that you can pretty much use it in any kind of .NET related application. But that's the beauty of .NET, right? :)

When I started doing the code for this client, I encountered a slight stability problem with the UDP protocol. Due to the nature of UDP, if you open a socket for receiving, it will block the code until it receives something from the host. This meaning that the page won't load if the server doesn't respond (ie. bad address or, sometimes the packets just don't arrive at all.).

I've tried to solve the problem by adding an overloaded 'WatchDog' method to the Socket class. What this basically does, is that whenever you use the overloaded Socket class, and you open it for receiving, it will after a given time (iTimeout) eventually send a packet to itself (localhost).
UDP is a connectionless diagram protocol, so it doesn't differentiate from where the packet was received, so the block is broken.

It's probably not the only or the best method of solving this problem, but it should work :)

As for the rest of this code, what it does, in general, is actually just connect to any server you specify, and throw it a status query. It'll read the data returned to a string.

I'm currently developing a project for a web application that will use this code to communicate crossway with servers and parse the output to a website. So this piece of code is subject to change as I decide to include extra features as the development progresses. ;)

Code: Select all

// Code begin
public class QuakeIIUDP
	{
		private	int				m_iPort;
		private	string			m_szHostname;
		private static int		m_iTimeout = 1000;
		private TimedSocket		m_pSock;
		private string			szRcvd;

		public QuakeIIUDP (string szHostname, int iPort)
		{
			m_iPort			= iPort;
			m_szHostname	= szHostname;
		}

		public void set_port (int iPort)
		{
			m_iPort = iPort;
		}

		public void set_hostname (string szHostname)
		{
			m_szHostname = szHostname;
		}

		public string Receive ()
		{
			// buffer for receiving data
			byte [] pSend		= Encoding.ASCII.GetBytes ("\xFF\xFF\xFF\xFFstatus");

			// create a connection ;)
			m_pSock = Connect (m_szHostname, m_iPort);

			if (m_pSock == null)
				return "Connection failed";

			// our "hacked" UdpClient
			m_pSock.Timeout		= m_iTimeout;

			EndPoint IPEP		= m_pSock.RemoteEndPoint;

			// Query server ÿÿÿÿstatus
			szRcvd = "No data";

			try
			{
				m_pSock.SendTo (pSend, 0, pSend.Length, SocketFlags.None, IPEP);
				Byte[] pRcv = m_pSock.Receive ();
				szRcvd = Encoding.ASCII.GetString (pRcv);
			}
			catch (SocketException)
			{
				szRcvd = "Connection failed.";
			}

			return szRcvd;
		}

		public class TimedSocket : Socket
		{
			public TimedSocket (AddressFamily address_family, SocketType socket_type,
				ProtocolType protocol_type) : base (address_family, socket_type,
				protocol_type) 
			{
			}

			private int m_iTimeout = 0;
			private Thread m_threadWatchDog;

			public int Timeout
			{
				set { m_iTimeout = value; }
				get { return m_iTimeout; }
			}

			public byte [] Receive ()
			{
				byte [] pRet = new byte [256];
				m_threadWatchDog = new Thread(new ThreadStart(StartWatchdog));
				m_threadWatchDog.Start();
				try
				{
					base.Receive (pRet, 0, pRet.Length, SocketFlags.None);
				}
				catch (SocketException)
				{
					pRet = null;
				}
				finally
				{
					m_threadWatchDog.Abort();
				}
				return pRet;
			}

			private void StartWatchdog()
			{
				IPEndPoint ep		= (IPEndPoint) this.LocalEndPoint;
				IPEndPoint IPEP		= new IPEndPoint (IPAddress.Loopback, ep.Port);
				
				Thread.Sleep(m_iTimeout);
				
				Byte [] pSend		= Encoding.ASCII.GetBytes ("/xFF/xFF/xFF/xFF");
				this.SendTo (pSend, 0, pSend.Length, SocketFlags.None, IPEP);
			}
		}

		// Attempts a successful connection.
		public static TimedSocket Connect (string szHostname, int iPort)
		{
			TimedSocket pSock	= null;
			
			try
			{
				IPHostEntry pIP = null;
				
				pIP = Dns.Resolve (szHostname);

				// Loop through the AddressList to obtain the supported AddressFamily. This is to avoid
				// an exception to be thrown if the host IP Address is not compatible with the address family
				// (typical in the IPv6 case).
				foreach(IPAddress ipad in pIP.AddressList)
				{
					IPEndPoint pIPE			= new IPEndPoint (ipad, iPort);

					TimedSocket pSockTmp	= 
						new TimedSocket (pIPE.Address.AddressFamily, SocketType.Dgram, ProtocolType.Udp);
					
//					IPEndPoint pEPLocal		= new IPEndPoint (IPAddress.Any, 27904);
//					pSockTmp.Bind (pEPLocal);
					pSockTmp.Connect (pIPE);

					if (pSockTmp.Connected)
					{
						pSock = pSockTmp;
						break;
					}
					else
						continue;
				}
			}
			catch
			{}

			return pSock;
		}	
	}
k, I really need to start reviewing my code properly before posting.
I've edited this 5 times now. Hopefully this was the last ;)
Post Reply