Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / desktop / WinForms

Navy Battle Game - II

2.82/5 (5 votes)
26 Jul 20055 min read 2   976  
A client-server navy battle game.

Server image with the X

Introduction

A client-server application with sockets and IO to play the Navy Battle game.

Before you start to read

This article is the second part of a two-article series. The first part covers how to draw a navy battle battlefield. The second part covers client-server connection and how to draw the coordinates in the right places. To better understand this article, you should know a little bit of socket connections (for the part 2), the namespace System.Drawing and IO (for the part 2 too). If you don't have any idea what I'm talking about, I recommend you read the Deitel's book "C# How to Program". But if you don't have money, the documentation in the MSDN is a great help. The great deal is you to have the book and to navigate in the MSDN.

We're back again

We're back again, now to explain the client-server connection and how to draw the "X"s in the battlefield.

The server

In a client-server connection, we have to follow some steps. Below are the server steps:

  • Create the listener object;
  • Start the object;
  • Associate it to a socket to make the transaction data;
  • Send and receive data;
  • And finally, close the connection when all the things are done;

In a client-server application, the methods that start the server or client must be put in threads. You must do this because the server stays listening until a connection is made, and when the client application starts, the server catches the connection request made by the client and the magic is done.

OK, now I put here the constructor of my class:

C#
//The constructor
public Server(Graphics g)
{
    G = g;
    IPAddress IPLocalhost = IPAddress.Parse("127.0.0.1");
    //first commandment: create the listener
    ServerListener = new TcpListener(IPLocalhost, 65000);
    //second commandment: start it!
    ServerListener.Start();
}

You have noticed that in my constructor I pass a Graphics parameter called "g". Why? The explanation is in the "Desenho" object which is in the Running() method.

C#
public void Running()
{
    try
    {
        //third commandment: associate the Listener 
        //with the socket to receive data
        while(true)
        {
            Connexion = ServerListener.AcceptSocket();
            //fourth commandment: make transactions
            if(Connexion.Connected)
            {
                DataStream = new NetworkStream(Connexion);
                Reader = new BinaryReader(DataStream);
                Writer = new BinaryWriter(DataStream);
                m_bConnect = true;
                do
                {
                    sData = Reader.ReadString();
                    using(Draw Desenho = new Draw())
                    {
                        Desenho.GetCoordinates(sData+
                          Form1.stCoordinates, G, true);
                    }
                }while(DataStream != null && 
                   sData != "Close connection");
            }
        }
        //fifth commandment: close it!
        Reader.Close();
        Writer.Close();
        DataStream.Close();
        Connexion.Close();
        Application.Exit();
    }
    catch(SocketException SocketEx)
    {
        MessageBox.Show(SocketEx.Message);
    }

This method has a try/catch block that has a socket which receives a socket object returned by the AcceptSocket() method of my TcpListener object. It verifies if the connection is established accessing the Connected property and creates the datastream and the reader and the writer. What about the Graphics g parameter? In the do while loop, there is a statement: using. This statement guarantees that the object is released when you have done all the necessary things with it. Notice that there is a Draw object called Desenho. The GetCoordinates method of the Desenho object is called and one of the parameters is the Graphics object.

Caution: It's not a good idea to create a variable of type Graphics. Like Jesse Liberty says: "Use the Graphics object when necessary and release it." I use this guy only in my GetCoordinates method and it's automatically released when the using statement is finished.

The Client

Now, let's see the steps for the client:

  • Create the object;
  • Obtain the stream where the data will be sent and received;
  • Send and receive data;
  • Close the connection;

The steps for the client are not so different from the server. The client establishes a communication with the server sending a request. The client request acts like a connector, and the socket server, like a tomade. It's very simple. Now, we analyze the constructor and the RunningClient() method.

Look at this piece of code:

C#
//The constructor
public Client(Graphics G)
{
    Gr = G;
}
public void RunningClient()
{
    cClient = new TcpClient();
    try
    {
        cClient.Connect("localhost", 65000);
        DataStream = cClient.GetStream();
        Reader = new BinaryReader(DataStream);
        Writer = new BinaryWriter(DataStream);
        do
        {
            sData = Reader.ReadString();
            using(Draw Desenho = new Draw())
            {
                Desenho.GetCoordinates(sData + 
                  Form1.stCoordinates, Gr, true);
            }
        } while(DataStream != null && sData != "Close connection");
        Reader.Close();
        Writer.Close();
        DataStream.Close();
        cClient.Close();
        Application.Exit();
    }
    catch(SocketException ex)
    {
        MessageBox.Show(ex.Message);
    }
}

Very probably, you've noticed that this method and the Running method of the class Server are much equal. And you're right. But there are a few differences. Look at this piece of code:

C#
public void RunningClient()
{
    cClient = new TcpClient();
    try
    {
        cClient.Connect("localhost", 65000);
        DataStream = cClient.GetStream();
    //others parts of code supressed
}

Notice that the client constructor receives a Graphics parameter, like the server constructor. Notice too that the TcpClient object doesn't have a Start method. As it is a client, it sends to the server a connection request with the Connect(), while in the server there is a Start() that listens if a client wants to establish a connection (put in a while loop). When the connection is established, the client calls the GetStream() method to obtain the NetworkStream that the data will be transferred to. This information is assigned in the DataStream object. So, the transfer will occur.

The GetCoordinates Method

This method is so important in our Navy Battle. It's used in the server as in the client. It receive three parameters: a Graphics parameter, a string with coordinates, and a boolean. The Graphics parameter is used only to be passed to a private method called DrawX(). The boolean variable is used to see if the player has inserted the coordinates to populate his battlefield or he inserted a coordinate to hit the enemy. The string variable contains the coordinates that populate the battlefield of the current user or the coordinates that the current user chooses to hit his enemy. Below is the method:

C#
public void GetCoordinates(string sCoordinates, 
                          Graphics G, bool bInserted)
{
    int[] iLocations = new int[sCoordinates.Length];
    int iLength = sCoordinates.Length;
    string sReceive;
    string sCompare = sCoordinates.Substring(0,2);
    m_bWhatX = bInserted;
    for(int i = 0; i < iLength ; i++)
    {
        sReceive = sCoordinates.Substring(i,1);
        switch (sReceive)
        {
            /*populating the array with the coordinates 
              that will be used to draw*/
            case "A":
              iLocations[i] = 90; 
              break;
            case "a":
              iLocations[i] = 90; 
              break;
            case "B":
              iLocations[i] = 140;
              break;
            case "b":
              iLocations[i] = 140;
              break;
            case "C":
              iLocations[i] = 190;
              break;
            case "c":
              iLocations[i] = 190;
              break;
            case "D":
              iLocations[i] = 240;
              break;
            case "d":
              iLocations[i] = 240;
              break;
            case "E":
              iLocations[i] = 290;
              break;
            case "e":
              iLocations[i] = 290;
              break;
            case "1":
              iLocations[i] = 40; 
              break;
            case "2":
              iLocations[i] = 90;
              break;
            case "3":
              iLocations[i] = 140;
              break;
            case "4":
              iLocations[i] = 190;
              break;
            case "5":
              iLocations[i] = 240;
              break;
        }
    }
    for(int i = 0 ; i <= 8 ; i+=2)
    {
        //to draw the data inserted in TxtLocation
        if(!bInserted)
        {
            /*just a inversion, cause horizontal
             *line in battlefield is of numbers
             * and vertical, is composed by letters
             */
            DrawX(iLocations[i+1], iLocations[i], G);
        }
        //to compare and draw the data sended 
        //by client with the server coordinates and draw 
        else
        {
            //with this, the string will be 
            //the length of 10 when the length is 12
            sCoordinates = sCoordinates.Remove(0,2);
            while(true)
            {
                if(sCompare == sCoordinates.Substring(i,2))
                {
                    DrawX(iLocations[i+1], iLocations[i], G);
                    MessageBox.Show("Acertou");
                    break;
                }
                else
                {
                    DrawX(iLocations[i+1], iLocations[i], G);
                    break;
                }
            }
            break;
        }
    }
}

Don't be scared with the size of this method. It's simple. The method gets each character of the string and puts it in a switch statement to populate the interger array iLocations that is used to save all the coordinates that will be used by the DrawX() method. The numbers that iLocation array holds, are used by the DrawX() method to draw the "X"" in the center of some square, which is specified by the string sCoordinates. Then the boolean bInserted is used to say if the DrawX() method draws a single coordinate (that is the coordinate send by the enemy) or draws the coordinates that the current user chooses to put in their battlefield.

The DrawX method

Finally, we will talk about the DrawX() method. It is very, very simple. Look for yourself:

C#
private void DrawX(int iX, int iY, Graphics G)
{
    Pen pColor;
    if(!m_bWhatX)
    {
        //in the case of server the code is this: 
        //    pColor = new Pen(Color.Blue, 5);
        pColor = new Pen(Color.Red, 5);
    }
    else
    {
        //in the case of server the code is this:
        //    pColor = new Pen(Color.Red, 5);
        pColor = new Pen(Color.Blue, 5);
    }
    Point pA = new Point(iX, iY);
    Point pB = new Point(iX+30, iY+30);
    Point pA1 = new Point(iX+30, iY);
    Point pB1 = new Point(iX, iY+30);
    G.DrawLine(pColor,pA,pB);
    G.DrawLine(pColor,pA1,pB1);
}

Remember that this method draws all the coordinates send by the client populating the battlefield or draws the coordinate send by the enemy. This is done using the boolean variable m_bWhatX that receives the value of the bInserted parameter. I put the server's Pen objects commented just for you to know what "X" each application draws. Do you see why this method is so simple?

Thank you again

Man, I hope that you enjoyed this article and the first part of it. I loved to write this article (it's my first time). If you've liked or if you've hated it, send me an email with your comments, doubts, corrections, or new ideas. I want to tell you something: if you want to be a good programmer, you need to do one thing: study, a lot. I said this from my own experience.

Article's History

  • 07-16-2005 - The beginning of the second part of this article series.
  • 07-26-2005 - I stopped in between for a while, but finally, the second part is finished. Revision of both parts done and the complete package sent to The Code Project.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here