记录一次C# Linq to Sql实现DataGridView实时显示与刷新TCP/UDP服务端在线人数

业务背景

最近因为业务的需求,需要实现串口的透传(简单的TCP/UDP透传),并且显示TCP/UDP实时刷新和显示连接情况或者监听情况。非常简单的功能,但是会存在一些雷点,我会讲述成功实现的做法还有我踩过的坑。

效果如下两张图。

思路

1.使用Sql Server 创建一个数据库并且分离,并且放入程序目录下,并且连接路径改为目录下。

2.建立Linq to sql类。

3.创建串口时,将数据全部插入数据库。(连接状态文字栏根据选用的网络协议进行改变)

4.插入完成后,将数据填充显示。

5.TCP/UDP监听状态下,有客户端连入,则改变连接状态,断开也更新状态。


代码

1,2,3,4步想必大家都会,就不再赘述,关键是第5步。 我们需要在监听部分进行捕捉到客户端的连接,并且及时更新数据库。

首先,我们要获取到数据库的那一条要更新的记录才能做更新操作。作为服务端,其端口是唯一的,所以我们获取到它的端口:

((IPEndPoint)serverSocket.LocalEndPoint).Port.ToString();

serverSocket:是服务端用来监听的载体。

LocalEndPoint:可以获取当前服务器的各种信息。但是不可读取。

(IPEndPoint):强制转换成IPEndPoint类型,就调用Port方法,拿到了LocalEndPoint中我们实际使用的端口号了。

之后写一个方法,用拿到的端口号去数据库中查询,并且更新:

private void ReturnendPoint(Socket socket)//根据端口号返回数据库中的对象
        {
            vspdTableDataContext db = new vspdTableDataContext();//实例化   
            var res = (from r in db.PortSettingTable
                       where r.localportnum == ((IPEndPoint)socket.LocalEndPoint).Port.ToString().Trim()//查找端口号是否一致
                       select r).FirstOrDefault();
                res.connetstate = "已经连接";
            db.SubmitChanges();
            
        }
复制代码

但是这样的话,还达不到要求,因为只是改成了 “已经连接”,但是没有具体显示连接人数。 所以我们这里新建一个全局变量来存储每个端口号对应的人数,我这里用了字典:

Dictionary<string,int>OnlineNums=new Dictionary<string,int>();

在服务端开启监听的时候,我们就加入客户端的端口号和在线人数(刚刚开始监听,肯定是0): OnlineNums.Add(PortTxt, 0);

然后再改写一下我们上面的方法,我们希望把每个端口对应的在线人数同时传输进去进行更新,像这样:

ReturnendPoint(serverSocket, OnlineNums[port]);

而具体方法就是如下:

private void ReturnendPoint(Socket socket,int index)//根据端口号返回数据库中的对象
{
            vspdTableDataContext db = new vspdTableDataContext();//实例化   
            var res = (from r in db.PortSettingTable
                       where r.localportnum == ((IPEndPoint)socket.LocalEndPoint).Port.ToString().Trim()//查找端口号是否一致
                       select r).FirstOrDefault();
            if(index>0)
                res.connetstate = $"已经连接({index})";
            else//如果小于或等于0证明没人了
                res.connetstate = "正在监听";
            db.SubmitChanges();
            
}
复制代码

然后我们在监听方法里边调用:

public void AcceptClientConnet(object socket)//接收客户端连接的方法
{
            
            var serverSocket = socket as Socket;//强制转换
            string port = ((IPEndPoint)serverSocket.LocalEndPoint).Port.ToString();//获得端口号
            while (true)
            {
                var ProxSocket = serverSocket.Accept();//用户连接
                OnlineNums[port]++;//人数+1
                ReturnendPoint(serverSocket, pairs[port]);//更新数据库
                DataBind();//重新填充,刷新datagirdview
                ThreadPool.QueueUserWorkItem(ReceiveData, ProxSocket);//为了反复连接,使用线程池
            }
}
复制代码

在客户端断线的时候也是如法炮制:

public void ReceiveData(object socket)//接收客户端消息的方法
        {
            var ProxSocket = socket as Socket;
            byte[] data = new byte[1024 * 1024];//接收消息的缓冲区
            string port = ((IPEndPoint)ProxSocket.LocalEndPoint).Port.ToString();
            while (true)
            {
                int len = 0;//记录消息长度
                try
                {
                    len = ProxSocket.Receive(data, 0, data.Length, SocketFlags.None);
                }
                catch (Exception)
                {
                    //异常退出
                    OnlineNums[port]--;//人数-1
                    ReturnendPoint(ProxSocket, OnlineNums[port]);//重新填充,刷新datagirdview
                    DataBind();
                    return;
                }
                if (len <= 0)
                {
                    //如果小于0,证明无连接,服务端正常退出
                    OnlineNums[port]--;//人数-1
                    ReturnendPoint(ProxSocket, OnlineNums[port]);//重新填充,刷新datagirdview
                    DataBind();
                    ps.StopConnet(ProxSocket);
                    return;//让方法结束,终结当前接收服务端数据的异步线程
                }
                }

            }
        }
复制代码

这样的写法有点累赘,可以写成下面的形式:

ReturnendPoint(ProxSocket, OnlineNums[++port]);

ReturnendPoint(ProxSocket, OnlineNums[--port]);

这样就已经实现效果了。


雷点

  1. 如果用了Linq to Sql去填充DataGridView,就不要试图去直接改写单元格中的Value,这样没用。虽然会能赋值到目标单元格上的Value上,但是不会显示出来。因为数据会被你原本的DataSource覆盖。
  2. Linq to Sql的DataContext不要做全局变量的实例化。如果你填充数据集的方法和更新数据集的方法是在一个类里面的话,共用一个实例会出各种各样的错误。应该在独立方法体内实例化。但如果两个方法不在一个类中,随便。(天呐,简直是弱智级别的错误啊啊啊啊啊)
  3. 记得给DataGridView做跨线程处理。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值