x01.Weiqi.3 网络对弈
为简化,只实现了两个用户的网络对弈。但在此基础上,即使去实现一个具有商业价值的网络对弈程序,应该也不是什么难事。毕竟,核心的问题已经解决了。
首先,是服务端。新建一个 WinForm 项目 Server。在主窗口中,添加一个按钮m_ButtonStartServer,双击,在 click 事件中,实现TcpListener 的诸多功能。
主窗口文件 MainForm.cs 内容如下:
MainForm
namespace Server
{
publicpartialclass MainForm : Form
{
TcpListener m_TcpListener;
List<User> m_Users =new List<User>(); //为简化,只作 2 想
public MainForm()
{
InitializeComponent();
}
privatevoid ButtonStartServer_Click(object sender, EventArgs e)
{
IPAddress ip = Dns.GetHostAddresses(Dns.GetHostName())[0];
m_TcpListener =new TcpListener(ip, 51000);
m_TcpListener.Start();
ThreadStart threadStart =new ThreadStart(ListenClient);
Thread thread =new Thread(threadStart);
thread.Start();
MessageBox.Show("Start server success!");
}
void ListenClient()
{
while (true)
{
TcpClient client =null;
try
{
client = m_TcpListener.AcceptTcpClient();
}
catch
{
break; // 以此退出?
}
User user =new User(client);
m_Users.Add(user);
ParameterizedThreadStart ptStart =new ParameterizedThreadStart(ReceiveData);
Thread thread =new Thread(ptStart);
thread.Start(user);
}
}
void ReceiveData(object u)
{
User user = (User)u;
while (true)
{
string data = user.Reader.ReadLine();
for (int i =0; i <2; i++)
{
string color = i %2==0?"black" : "white";
m_Users[i].Writer.WriteLine(string.Format("{0},{1}", color, data));
m_Users[i].Writer.Flush();
}
}
}
protectedoverridevoid OnClosing(CancelEventArgs e)
{
base.OnClosing(e);
foreach (var item in m_Users)
{
item.Client.Close();
}
m_TcpListener.Stop();
}
}
}
{
publicpartialclass MainForm : Form
{
TcpListener m_TcpListener;
List<User> m_Users =new List<User>(); //为简化,只作 2 想
public MainForm()
{
InitializeComponent();
}
privatevoid ButtonStartServer_Click(object sender, EventArgs e)
{
IPAddress ip = Dns.GetHostAddresses(Dns.GetHostName())[0];
m_TcpListener =new TcpListener(ip, 51000);
m_TcpListener.Start();
ThreadStart threadStart =new ThreadStart(ListenClient);
Thread thread =new Thread(threadStart);
thread.Start();
MessageBox.Show("Start server success!");
}
void ListenClient()
{
while (true)
{
TcpClient client =null;
try
{
client = m_TcpListener.AcceptTcpClient();
}
catch
{
break; // 以此退出?
}
User user =new User(client);
m_Users.Add(user);
ParameterizedThreadStart ptStart =new ParameterizedThreadStart(ReceiveData);
Thread thread =new Thread(ptStart);
thread.Start(user);
}
}
void ReceiveData(object u)
{
User user = (User)u;
while (true)
{
string data = user.Reader.ReadLine();
for (int i =0; i <2; i++)
{
string color = i %2==0?"black" : "white";
m_Users[i].Writer.WriteLine(string.Format("{0},{1}", color, data));
m_Users[i].Writer.Flush();
}
}
}
protectedoverridevoid OnClosing(CancelEventArgs e)
{
base.OnClosing(e);
foreach (var item in m_Users)
{
item.Client.Close();
}
m_TcpListener.Stop();
}
}
}
其中,User 是为方便用户管理而添加的类。
代码文件 User.cs 内容如下:
User
namespace Server
{
class User
{
public TcpClient Client { get; privateset; }
public StreamReader Reader { get; privateset; }
public StreamWriter Writer { get; privateset; }
public User(TcpClient client)
{
this.Client = client;
NetworkStream netStream = client.GetStream();
Reader =new StreamReader(netStream, System.Text.Encoding.UTF8);
Writer =new StreamWriter(netStream, System.Text.Encoding.UTF8);
}
}
}
{
class User
{
public TcpClient Client { get; privateset; }
public StreamReader Reader { get; privateset; }
public StreamWriter Writer { get; privateset; }
public User(TcpClient client)
{
this.Client = client;
NetworkStream netStream = client.GetStream();
Reader =new StreamReader(netStream, System.Text.Encoding.UTF8);
Writer =new StreamWriter(netStream, System.Text.Encoding.UTF8);
}
}
}
如此,服务端就算完成。
客户端,直接从 BoardBase 继承,类名为:InetBoard。
代码文件 InetBoard.cs 内容如下:
InetBoard
namespace x01.Weiqi.Board
{
publicclass InetBoard : BoardBase
{
int m_Col =-1, m_Row =-1, m_StepCount =-1;
string m_Color =string.Empty;
TcpClient m_Client =null;
StreamWriter m_Writer =null;
StreamReader m_Reader =null;
int m_Count =0;
System.Windows.Forms.Timer m_Timer =new System.Windows.Forms.Timer();
public InetBoard(int size =38)
: base(size)
{
m_Timer.Tick +=new EventHandler(Timer_Tick);
m_Timer.Start();
InitClient();
}
void Timer_Tick(object sender, EventArgs e)
{
Draw();
}
void Draw()
{
int col = m_Col;
int row = m_Row;
if (NotIn(col, row))
{
return;
}
DrawChess(col, row);
Eat(col, row);
m_Count++;
}
void InitClient()
{
// 测试,取主机即可。实际,应为服务器域名
m_Client =new TcpClient(Dns.GetHostName(), 51000);
m_Writer =new StreamWriter(m_Client.GetStream(), Encoding.UTF8);
m_Reader =new StreamReader(m_Client.GetStream(), Encoding.UTF8);
Thread thread =new Thread(new ThreadStart(Receive));
thread.Start();
}
void Receive()
{
while (true)
{
string data = m_Reader.ReadLine();
string[] step = data.Split(',');
m_Color = step[0];
m_Col =int.Parse(step[1]);
m_Row =int.Parse(step[2]);
m_StepCount =int.Parse(step[3]);
}
}
void Send(string data)
{
m_Writer.WriteLine(data);
m_Writer.Flush();
}
protectedoverridevoid OnMouseLeftButtonDown(System.Windows.Input.MouseButtonEventArgs e)
{
base.OnMouseLeftButtonDown(e);
if (m_Color =="black"&& IsBlack !=false)
{
return;
}
if (m_Color =="white"&& IsBlack ==false)
{
return;
}
int col = (int)e.GetPosition(this).X / ChessSize;
int row = (int)e.GetPosition(this).Y / ChessSize;
string data =string.Format("{0},{1},{2}", col, row, m_Count +1);
Send(data);
}
}
}
{
publicclass InetBoard : BoardBase
{
int m_Col =-1, m_Row =-1, m_StepCount =-1;
string m_Color =string.Empty;
TcpClient m_Client =null;
StreamWriter m_Writer =null;
StreamReader m_Reader =null;
int m_Count =0;
System.Windows.Forms.Timer m_Timer =new System.Windows.Forms.Timer();
public InetBoard(int size =38)
: base(size)
{
m_Timer.Tick +=new EventHandler(Timer_Tick);
m_Timer.Start();
InitClient();
}
void Timer_Tick(object sender, EventArgs e)
{
Draw();
}
void Draw()
{
int col = m_Col;
int row = m_Row;
if (NotIn(col, row))
{
return;
}
DrawChess(col, row);
Eat(col, row);
m_Count++;
}
void InitClient()
{
// 测试,取主机即可。实际,应为服务器域名
m_Client =new TcpClient(Dns.GetHostName(), 51000);
m_Writer =new StreamWriter(m_Client.GetStream(), Encoding.UTF8);
m_Reader =new StreamReader(m_Client.GetStream(), Encoding.UTF8);
Thread thread =new Thread(new ThreadStart(Receive));
thread.Start();
}
void Receive()
{
while (true)
{
string data = m_Reader.ReadLine();
string[] step = data.Split(',');
m_Color = step[0];
m_Col =int.Parse(step[1]);
m_Row =int.Parse(step[2]);
m_StepCount =int.Parse(step[3]);
}
}
void Send(string data)
{
m_Writer.WriteLine(data);
m_Writer.Flush();
}
protectedoverridevoid OnMouseLeftButtonDown(System.Windows.Input.MouseButtonEventArgs e)
{
base.OnMouseLeftButtonDown(e);
if (m_Color =="black"&& IsBlack !=false)
{
return;
}
if (m_Color =="white"&& IsBlack ==false)
{
return;
}
int col = (int)e.GetPosition(this).X / ChessSize;
int row = (int)e.GetPosition(this).Y / ChessSize;
string data =string.Format("{0},{1},{2}", col, row, m_Count +1);
Send(data);
}
}
}
用户控件 Chess 只能在 STA 中处理,故添加了一个 Timer。逻辑不复杂,不详叙。
用法:
1. 运行Server,效果图如下:
2. 启动两个 x01.Weiqi,从菜单中选择 Inet 项。用户1和2的运行效果图如下:
源代码可从 http://www.cnblogs.com/china_x01 的 download\code\x01.Weiqi 获取。
Copyright (c) 2011 by x01(china_x01@qq.com),未经许可,请勿转载。