网络中国象棋小游戏的实现

      开学了,去图书馆借了几本书,没有找到想要的C++网络编程,却是找到了几本LINUX的书,以及一本《Visual C#经典游戏编程开发》。翻了翻发现里面有个能够联网对弈的中国象棋游戏,一直写ASP.NET的网页,也想试着写写Winform的图形程序了,并且能够了解一下.NET的网络编程相关的内容。编程

      象棋方面,先从网上找了张棋盘图片,在PS里把棋子一一扣了出来做为素材。编写对应的棋子类和棋盘类,前者实现棋子身份的识别和棋子图片的绘制,后者实现棋盘绘制、走棋规则、棋谱生成等功能。c#

      网络通讯的协议使用的是UDP协议,发送的信息规则是:“命令|参数1|参数2……”,主要命令:join表示用户想要联机,处于接受对方链接的状态;conn表示收到对方联机命令,已准备好开始游戏,对方能够开局了;new表示其中一方提出从新开始游戏;move|id|x|y表示移动棋盘上编号为id的棋子到x,y处;succ|赢方代号 表示此局已分出胜负;exit表示退出游戏。网络

      游戏开始后建立了一个线程,在read方法中建立UdpClient对象,循环侦听指定端口中由指定IP主机传来的信息。主要代码是:socket

udpClient = new UdpClient(Convert.ToInt32(txt_port.Text));
int id, x, y;
while(readFlag == true)
{
	try
	{
		byte[] data = udpClient.Receive(ref remote);
		string strData = Encoding.UTF8.GetString(data);
		string[] a = strData.Split('|');		// 分割命令与参数
		switch(a[0])							// 判断命令
		{
			case "join":
			case "conn":
			//......
		}
	}
	catch
	{
		break;
	}
}

      游戏过程当中发送数据则是经过send方法,建立UdpClient网络服务对象,向指定IP的主机发送消息到设定的端口号。完成后发送后,关闭UDP网络服务。主要代码:spa

UdpClient sendUdp = new UdpClient();
UPAddress remoteIP;
try {
	remoteIP = IPAddress.Parse(txt_IP.Text);
}
catch {
	MessageBox.Show("请输入正确的IP地址!", "错误");
	return;
}
remote = new IPEndPoint(remoteIP, Convert.ToInt32(txt_remoteport.Text));
byte[] buffer = Encoding.UTF8.GetBytes(str);
sendUdp.Send(buffer, buffer.Length, remote);
sendUdp.Close();

      原书中的代码有一些小问题,因此作了点改动和简化。不过基本的思路仍是和原书上是同样的。线程

      通讯过程方面,每次联机都须要双方填写好对方的IP、端口号以及本身的端口号。并且,因为没有明显的游戏过程标记,因此形成了一些通讯问题,致使在不正确的阶段收到对方错误的请求时游戏出现意外的状况。3d

      因而在完成后,就开始了对原程序的改造。考虑到内网用户链接外网的问题,就将游戏中的UDP协议通讯改成了使用Socket经过TCP协议进行游戏通讯。添加游戏状态state字段,包括如下几个主要状态:code

        public const short WAITING_CLIENT = 1;  // 做为主机,等待客户端链接
        public const short SERVING = 2;         // 做为主机,客户端已链接,游戏中
        public const short WAITING_SERVER = 3;  // 做为从机,等待主机接受链接
        public const short CONNECTING = 4;      // 做为从机,链接到主机,游戏中

      游戏的命令和监听流程也作了对应的改动。orm

      主机创建游戏等待客户端链接的过程主要包括:监听端口、创建链接、开始监听消息,这三个步骤。首先建立线程,在线程中执行acceptClientConnect监听端口等待链接,检测到链接后创建链接,转入Read监听阶段。对象

        private void WaitClient()
        {
            // 启动一个线程来接受请求
            thread = new Thread(acceptClientConnect);
            thread.Start();
            SetState(WAITING_CLIENT);
        }

        // 接受请求
        private void acceptClientConnect()
        {
            IPEndPoint localEndPoint = new IPEndPoint(IPAddress.Any, DEFAULT_PORT);
            listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            toolStripStatusLabel1.Text = "正在等待其余玩家链接……";
            try
            {
                if (timeoutThread != null)
                {
                    timeoutThread.Abort();
                }
                timeoutThread = new Thread(WaiteTimeOut);
                timeoutThread.Start();
                listener.Bind(localEndPoint);
                listener.Listen(1);
                connection = listener.Accept();
                listener.Close();
                listener = null;
                Read();
            }
            catch
            {
                toolStripStatusLabel1.Text = "链接中断,游戏终止。";
                SetState(FREE);
            }
        }

        private void WaiteTimeOut()
        {
            Thread.Sleep(30000);
            if (connection == null || !connection.Connected)
            {
                if (listener != null)
                {
                    SetState(FREE);
                    toolStripStatusLabel1.Text = "链接超时,无玩家链接游戏。";
                }
            }
            timeoutThread = null;
        }

      预约的监听超时时间是30秒,30秒后没有链接时,转到FREE状态并终止监听。终止的实现思路是在主机自己建立一个TCP协议的Socket对象,向自身端口发送终止链接的消息,listener创建链接后在Read中接受到此消息中止阻塞,结束游戏完全转为FREE状态,线程结束。

        private void SetState(int newState)     // 支线程
        {
            state = newState;
            switch (state)
            {
                case FREE:
                    btnCreate.Text = "建立游戏";
                    btnCreate.Enabled = true;
                    btnJoin.Text = "加入游戏";
                    btnJoin.Enabled = true;
                    if (listener != null)
                    {
                        Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                        socket.Connect(IPAddress.Parse("127.0.0.1"), DEFAULT_PORT);
                        if (socket.Connected)
                        {
                            socket.Send(Encoding.UTF8.GetBytes("end"));
                        }
                    }
                    if (connection != null)
                    {
                        connection.Close();
                        connection = null;
                    }
                    break;
                case WAITING_CLIENT:
                    btnCreate.Text = "关闭游戏";
                    btnJoin.Enabled = false;
                    break;
                case SERVING:
                    break;
                case WAITING_SERVER:
                    btnJoin.Text = "断开游戏";
                    btnCreate.Enabled = false;
                    break;
                case CONNECTING:
                    break;
            }
        }

      接受消息的Read方法和发送消息的Send方法方面,本来的UdpClient收发消息改成经过建立的connection(Socket)链接来通讯。

        private void Read()
        {
            int t, x1, y1, x2, y2;
            while (state != FREE)
            {
                try
                {
                    byte[] data = new byte[1024];
                    int len = connection.Receive(data);
                    string msg = Encoding.UTF8.GetString(data);
                    string[] a = msg.Split('|');
                    switch (a[0])
                    {
                        case "end": // 终止listener的监听
                            if (state != WAITING_CLIENT) continue;
                            connection.Close();
                            connection = null;
                            SetState(FREE);
                            return;
                        case "join": // 客户端请求加入游戏
                            break;
                        case "acce": // 服务端接受加入的请求
                            break;
                        case "exit": // 对方方退出游戏
                            break;
                        case "new":  // 对方方提出从新开局
                            break;
                        case "succ": // 一方获胜
                            break;
                        case "lose": // 一方认输
                            break;
                        case "move": // 对方移动棋子
                            break;
                    }
                }
                catch
                {
                    break;
                }
            }
        }

        private void Send(string msg)
        {
            if (connection != null)
            {
                byte[] buffer = Encoding.UTF8.GetBytes(msg);
                connection.Send(buffer);
            }
        }

      具体游戏效果截图:

2013-09-05_214002

2013-09-05_214006

01

02

项目工程下载:

 点我下载整个项目的压缩包(883K)

 点我下载整个项目(UDP版)的压缩包(881K)

相关文章
相关标签/搜索