[.net] Simplest Non-Threading Socket Loop (C#)?

Started by
8 comments, last by BradSnobar 14 years, 11 months ago
I'd like to make a loop for my MUD, but I don't know much about Sockets nor all the Synchronous/Asynchronous stuff. What would be the design of a simple loop that would include getting/sending info to every open socket in a given moment? Cam somebody write me up a simple game loop including the socket commands for the Asynchronous method? (To what I've understood, Asynchronous is the only solution if you're not Threading each socket right?) Thanks!
Advertisement
If you are writing a MUD this is not just something you can gloss over. You are just going to have to suck it up and learn it.

theTroll
Quote:Original post by TheTroll
If you are writing a MUD this is not just something you can gloss over. You are just going to have to suck it up and learn it.

theTroll


I'd have to agree. I managed to get mine up and running pretty quick, but over time I learned it had several key deficiencies. It would appear to be working fine, but every now and then, under circumstances difficult (but not impossible) to reproduce, something would go wrong.

Even now, I'm not 100% positive that I have it right, but it appears to be working okay and I threw some pretty vigorous tests at it.

So don't get discouraged, but don't expect it to be a walk in the park. We're here to help as you need it, though.

Good luck!
Without order nothing can exist - without chaos nothing can evolve.
It's been awhile since I've done much with Sockets. I remember them being something of a pain, and I haven't touched them since. If you're looking to simplify things though, why not just make an AJAX browser based MUD? It would simplify the networking end immensely.
-----OpenEndedAdventure.com - The Adventure that Anyone Can Edit.
That's one idea. Being able to make the MUD fancier with small graphics or maybe even sounds is very interesting, even though I'd prefer to keep it mainly text-based.

Even though I've already taken a brief look at the inside of some AJAX books on some bookstores, I don't know much about it.

How long would it take to know enough for coding up a server and a site to handle that?
To deviate slightly, I never understood why people put graphical content in MUDs. It defeats the purpose. I always look at MUDs being the gaming equivalent of books. You'd never see ASCII art in a book. Why in a MUD?

That said, I'm all for graphical utilities. A roundtime bar and a room exit compass are invaluable tools I'd not want to play without.
Without order nothing can exist - without chaos nothing can evolve.
As far as the AJAX thing goes, I'd actually been tinkering with code to make an AJAX text adventure.

I think I might turn this into a tutorial at some point. For now, I'll post the code.

The server side is ASP.NET/C#. However, there's hardly any server side code at all. Most of the code is &#106avascript. I'm sure it would be real easy to accomplish the same server side thing in PHP.

At any rate, all the server side code below does is send out a response to a command sent from &#106avascript/Ajax.<br><br><!--STARTSCRIPT--><!--source lang="c#"--><div class="source"><pre><br><span class="cpp-keyword">namespace</span> Advent<br>{<br> <span class="cpp-keyword">public</span> partial <span class="cpp-keyword">class</span> _Default : System.Web.UI.Page<br> {<br> <span class="cpp-keyword">private</span> <span class="cpp-keyword">string</span> CurrentSessionID<br> {<br> get<br> {<br> <span class="cpp-keyword">return</span> Request[<span class="cpp-literal">"SessionID"</span>];<br> }<br> }<br><br> <span class="cpp-keyword">private</span> System.Web.SessionState.HttpSessionState CurrentSession<br> {<br> get<br> {<br> <span class="cpp-keyword">if</span> (<span class="cpp-keyword">String</span>.Is<span class="cpp-literal">Null</span>OrEmpty(CurrentSessionID))<br> {<br> <span class="cpp-keyword">return</span> <span class="cpp-literal">null</span>;<br> }<br><br> <span class="cpp-keyword">if</span> (Application[CurrentSessionID] != <span class="cpp-literal">null</span>)<br> {<br> <span class="cpp-keyword">return</span> (System.Web.SessionState.HttpSessionState)Application[CurrentSessionID];<br> }<br><br> <span class="cpp-keyword">return</span> <span class="cpp-literal">null</span>;<br> }<br> }<br><br> <span class="cpp-keyword">private</span> <span class="cpp-keyword">string</span> UserCommand<br> {<br> get<br> {<br> <span class="cpp-keyword">if</span> (Request[<span class="cpp-literal">"command"</span>] == <span class="cpp-literal">null</span>)<br> {<br> <span class="cpp-keyword">return</span> <span class="cpp-literal">""</span>;<br> }<br><br> <span class="cpp-keyword">return</span> (<span class="cpp-keyword">string</span>)(Request[<span class="cpp-literal">"command"</span>]).Trim().ToUpper();<br> }<br> }<br><br> <span class="cpp-keyword">protected</span> <span class="cpp-keyword">void</span> Page_Load(<span class="cpp-keyword">object</span> sender, EventArgs e)<br> {<br> <span class="cpp-keyword">if</span> (UserCommand.Length &gt; <span class="cpp-literal"><span class="cpp-number">0</span></span>)<br> {<br> Response.Clear();<br> Response.Write(<span class="cpp-literal">"Result of command \"</span><span class="cpp-literal">" + UserCommand + "</span>\<span class="cpp-literal">" from Session "</span> + CurrentSessionID);<br> Response.End();<br> }<br> <span class="cpp-keyword">else</span><br> {<br> Application[Session.SessionID] = Session;<br> <span class="cpp-keyword">string</span> startupScript = <span class="cpp-literal">""</span>;<br> startupScript += <span class="cpp-literal">"var sessionID = '"</span> + Session.SessionID + <span class="cpp-literal">"';"</span> + <span class="cpp-literal">"\r\n"</span>;<br> <span class="cpp-comment">//startupScript += "alert(sessionID)";</span><br> Page.ClientScript.RegisterStartupScript(<span class="vb-function">typeof</span>(Page), <span class="cpp-literal">"startup"</span>, <span class="cpp-literal">"&lt;script&gt;\r\n"</span> + startupScript + <span class="cpp-literal">"\r\n&lt;/script&gt;"</span>);<br> }<br> }<br> }<br>}<br><br></pre></div><!--ENDSCRIPT--><br><br>Here's the client side.<br>It's a little more involved.<br>Basically, the markup contains a <div>, used for the text ("You are in a forest. A dog has bitten you. etc.) and a textbox (&lt;input type="text" /&gt;) where the user enters their commands.<br><br><!--STARTSCRIPT--><!--source lang="cpp"--><div class="source"><pre><br>&lt;%@ Page Language=<span class="cpp-literal">"C#"</span> AutoEventWireup=<span class="cpp-literal">"true"</span> CodeBehind=<span class="cpp-literal">"Default.aspx.cs"</span> Inherits=<span class="cpp-literal">"Advent._Default"</span> %&gt;<br><br>&lt;!DOCTYPE html <span class="cpp-keyword">PUBLIC</span> <span class="cpp-literal">"-//W3C//DTD XHTML 1.0 Transitional//EN"</span> <span class="cpp-literal">"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"</span>&gt;<br><br>&lt;html xmlns=<span class="cpp-literal">"http://www.w3.org/1999/xhtml"</span> &gt;<br>&lt;head runat=<span class="cpp-literal">"server"</span>&gt;<br> &lt;title&gt;Untitled Page&lt;/title&gt;<br>&lt;script type=<span class="cpp-literal">"text/javascript"</span>&gt;<br>function CheckEnter(ev)<br>{<br> var keyCode = null;<br> <span class="cpp-keyword">if</span>(window.event)<br> {<br> keyCode = window.event.keyCode;<br> }<br> <span class="cpp-keyword">else</span> <span class="cpp-keyword">if</span>(ev)<br> {<br> keyCode = e.which;<br> }<br> <span class="cpp-keyword">else</span><br> {<br> <span class="cpp-keyword">return</span> <span class="cpp-keyword">true</span>;<br> }<br><br> <span class="cpp-keyword">if</span>(keyCode == <span class="cpp-number">13</span>)<br> {<br> SendCommand();<br> <span class="cpp-keyword">return</span> <span class="cpp-keyword">false</span>;<br> }<br><br> <span class="cpp-keyword">return</span> <span class="cpp-keyword">true</span>;<br>}<br><br>function SendCommand()<br>{<br> var tbText = document.getElementById('tbCommand');<br> var command = Trim(tbText.value);<br> tbText.value = '';<br><br> <span class="cpp-keyword">if</span>(command.length &gt; <span class="cpp-number">0</span>)<br> {<br> var url = '<span class="cpp-keyword">default</span>.aspx?Command=' + escape(command) + '&amp;SessionID=' + sessionID;<br> var oAjax = GetAjaxObject();<br> oAjax.onreadystatechange = function() { <span class="cpp-keyword">if</span>(oAjax.readyState == <span class="cpp-number">4</span>) { ProcessCommandResponse(oAjax); } }<br> oAjax.open(<span class="cpp-literal">"POST"</span>, url, <span class="cpp-keyword">true</span>); <br> oAjax.send('asdf');<br> }<br>}<br><br>function ProcessCommandResponse(oAjax)<br>{<br> var dvLog = document.getElementById('dvLog');<br> dvLog.innerHTML += oAjax.responseText + '&lt;br /&gt;';<br>}<br><br>function GetAjaxObject()<br>{<br> var oAjax = null;<br> <span class="cpp-keyword">try</span><br> {<br> oAjax = <span class="cpp-keyword">new</span> XMLHttpRequest(); <br> }<br> <span class="cpp-keyword">catch</span>(ex)<br> {<br> oAjax = <span class="cpp-keyword">new</span> ActiveXObject(<span class="cpp-literal">"Microsoft.XMLHTTP"</span>);<br> }<br><br> <span class="cpp-keyword">return</span> oAjax;<br>}<br><br>function Trim(str)<br>{<br> <span class="cpp-keyword">return</span> str.replace(/^\s+|\s+$/g,<span class="cpp-literal">""</span>);<br>}<br><br>&lt;/script&gt;<br>&lt;/head&gt;<br>&lt;body&gt;<br> &lt;form id=<span class="cpp-literal">"form1"</span> runat=<span class="cpp-literal">"server"</span>&gt;<br> &lt;div id=<span class="cpp-literal">"dvLog"</span> style=<span class="cpp-literal">"background-color:Black; color:#00FF00; width:640px; height:480px; font-family:Courier New;"</span>&gt; <br> &lt;/div&gt;<br> &lt;input id=<span class="cpp-literal">"tbCommand"</span> style=<span class="cpp-literal">"width:640px;"</span> type=<span class="cpp-literal">"text"</span> onkeypress=<span class="cpp-literal">"return CheckEnter(event);"</span> /&gt;<br> &lt;/form&gt;<br> <br>&lt;script type=<span class="cpp-literal">"text/javascript"</span>&gt;<br>&lt;/script&gt; <br> <br>&lt;/body&gt;<br>&lt;/html&gt;<br><br></pre></div><!--ENDSCRIPT--><br><br><br>When the user types something in the textbox, it checks to see what character was typed. If that character is the Enter key (ASCII 13), then it sends it off to the Server using Ajax. (Normally when you hit enter &#111;n a textbox, a postback is performed. Since we don't want a postback, we return false in this case, which cancels the brower's normal action.)<br><br>To send the command off to the server, we first create an Ajax object. Depending &#111;n which browser you're using, the command to do so is different. So, we wrap both in a try-catch. Whichever &#111;ne succeeds is the correct command for the user's browser.<br><br>After you have an Ajax object, set the target url (passing the command that the user typed within that url), set a callback function, and send it to the server.<br><br>oAjax.onreadystatechange = function() { if(oAjax.readystate==4) { alert('Received data from the server!!!'); } }<br><br>This callback is called every time the Ajax object's "readystate" is changed. The &#111;nly &#111;ne you really care about is readystate 4. This means that the server has responded.<br><br>oAjax.responseText contains the response from the server.<br><br><br>Anyway, I'm sure that's a lot to chew &#111;n.<br>Like I said, I'll turn this into a tutorial at some point. For now, let me know if any (or all) of it doesn't make sense.
-----OpenEndedAdventure.com - The Adventure that Anyone Can Edit.
why not just use WCF or SOAP? MUDs are generally pretty latency tolerant.
if you think programming is like sex, you probably haven't done much of either.-------------- - capn_midnight
Could always use Lidgren.Network. +1 for WCF if you can afford the latency. It can use raw TCP sockets for better performance.
Anthony Umfer
There is a good book on the subject here:

TCP/IP Sockets in C#: Practical Guide for Programmers


http://www.amazon.com/TCP-IP-Sockets-Practical-Programmers/dp/0124660517/ref=sr_1_6?ie=UTF8&s=books&qid=1242345884&sr=8-6

This topic is closed to new replies.

Advertisement