版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/sinat_24994943/article/details/56044565
博主主要是用了socket的方法去实现了服务器和客户端之间的通信,由于楼主对于socket网络编程这一方面的基础相当薄弱,故也是边学边学,有些地方的代码不够完善还请大神指正!博主还需学习。
首先,先说说服务端方面:服务端采用的是VS的windows form窗体应用程序做的,也是为了方便查看和操作吧。
窗体长这样
首先先声明一个公共类
public class Common
{
/// <summary>
/// 保存服务器来的消息
/// </summary>
public static byte[] ReceiveBuffer = new byte[1024 * 1024];
/// <summary>
/// 监听用的socket
/// </summary>
public static Socket ListenSocket;
/// <summary>
/// 保存所有负责通信用是socket
/// </summary>
public static Dictionary<string, Socket> connSocket = new Dictionary<string, Socket>();
}
服务端首先要开启服务,点击“启动服务器”按钮,则可以从两个TextBox中读取输入的IP地址和端口号的信息,通过这个信息开启服务
void StartServer()
{
try
{
string _ip = tbIp.Text;
int _point = int.Parse(tbPoint.Text);
//创建监听客户端请求的socket
Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
//监听用的ip和端口
IPAddress address = IPAddress.Parse(_ip);
IPEndPoint point = new IPEndPoint(address, _point);
//绑定
socket.Bind(point);
socket.Listen(10);
//异步 开始监听
socket.BeginAccept(new AsyncCallback(Listen), socket);
//禁用当前按钮
btnStart.Enabled = false;
//启动时间
startTime.Text = DateTime.Now.ToString();
//底部提示消息
tssMsg.Text = "服务器已经启动";
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
开启服务后,异步实现监听和接收消息,以保证服务器不会阻塞。
通过接收到的消息的头一个字节——协议位,来判断客户端发送的消息是要做什么的
void Listen(IAsyncResult result)
{
try
{
//获取监听的socket
Socket clientSocket = result.AsyncState as Socket;
//与服务器通信的socket
Socket connSocket = clientSocket.EndAccept(result);
string ip = connSocket.RemoteEndPoint.ToString();
//连接成功。保存信息
if (!Common.connSocket.ContainsKey(ip))
Common.connSocket.Add(ip, connSocket);
//连接成功,更新服务器信息
changeList(connSocket);
//等待新的客户端连接 ,相当于循环调用
clientSocket.BeginAccept(new AsyncCallback(Listen), clientSocket);
//接收来自客户端信息 ,相当于循环调用
connSocket.BeginReceive(Common.ReceiveBuffer, 0, Common.ReceiveBuffer.Length, 0, new AsyncCallback(Receive), connSocket);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
void Receive(IAsyncResult result)
{
//与客户端通信的socket
Socket clientSocket = result.AsyncState as Socket;
string ip = clientSocket.RemoteEndPoint.ToString();
try
{
//获取实际的长度值
int num = clientSocket.EndReceive(result);
if (num > 0)
{
byte[] data = new byte[num];
//复制实际的长度到data字节数组中
Array.Copy(Common.ReceiveBuffer, 0, data, 0, num);
//判断协议位
int command = data[0];
switch (command)
{
case 1:
//服务器保存发送方的IP和其棋子的颜色,并寻求配对
MatchingRival(data,ip);
break;
case 2:
//接收发送方发来的移动棋子的消息,并转发给接收方
SendMoveMessageToClient(data);
break;
case 3:
//接收发送方的悔棋消息,并转发给接收方
SendRetractMessageToClient(data);
break;
case 6:
//接收到发送方想要确定接收方是否在线的意愿,并做检测
SendDisconnectMessageToClient(data);
break;
default:
break;
}
//接收其他信息
clientSocket.BeginReceive(Common.ReceiveBuffer, 0, Common.ReceiveBuffer.Length, 0, new AsyncCallback(Receive), clientSocket);
}
else //客户端断开
{
clientOff(clientSocket);
}
}
catch (Exception ex)
{
clientOff(clientSocket);
}
}
在介绍每一个从客户端接收的消息是干什么的之前(下一篇再整理),我们要先了解一下changeList(更新列表)和changOnlineCount(更新在线人数)这两个函数
void changeList(Socket socket)
{
//获取客户端信息 ip和端口号
string ip = socket.RemoteEndPoint.ToString();
//客户端登陆时间
string time = DateTime.Now.ToString();
//跨线程操作ui
this.Invoke(new Action(() =>
{
//新增一行
dgvList.Rows.Add();
//获取当前dgvList的行
int rows = dgvList.Rows.Count;
//赋值
dgvList.Rows[rows - 1].Cells[0].Value = ip;
dgvList.Rows[rows - 1].Cells[1].Value = time;
//把ip当作当前行的tag标记一下,为了删除行的时候可以找到该行
dgvList.Rows[rows - 1].Tag = ip;
//更新在线人数
changOnlineCount(true);
}));
}
void changOnlineCount(bool tag)
{
int num = 0;
if (tag) num = int.Parse(lbCount.Text) + 1;
else num = int.Parse(lbCount.Text) - 1;
this.Invoke(new Action(() =>
{
//更新在线人数
lbCount.Text = num.ToString();
if (num == 0) Common.connSocket.Clear();
}));
}
当有客户端连接的时候,首先会执行一次changeList函数,将所连接的客户端的信息显示在DataGridView里面,并在“在线人数”处+1
同样的,当有客户端断开的时候,也会更新列表,“在线人数”-1,删除在DataGridView里面的信息,并关闭该连接
void clientOff(Socket clientSocket)
{
//从集合删除下线的ip
string outIp = clientSocket.RemoteEndPoint.ToString();
if (Common.connSocket.ContainsKey(outIp))
Common.connSocket.Remove(outIp);
//更新服务器在线人数
changOnlineCount(false);
this.Invoke(new Action(() =>
{
//更新列表
//删除退出的ip
for (int i = 0; i < dgvList.Rows.Count; i++)
{
if (dgvList.Rows[i].Tag.ToString() == outIp)
{
dgvList.Rows.RemoveAt(i);
break;
}
}
}));
clientSocket.Shutdown(SocketShutdown.Receive);
clientSocket.Close();
}