Tutorials‎ > ‎

[C#] Socket Simple Multiplayer Game [Tic Tac Toe] (Part 1 - Interface and Connection)

posted Feb 25, 2013, 6:26 PM by Davin Timothy Ivander   [ updated Aug 15, 2016, 11:23 PM by Surya Wang ]

Introduction

    Using Socket in C# to create simple multiplayer game, connecting with each other between player, sending the data and retrieving the data sent. using asynchronous to manage the connection, while creating the logic of game.

    In this tutorial I will guide you to create a game named tic tac toe, a simple 2 player game that allows player to pick a slot in 3x3 table, and whoever can line 3 of em wins.


Main Menu Interface



Description:
  • The first textbox is used for input the ip of the SERVER, and the second box for the port of the SERVER.
  • Start as server only if you wanted to host the game, not to join the game, beware the port of each host cannot same.
  • Start as client only if you wanted to join the game which already hosted.
Component Used:
  • 3 label
  • 3 button
  • 2 textbox

Main Form Code


using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Text.RegularExpressions;

namespace DvTicTacToe
{
    public partial class Main : Form
    {
        private bool isServer = false;
        private SocketManagement con;// object for connecting 

        public Main()
        {
            InitializeComponent();
        }

        private void Main_Load(object sender, EventArgs e)
        {
            //make the form can be moved with FormBorderStyle.None
            new ResizeForBorderlessForm(this).AllowResizeAll = false;
        }

        private bool checkIPandPort(string ip, string port)
        {
            //Check the ip and port is in valid format
            if (Regex.IsMatch(ip, @"^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$") 
                && Regex.IsMatch(port, "^[0-9]{1,6}$"))
            {
                string[] temp = ip.Split('.');
                foreach (string q in temp)
                {
                    try
                    {
                        if (Int32.Parse(q) > 255) return false;
                    }
                    catch (Exception) { return false; }
                }
                return true;
            }
            return false;
        }

        private void ConnectAsServer(string ip, int port)
        {
            con = new SocketManagement(ip, port);
            if (con.StartAsServer()) GameStart();
        }
        private void ConnectAsClient(string ip, int port)
        {
            con = new SocketManagement(ip, port);
            if (con.StartAsClient()) GameStart();
        }

        private void EnableAll()
        {
            IpBox.Enabled = true;
            PortBox.Enabled = true;
            StartAsClientBtn.Enabled = true;
            StartAsServerBtn.Enabled = true;
        }
        private void DisableAll()
        {
            IpBox.Enabled = false;
            PortBox.Enabled = false;
            StartAsClientBtn.Enabled = false;
            StartAsServerBtn.Enabled = false;
        }

        private void GameStart()
        {
            this.Hide();
            new GameForm(this, isServer, con).Show();
        }
        private void StartAsServerBtn_Click(object sender, EventArgs e)
        {
            DisableAll();
            if (checkIPandPort(IpBox.Text, PortBox.Text))
            {
                isServer = true;
                ConnectAsServer(IpBox.Text, Int32.Parse(PortBox.Text));
            }
            else EnableAll();
        }

        private void StartAsClientBtn_Click(object sender, EventArgs e)
        {
            DisableAll();
            if (checkIPandPort(IpBox.Text, PortBox.Text))
            {
                ConnectAsClient(IpBox.Text, Int32.Parse(PortBox.Text));
            }
            else EnableAll();
        }

        private void button3_Click(object sender, EventArgs e)
        {
            Environment.Exit(0);
        }
    }
}

Description:
  • When Form Loaded we add the form into ResizeForBorderlessForm class to allow them to move but not to resized.
  • After that the program will wait for user input, both IP and Port for either server or client.
  • If user press either server or client button then the program will validate the IP and the Port, if any of them is wrong, then the program will notify and asking for new IP and Port, then if the IP and Port already right.
  • When the IP and the Port right, the program will create either server or client based on what button the user click, and user won't able to change IP, change Port, Start as Server or Start as Client.
  • When the program be a server, the program will waiting for the client to connect.
  • When the program be a client, the program will try to connecting to the server.
  • If connected then the program will start a new form for the game, and hide the main form.
Note: For the Connection will be described below.


SocketManagement Class Code

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Net.Sockets;
using System.Net;

namespace DvTicTacToe
{
    public class SocketManagement
    {
        private IPAddress _IP;
        private int _PORT;
        private TcpListener _TCP;
        private TcpClient _CLIENT;
        private NetworkStream _STREAM;

        public SocketManagement(String ip, int port) {
            _IP = IPAddress.Parse(ip);//parse string into IPAddress
            _PORT = port;
        }

        public bool StartAsServer() 
        {
            try
            {
                _TCP = new TcpListener(_IP, _PORT);
                _TCP.Start();
                _CLIENT = GetTcpClient();
                _STREAM = _CLIENT.GetStream();
            }
            catch (Exception ex) { System.Windows.Forms.MessageBox.Show(ex.Message); return false; }
            return true;
        }

        public TcpClient GetTcpClient() {
            return _TCP.AcceptTcpClient();
        }

        public bool StartAsClient() 
        {
            try 
            {
                _CLIENT = new TcpClient();
                _CLIENT.Connect(_IP, _PORT);
                _STREAM = _CLIENT.GetStream();
            }
            catch (Exception ex) { System.Windows.Forms.MessageBox.Show(ex.Message); return false; }
            return true;
        }

        public bool sendBoard(int[][] obj) {
            try
            {
                string temp = "";
                for (int y = 0; y < 3; y++)
                    for (int x = 0; x < 3; x++)
                        temp += obj[y][x];

                byte[] bytes = new byte[255];
                bytes = new ASCIIEncoding().GetBytes(temp);
                _STREAM.Write(bytes, 0, bytes.Length);
            }
            catch (Exception ex) { System.Windows.Forms.MessageBox.Show(ex.Message); return false; }
            return true;
        }

        public int[][] getBoard() {

            byte[] bytes = new byte[255];
            _STREAM.Read(bytes,0,bytes.Length);
            string temp = new ASCIIEncoding().GetString(bytes);
            char[] charOfTemp = temp.ToCharArray();
            int[][] obj = { new int[] { 0, 0, 0 }, new int[] { 0, 0, 0 }, new int[] { 0, 0, 0 } };
            for (int y = 0; y < 3; y++)
                for (int x = 0; x < 3; x++)
                    obj[y][x] = Int32.Parse(""+charOfTemp[(y*3)+x]);
            return obj;
        }
    }
}

Description:
  • When the class created, it will ask for IP and Port.
  • When StartAsServer was called:
    • Create the TCPListener and start it.
    • The TCPListener object call AcceptClient() to wait and listen for the TCPClient and store them in TCPClient object.
    • If the client has connected then the program will create the stream between them and store it.
  • When StartAsClient was called:
    • Create the TCPClient and tries to connect to the server.
    • If the connection has been established then the program will create the stream between them and store it.
  • When sendBoard was called, the program simply parse the array and send to others via stream in byte.
  • When getBoard was called, the program will simply wait the data sent from server and parse it from byte into array.

ResizeForBorderlessForm Class Code

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Drawing;

namespace DvTicTacToe
{
    class ResizeForBorderlessForm
    {
        enum Stats
        {
            None,
            ResizeNS,
            ResizeWE,
            ResizeNSWE,
            Move
        }

        private Form target;
        private Stats current = Stats.None;
        private int diff_x, diff_y;
        public Boolean AllowMove = true, AllowResizeAll = true, AllowResizeNS = false, AllowResizeWE = false;

        public ResizeForBorderlessForm(Form source)
        {
            this.target = source;
            if(source.FormBorderStyle != FormBorderStyle.None)source.FormBorderStyle = FormBorderStyle.None;
            target.MouseMove += new MouseEventHandler(target_MouseMove);
            target.MouseDown += new MouseEventHandler(target_MouseDown);
            target.MouseUp += new MouseEventHandler(target_MouseUp);
        }

        void target_MouseUp(object sender, MouseEventArgs e)
        {
            current = Stats.None;
        }

        void target_MouseDown(object sender, MouseEventArgs e)
        {
            if (target.WindowState != FormWindowState.Maximized)
            {
                diff_x = Cursor.Position.X - target.Location.X;
                diff_y = Cursor.Position.Y - target.Location.Y;
                if (e.X > target.Width - 5 && e.Y > target.Height - 5) current = Stats.ResizeNSWE;
                else if (e.X > target.Width - 5) current = Stats.ResizeWE;
                else if (e.Y > target.Height - 5) current = Stats.ResizeNS;
                else current = Stats.Move;
            }
        }

        void target_MouseMove(object sender, MouseEventArgs e)
        {
            if (target.WindowState != FormWindowState.Maximized)
            {
                if (e.X > target.Width - 5 && e.Y > target.Height - 5)
                {
                    if (AllowResizeAll || (AllowResizeNS && AllowResizeWE)) Cursor.Current = Cursors.SizeNWSE;
                }
                else if (e.X > target.Width - 5)
                {
                    if (AllowResizeAll || AllowResizeWE) Cursor.Current = Cursors.SizeWE;
                }
                else if (e.Y > target.Height - 5)
                {
                    if (AllowResizeAll || AllowResizeNS) Cursor.Current = Cursors.SizeNS;
                }
                else Cursor.Current = Cursors.Arrow;

                switch (current)
                {
                    case Stats.Move:
                        if (AllowMove) target.Location = new Point(Cursor.Position.X - diff_x, Cursor.Position.Y - diff_y);
                        break;

                    case Stats.ResizeNSWE:
                        if (AllowResizeAll || (AllowResizeNS && AllowResizeWE)) target.Size = new Size(Cursor.Position.X - target.Location.X, Cursor.Position.Y - target.Location.Y);
                        break;

                    case Stats.ResizeNS:
                        if (AllowResizeAll || AllowResizeNS) target.Size = new Size(target.Width, Cursor.Position.Y - target.Location.Y);
                        break;

                    case Stats.ResizeWE:
                        if (AllowResizeAll || AllowResizeWE) target.Size = new Size(Cursor.Position.X - target.Location.X, target.Height);
                        break;
                }
            }
        }


    }
}

Description: this class is created to allow FormBorderStyle to move and to be resized.

Explanation

Now that you have seen the code above, you should understand a little bit. This part will only explain for the Main Form, ResizeForBorderlessForm and SocketManagement only, the game in GameForm will be explained in Part 2 of the Tutorial.

The simple concept of the program is to allow user to create the server or became a client, in this case server is same as host, and client is same as join the host, The server will be created normally based on their own IP, note that the 2 computers must connected to 1 network and can access each other.

ċ
DvTicTacToe.zip
(122k)
Davin Timothy Ivander,
Feb 25, 2013, 6:26 PM