Thread: IRC, reading the stream

  1. #1
    Registered User
    Join Date
    Mar 2006
    Posts
    17

    IRC, reading the stream

    I decided that I've finally reached the point in my C# knowledge where I could attempt a very simplistic IRC console client. I'm not trying to reach a level to compete with mIRC or anything like that, this is just me trying to learn how sockets and network streams work and interrelate. And so far.. well, I can get it to connect to the IRC network and I can get it to join a channel but it won't display anything on the incoming side of things. I haven't really written the code for that yet, mainly because I'm not entirely sure how.

    Er, well, let me say this instead. I know how to get a line from the stream and send it to the console:

    Console.WriteLine(StreamReader.ReadLine());

    But what I don't understand is how to make it display ALL of the incoming information from the server. IRC constantly sends information (whether it be people talking, ctcp, server messages, whatever) and I don't understand how to collect and display all of that information continiouslly.

    Here's what I have so far. It's split into two files: Program.cs and IRC.cs.

    Program.cs
    Code:
    using System;
    using System.Collections.Generic;
    using System.Text;
    
    namespace IRCtesting
    {
        class Program
        {
            static void Main(string[] args)
            {
                System.Console.WriteLine("Starting the client..\n");
    
                IRC irc;
    
    
                if (args.Length == 0)
                {
                    // Create a new IRC connection using the default constructor
                     irc = new IRC();    
                }
                else
                {
                    // Code to seperate and get the arguments later
                    irc = new IRC();    
                }
    
    
                while (irc.canQuit == false)
                {
                    irc.getCommand();
                }
    
    
                System.Console.WriteLine("Press any key to close the client...\n");
                System.Console.ReadLine();
            }
        }
    }

    IRC.cs
    Code:
    using System;
    using System.Collections.Generic;
    using System.Text;
    using System.Threading;
    using System.Net;
    using System.Net.Sockets;
    using System.IO;
    
    namespace IRCtesting
    {
    
        class IRC
        {
    
            #region variables
    
            // Public variables
            public        bool            canQuit             = false;
            public        bool            connected           = false;
            public static StreamWriter    writer;   
            public static StreamReader    reader;                                  
    
            // Private variables
            private       int             ircPort             = 6667;
            private       string          ircServer           = "irc.esper.net";   
            private       string          nickNameMain        = "CSharpIRC";
            private       string          nickNameBckup       = "CSharpIRCbck";
            private       string          user                = "USER CSharp 1 * :Dont look at me !!!";
            private       string          channel             = "";
            private       TcpClient       ircClient;
            private       NetworkStream   stream;
            private       Thread          pingSender;
    
            // Private constants
            private const string          CRLF                = "\r\n";
            private const string          PING                = "PING :";
    
            #endregion
    
    
            #region constructors
    
            ////////////////////////////////////////////////
            //////////////CONSTRUCTORS//////////////////////
            // There are six constructors which allow you //
            // to specify some or all the information     //
            // necessary to connecting.                   //
            ////////////////////////////////////////////////
            public IRC()
            {
                // Connect to IRC
                connect();
            }
    
            public IRC(string server)
            {
                ircServer = server;
    
                // Connect to IRC
                connect();
            }
    
            public IRC(int port)
            {
                ircPort       = port;
            }
    
            public IRC(string server, int port)
            {
                ircServer     = server;
                ircPort       = port;
    
                // Connect
                connect();
            }
    
            public IRC(string server, int port, string mainNick)
            {
                ircServer     = server;
                ircPort       = port;
                nickNameMain  = mainNick;
    
            }
    
            public IRC(string server, int port, string mainNick, string backupNick)
            {
                ircServer     = server;
                ircPort       = port;
                nickNameMain  = mainNick;
                nickNameBckup = backupNick;
    
                // Connect to IRC using the settings
                connect();
            }
    
            #endregion
    
    
    
            private void connect()
            {
                // Tell people we're going to connect
                System.Console.WriteLine("Connecting to " + ircServer + ":" + ircPort + " ...\n");
    
                try
                {
    
                    // Try to make a connection to the server
                    ircClient = new TcpClient(ircServer, ircPort);
    
                    stream = ircClient.GetStream();
                    reader = new StreamReader(stream);
                    writer = new StreamWriter(stream);
    
                    // Start up our ping handler
                    PingControl();
    
                    // Send the ident information to the server (i.e. our user and who we are)
                    writer.WriteLine(user);
                    writer.Flush();
                    writer.WriteLine("NICK " + nickNameMain);
                    writer.Flush();
                 
                    // We're connected, so let's set our bool to true.
                    connected = true;
                }
                catch (Exception e)
                {
                    // Show the exception
                    Console.WriteLine(e.ToString() + CRLF);
    
                    // Sleep, before we try again
                    Thread.Sleep(5000);
                    connect();
                }
    
            }
    
            private void quit()
            {
                // Message that we're quitting
                writer.WriteLine("QUIT ");
                writer.Flush();
    
                // Exit out of the program.
                quit_program();
            }
    
            private void quit(string message)
            {
                // Message that we're quitting
                writer.WriteLine("QUIT " + ":" + message);
                writer.Flush();
    
                // Exit out of the program.
                quit_program();
            }
    
            private void quit_program()
            {
                // Close everything down
                stream.Close();
                writer.Close();
                reader.Close();
                ircClient.Close();
    
                // Tell the console that we can quit and release control
                connected = false;
                canQuit = true;
    
            }
    
    
            // Join a channel, no key
            private void join(string chan)
            {
                if (channel == "")
                {
                    channel = chan;
                    writer.WriteLine("JOIN " + channel);
                    writer.Flush();
    
                }
    
                else
                {
                    Console.WriteLine("This client only supports one channel right now.\n");
                }
            }
    
            // Join a channel with a key
            private void join(string channel, string key)
            {
            }
    
            // Send something to the channel
            private void sendToChannel(string message)
            {
                writer.WriteLine("PRIVMSG " + channel + " :" + message);
                writer.Flush();
    
            }
    
    
            ////////////////////////////////////////////////
            ///////////////COMMAND PARSER///////////////////
            // The command parser takes the incoming text //
            // from the console and splits it up into     // 
            // tokens as well as determines what command  //
            // has been used and what function should be  //
            // called.                                    //
            ////////////////////////////////////////////////
            public void getCommand()
            {
    
                // Get the command string from the console
                string rawCommandString = Console.ReadLine();
    
                // Break the command into their tokenized parts so we can determine what to do
                string[] commandString;
                commandString = rawCommandString.Split(new char[] { ' ' });
    
                // Get the first element of the command string and convert it to upper case
                string command = commandString[0].ToString();
                command = command.ToUpper();
    
    
                ///////////////////////////////////////////////////
                // The QUIT command.  Used to disconnect from the//
                // server.  It'll also cause our console to stop.//
                ///////////////////////////////////////////////////
                if ((command == "QUIT") || (command == "/QUIT"))
                {
                    // We need to check to see if the person has typed a quit message
                    int commandLength = commandString.Length;
    
                    if (commandLength >= 2)
                    {
                        // Determine what the message is.
                        commandLength = commandString.Length - 1;
                        string message = "";
    
                        for (int i = 1; i <= commandLength; i++)
                        {
                            message = message + commandString[i] + " ";
                        }
    
                        // Let's quit with our message
                        quit(message);
                    }
    
                    else
                        quit();  // Quit with a default message of your nick.
                }
    
    
                ///////////////////////////////////////////////////
                // The JOIN command.  Used to join an IRC channel//
                // and may have a key (if the channel is locked) //
                ///////////////////////////////////////////////////
                if (((command == "JOIN") || (command == "/JOIN") || (command == "/J")))
                {
                    // Set our channel
                    string chan = commandString[1].ToString();
    
                    // Does the channel have the necessary #?
                    if (!chan.StartsWith("#"))  
                    {
                       // We'll add it here.
                        chan = "#" + commandString[1].ToString();
                    }
    
    
                    // Does this join command have a key for the channel?
                    // Check for the max number of words in the array first.
                        int commandLength = commandString.Length;
    
                        if (commandLength >= 3)
                        {
                            // Is the next word a space or null?
                            if (commandString[2].ToString() != "")
                            {
                                // We have a key!  Let's get the key and join the channel.
                                string key = commandString[2].ToString();
                                join(chan, key);
                            }
                        }
    
                    // No, it doesn't, so just join a regular channel
                        else
                            join(chan);
                }
    
                ///////////////////////////////////////////////////
                // The "Say" command to send a message to the    //
                // active channel.                               //
                ///////////////////////////////////////////////////
                if ((command == "SAY") || (command == "/SAY"))
                {
                    if (channel != "")
                    {
                        // Determine what the message is.
                        int commandLength = commandString.Length -1;
                        string message = "";
    
                        for (int i = 1; i <= commandLength; i++)
                        {
                            message = message + commandString[i] + " ";
                        }
    
                        // Send the message to the command
                        sendToChannel(message);
                    }
    
                    else
                    {
                        Console.WriteLine("You must first join a channel.\n");
                    }
                }
    
            }
    
    
    
            ////////////////////////////////////////////////
            ////////////////PING CONROL/////////////////////
            ////////////////////////////////////////////////
            // PING control is a set of two functions that//
            // will ping the server every 15 seconds on a //
            // seperate thread until we are no longer     //
            // connected to the server.                   //
            ////////////////////////////////////////////////
            private void PingControl() 
            {
                // Start a new thread for the Ping Control
                pingSender = new Thread (new ThreadStart(PingRun));
                pingSender.Start();
    
                // Begin running the control
                PingRun();
            }  
    
    
            // Send PING to irc server every 15 seconds
            private void PingRun()
            {
                // Is the client still running?  If so, we need to ping the server
                while ((canQuit == false) && (connected == true))
                {
                    writer.WriteLine(PING + ircServer);
                    writer.Flush();
                    Thread.Sleep(15000);
                   }
            }
    
    
        }
    }


    I should add that once I get the incoming information to display I plan to scrap the console version and start on a GUI version. But I really need some help on learning how to make it display everything. Even some basic ideas and concepts will help me out.

    Thanks guys.

  2. #2
    and the Hat of Clumsiness GanglyLamb's Avatar
    Join Date
    Oct 2002
    Location
    between photons and phonons
    Posts
    1,110
    You know how to connect to the server using a username etc...

    But you don't know how to split the data in the different formats, as in you need to know what data represents what ...

    If thats the case:

    Use a program like Ethereal to check whatever is coming in from the server.

    Have you checked the IRC RFC ? A quick google came up with http://www.irchelp.org/irchelp/rfc/rfc.html , actually http://www.irchelp.org/irchelp/rfc/index.html is better , pick your weapon .

    Once you know how each kind of message is delivered you can start building your classes for each sort of message, whenever you read data from the stream you can then try to Deserialize the data to whatever kind of object you need.

    I've only fiddled with sockets and networkstreams in C# for a short while in college when we were building a FTP client and server. But the system is the same, I think the representation of the data is where your problem lies...

    If not then consider this post useless .
    Last edited by GanglyLamb; 08-03-2006 at 04:55 PM.

  3. #3
    Registered User
    Join Date
    Mar 2006
    Posts
    17
    I'm not too concerned about what the actual incoming data is, at the moment. When I am, those links you provided will be the first place I go to read.

    What I'm confused about is how to constantly display the lines that are incoming. I know when you're reading from a file you can use a WHILE statement to go through each piece of the stream unti you reach the end of the file.

    I guess what I mean to ask.. is there a command or a function setting that basically goes:

    ON incoming data..... process and display to the console.

    Because I can't imagine setting up a method that will be running 100% of the time using a while loop to go through the StreamReader. Plus I've already tried something similar to this and it froze the program.

    I also can't imagine having a dataRead() call after every little thing you do in the code. Plus I don't think that would work too well for when you're idle in a channel and want to watch what people type.

    I hope that makes some kind of sense.

  4. #4
    and the Hat of Clumsiness GanglyLamb's Avatar
    Join Date
    Oct 2002
    Location
    between photons and phonons
    Posts
    1,110
    Makes perfect sense, create a method that will listen for any incoming data, and yes use a while loop in that method with a boolean value that represents wether the stream is valid or not ( connection alive or not ).

    Then have this method run in a seperate thread, something like:

    listenerThread = new Thread(new ThreadStart(connection.ReceiveInfo));

    Where ReceiveInfo is the method with the while loop .

    And in the ReceiveInfo the only thing you do is a readLine from the stream , whenever data is available it will be read, if not the thread will stop at that point untill data is available to be read.

    Keep in mind that if you want to use a GUI afterwards you will probably going to need delegates, and while this is not necesarry , as in 2 threads can write to the same console without any problem. It will be a problem when you get to the GUI. If thread 1 creates the GUI part where info is going to be displayed, then thread 2 will not be able to modify its content. But then delegates come to the rescue, but still, I would first implement it that it works on the console. Then try to get it working with a GUI, but keep in mind when working on the console version that you are still working with threads.

    ( on a side note: I have encountered this thing , I don't know how it comes but anyway, when dealing with threads in Visual Studio 2005 I get alot of CrossThreadExceptions while debugging or even when simply running the program from the VS environment, as soon as I just open the .exe the Exceptions disappear... sometimes they happen, sometimes not, and untill now I've not found a way around. And because of this I've been chasing ghosts for along while I guess when debugging )

    I also like to ad that the use of the using statement is very handy when dealing with streams, at least I find them good.
    If you don't know about the statement yet : http://msdn2.microsoft.com/en-US/library/yh598w02.aspx

    At least I won't forget to close any streams that are still open + I can clearly see the scope of where the stream will be available and not disposed of.
    Last edited by GanglyLamb; 08-03-2006 at 05:17 PM.

  5. #5
    Registered User
    Join Date
    Mar 2006
    Posts
    17
    All right. That makes sense. I'll go and try to implement it here in just a little bit, but I had one more question first.

    On sending streams you can use the flush command to empty the stream (unless I'm misundering what Flush really does). Is there an equilivant to reading the stream? Or will that not be necessary? I'm afraid that once I set up a while statement it'll continue to spit back the same lines over and over in an infinite loop.

  6. #6
    and the Hat of Clumsiness GanglyLamb's Avatar
    Join Date
    Oct 2002
    Location
    between photons and phonons
    Posts
    1,110
    The way I see flush in C# is like this:

    Imagine a Stream is a tube. When I do stream.WriteLine("bla"); I merely pour content into the tube at one end. At the other end of the tube the receiver is waiting for this data. But it will never receive the data since there's this lid keeping the water ( data ) from being flushed. A explicit flush will make sure, the tube ( buffer ) is empty and all data that was still residing inside the tube is being send to whatever it is that is on the other end.

    Basically simply put you don't need the flush at the receiving end in this situation, although there are cases where one would need to flush first.

    Just the readline will be enough ( at least that was enough when implementing my FTP server / client application ).

  7. #7
    Registered User
    Join Date
    Mar 2006
    Posts
    17
    Yeah, you're right. It works!

    Thanks a lot. And I'll try to keep it in mind that when I switch to GUI it'll be a bit different.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. New stream flushing FAQ candidate?
    By Sebastiani in forum C Programming
    Replies: 9
    Last Post: 07-01-2009, 05:57 PM
  2. Stream Server
    By ch4 in forum Networking/Device Communication
    Replies: 18
    Last Post: 06-29-2009, 03:09 PM
  3. Cannot close file stream
    By g4j31a5 in forum C++ Programming
    Replies: 8
    Last Post: 01-02-2007, 01:52 AM
  4. reterning a reference to the input stream
    By indigo0086 in forum C++ Programming
    Replies: 3
    Last Post: 06-22-2006, 10:29 AM
  5. Some help needed with IRC
    By SaintK in forum C++ Programming
    Replies: 2
    Last Post: 04-04-2004, 07:27 AM