Mail to Keith Dan
keith的天空
海阔凭鱼跃,天高任鸟飞
最近时间有点忙,本来象棋游戏是有一个网络版本的,但是由于最近时间比较忙,一直没发出来.
最近有一些朋友希望看到网络版象棋,本来这个是学生将做的项目,这里我把这个DEMO给大家,相互学习.

网络象棋,不得不说一下TCP/IP协议,这其实是两个协议,即tcp协议和ip协议。
所谓IP协议,IP协议是在网络层的协议.它主要完成数据包的发送作用。
所谓TCP协议,TCP协议也是建立在IP协议之上的,不过TCP协议是可靠的.按照顺序发送的。
所以正是由于他们的特性,使得他们一起可以使我们网络数据传输可靠。一般我们常说的TCP/IP“三次握手”,其实也就是第一步客户机向服务器发送一个TCP数据包,表示请求建立连接,第二步服务器接收到请求数据包以后再向客户端发送一次响应数据包,第三步客户机接收到以后再想服务器发送一次确认数据包。服务器接受到以后,我们的连接也就成功。

那么socket(套接字)呢?其实也就是两台机器的远程节点,它的操作也包括,打开,关闭,读写等等。
具体的呢,大家可以去看一下关于TCP/IP的书,这里我就不予累赘。

这里呢,本来最早是用了一套我封装好的socket库来做连接和数据传输,后来由于一些原因要把把项目难度减小,所以就用了.net标准的TcpListener和TcpClient来做连接。

这里呢主要是采用原来单机版象棋进行功能扩展的,其实所谓网络版本,也就是将原本在电脑的输入操作换做网络数据输入,所以其他算法和设计都基本一样。这里我讲一下具体实现的网络部分
首先需要创建网络服务器和客户机的登陆部分
 
分为服务器和客户机登陆以后:
客户端:
                TcpClient client = new TcpClient();
                IPAddress ip 
= IPAddress.Parse(this.tbIP.Text);
                
int port = Convert.ToInt32( tbPort.Text);
                client.Connect(ip,port);
                Flag.PlayerType 
= Enums.ChessType.red;
                TcpTransfer tranfer 
= new TcpTransfer(client);

服务器则是需要建立监听:
int port = Convert.ToInt32( tbPort.Text);
                TcpListener listener 
= new TcpListener(port);
                listener.Start();
                TcpClient client 
= listener.AcceptTcpClient();
                Flag.PlayerType 
= Enums.ChessType.blue;
                TcpTransfer tranfer 
= new TcpTransfer(client);

这里数据传输呢,封装在TcpTransfer类中
/// <summary>
    
/// tcp传输控制类
    
/// </summary>

    public class TcpTransfer 
    
{
        
/// <summary>
        
/// 接收信息事件的委托
        
/// </summary>

        public delegate void ReceiveMsgHandle(string msg); 
        
/// <summary>
        
/// 接收信息事件
        
/// </summary>

        public event ReceiveMsgHandle ReceiveMsg;
        
/// <summary>
        
/// 接收信息事件激活方法
        
/// </summary>
        
/// <param name="msg"></param>

        private void OnReceive(string msg)
        
{
            
if(ReceiveMsg !=null)
                ReceiveMsg(msg);
        }


        
private TcpClient _client;
        
public TcpTransfer(TcpClient client)
        
{
            _client 
= client;
        }


        
/// <summary>
        
/// 发送信息
        
/// </summary>
        
/// <param name="msg"></param>

        public void Send(string msg)
        
{
            
//获取网络流
            NetworkStream ns = _client.GetStream();
            
//将要发送的字符串转换为字节数组
            byte [] bytes = System.Text.Encoding.Default.GetBytes( msg );
            
//写入网络流
            ns.Write( bytes,0,bytes.Length );
            ns.Flush();
        }


        
/// <summary>
        
/// 接收信息
        
/// </summary>

        public void Receive()
        
{
            
//获取网络流
            NetworkStream ns = _client.GetStream();
            
while(true)
            
{
                
byte [] bytes = new byte[512];
                
int size = 0;
                
                
try
                
{
                    
//刷新流
                    ns.Flush();
                    
//从网络流里读取信息到byte[]
                    size = ns.Read(bytes,0,bytes.Length);
                }

                
catch
                
{
                    System.Windows.Forms.MessageBox.Show(
"tcp连接出现故障!程序异常的退出!");
                    System.Windows.Forms.Application.Exit();
                }

                
                
/*
                 * 由于socket连接后作为网络传输的会有很多异常
                 * 在这里我们只是简单的做了一点处理,当然,在实际开发中socket异常的处理绝不仅仅如此简单
                 * 如果断开连接,有可能会出现一直接收0个字节问题
                 * 如果接收到0个字节,则退出循环
                 * 
*/

                
if(size<=0break;

                
//将接收到的字节转换为字符串
                string msg = System.Text.Encoding.Default.GetString(bytes);

                OnReceive( msg );

                
//当前线程休眠100毫秒
                System.Threading.Thread.Sleep(100);

            }

        }

    }

这里呢,大家注意一下,由于我们的象棋作用是在子线程上,而窗体呢是在主线程上,如果我们用子线程去改变主线程的窗体,则会带来异常或者线程的阻塞。于是,定义了三个委托来处理窗体改变(棋子位置变换问题)问题。
/// <summary>
        
/// 选中操作的异步委托
        
/// </summary>

        private delegate bool CheckHandle(IChessItem ic);
        
/// <summary>
        
/// 移动操作的异步委托
        
/// </summary>

        private delegate bool MoveToHandle(int x,int y);
        
/// <summary>
        
/// 吃棋子的异步委托
        
/// </summary>

        private delegate bool EatHandle(IChessItem ic);
在我们TCP事件里面,则采用invoke异步的模式进行委托
        /// <summary>
        
/// tcp接受信息的事件响应方法
        
/// </summary>
        
/// <param name="msg">tcp接收的信息</param>

        private void _tranfer_ReceiveMsg(string msg)
        
{
            
/*
             * 1。拆分接收到的信息,判断包头的命令
             * 2。由于当前接收到的信息线程作用在子线程(receive)上,我们将改变主线程上的窗体信息,
             * 此时会产生主线程阻塞,此时必须使用窗体的异步调用,使用invoke来对窗体改变
             * 3。这里涉及到委托传入参数问题
             * 
*/

            
string [] args = msg.Split('|');
            
if(args[0== "check")
            
{
                IChessItem ic 
= GetItemOnPoint( Convert.ToInt32(args[1]),Convert.ToInt32(args[2]));
            
object [] os  = {ic};
                
this.Invoke(new CheckHandle(Check),os);
            }


         }

}

其余的呢和前面单机版本差不多,这里就不再多讲了
这里呢有一些地方,如socket传输等,实际的项目里面异常和其他的判断处理远远比这复杂,我这里只是简单的做一点处理,呵呵,本来也只是教学用的^_^

Source  Demo
posted on 2007-07-08 15:29  KeithDan  阅读(3932)  评论(7编辑  收藏  举报