在实时通信应用中,最常用也最基本的就是终端列表的实时刷新,也就是当客户端上线或下线时,服务端都会向全体客户端推送通知及新的在线客户端列表。
public partial class ClientModel
{
public ClientModel()
{
}
private string _client_name;
private string _client_ip="192.168.1.1";
private string _connectionid;
//计算机名
public string ClientName
{
set { _client_name = value; }
get { return _client_name; }
}
//计算机IP
public string ClientIP
{
set { _client_ip = value; }
get { return _client_ip; }
}
//通信连接id
public string ConnectionId
{
set { _connectionid = value; }
get { return _connectionid; }
}
}
服务端创建请参考下面文章
在MyHub类中添加服务端方法。对于客户端上线建立连接,服务端方法有两种,一种是重载Hub类的OnConnected方法,另一种是就是自定义的方法了。OnConnected方法在客户端与服务端建立通信连接时会被立即调用,但我们很多时候是先建立连接等到用户验证后再登录刷新在线终端列表,也就是说两者的触发时机不同,但基本原理是一致的。
首先定义一个变量来存储在线终端信息:
public List<ClientModel> ClientList = new List<ClientModel>();
OnConnected方法
public override Task OnConnected()
{
ClientModel client = new ClientModel
{
ClientName = GetName(),//获取远程计算机名
ClientIP = GetIp(),//获取远程计算机IP
ConnectionId=Context.ConnectionId
};
foreach (ClientModel m in ClientList)
{
if (m.ClientIp==client.ClientIp)
{
ClientList.Remove(m);
break;
}
}
ClientList.Add(client);
Clients.All.getclientlist(ClientList);
string output = string.Format("用户 {0} 上线,IP:{1}", client.ClientName, client.ClientIP);
Application.Current.Dispatcher.Invoke(() =>
{
((MainWindow)Application.Current.MainWindow).WriteToConsole(output);
});
return base.OnConnected();
}
自定义方法GetClientList
public Task GetClientList(string clientname,string clientmac,string clientip)
{
ClientModel client = new ClientModel
{
ClientName = clientname,
ClientIP = clientip,
ConnectionId=Context.ConnectionId
};
foreach (Model.ClientModel m in ClientList)
{
if (m.ClientIp==clientip)
{
ClientList.Remove(m);
break;
}
}
ClientList.Add(client);
Task all = Clients.All.getclientlist(ClientList);
string output = string.Format("用户 {0} 上线,IP:{1}", client.ClientName, client.ClientIP);
Application.Current.Dispatcher.Invoke(() =>
{
((MainWindow)Application.Current.MainWindow).WriteToConsole(output);
});
return all;
}
创建WPF项目ClientApp(不低于.NET Framework 4.0),使用NuGet管理器引用SignalR程序包,只需要引用Microsoft.AspNet.SignalR.Client即可。使用设计器添加一个数据列表(DataGrid或ListView)用于绑定在线客户端列表。
public static HubConnection Connection = new HubConnection(ConfigurationManager.AppSettings["ServiceUri"]);
public static IHubProxy MyHub;
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
ConnectAsync();
}
private async void ConnectAsync()
{
MyHub = Connection.CreateHubProxy("MyHub");
try
{
await Connection.Start();//建立连接
}
catch (Exception)
{
throw;
}
}
针对服务端的实现方法,客户端也有不同的方法,如果使用服务端的OnConnected方法,上面的方法足够了,因为客户端的基本信息都是包含在Context中的,客户端与服务端建立连接时,服务端可以从Context中获取客户端信息。当然也可以写个自定义方法连接服务端的GetClientList方法。
private void ClientOnline()
{
string clientname=GetName();
string clientip=GetIP();
MyHub.Invoke("",clientname,clientip);
}
在通信连接建立后或在用户执行相应操作后执行此方法,向服务端发送客户端信息,服务端经过处理后将更改后的在线客户端列表广播至各客户端,现在缺少的就是客户端监听服务端的方法了。监听方法本质是一个长轮询,可以在其中接受服务端推送的数据并执行相应操作。
public MainWindow()
{
InitializeComponent();
ConnectAsync();
ConnectListener();
}
public void ConnectListener()
{
MyHub.On<List<ClientModel>>("getclientlist", list =>{
//数据绑定
this.Dispatcher.Invoke(delegate{
dgList.ItemsSource = list;
})
});
}
最后就需要关闭终端下线的方法了
服务端:
public override Task OnDisconnected(bool stopCalled)
{
Model.ClientModel client = new Model.ClientModel();
foreach (Model.ClientModel m in ClientList)
{
if (m.ConnectionId==Context.ConnectionId)
{
client = m;
ClientList.Remove(m);
break;
}
}
Clients.All.getuserlist(ClientList);
string output = string.Format("用户 {0} 下线,IP:{1}", client.ClientName, client.ClientIP);
Application.Current.Dispatcher.Invoke(() =>
{
((MainWindow)Application.Current.MainWindow).WriteToConsole(output);
});
return base.OnDisconnected(stopCalled);
}
客户端
private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
Connection.Stop();
Connection.Dispose();
}
其实上面只是简单的示例,而实际的业务场景会更复杂,但其基本原理是一致的,多多思考,可以扩展更多。
原文:https://www.cnblogs.com/lonelyxmas/p/10274980.html