要求: 在同1个方案中,分别编写服务端程序和客户端程序,利用tcp实现简单的群聊功能。 1.服务端程序选择WCF,客户端程序选择WPF。 2.客户端与服务端连接成功后,通过服务端获取已在线的用户,并将其显示在客户端的在线用户栏。 3.不论哪个用户发送聊天消息,其他所有用户都能看到该消息。 4.当某个用户推出后,在线用户列表中自动移除该用户。 源码:https://github.com/invokeG/WCF-TCP-chat https://download.csdn.net/download/weixin_44223180/12580184 实现简单的聊天程序
1. 界面一,选择启动两个或一个客户端两个button 启动并初始化窗口方法,实例化聊天窗口,初始化窗口位置和默认用户名。关闭子窗体时激活父窗体。
private void StartWindow(string userName, int left, int top) { ChatCline w = new ChatCline(); w.Left = left; w.Top = top; w.UserName = userName; w.Owner = this; w.Closed += (sender, e) => this.Activate();//关闭子窗体时激活父窗体 w.Show(); } private void bt1_Click(object sender, RoutedEventArgs e) { StartWindow("用户1", 0, 0); StartWindow("用户2", 400, 300); } private void bt2_Click(object sender, RoutedEventArgs e) { ChatCline w = new ChatCline(); w.Owner = this; w.Closed += (sendObj, args) => this.Activate(); w.Show(); }2. 聊天框口界面,顶部三个控件用来登录,下面的控件用一个canvas来装他们,隐藏时更方便,直接将canvas隐藏即可。 3. 服务端 Users类,定义属性和回调接口,拿到回调接口后可实现一对一
class Users { public string UserName { get; set; } public readonly IService1Callback callback; public Users(string userName, IService1Callback callback) { this.UserName = userName; this.callback = callback; } }CC类,用来装users的数据和对users的查找
class CC { public static List<Users> Users { get; set; } static CC() { Users = new List<Users>(); } public static Users GetUser(string userName) { Users user = null; foreach (var v in Users) { if (v.UserName == userName) { user = v; break; } } return user; } }服务端接口: 定义callback,别名设置IService,也可以有没有
[ServiceContract(Namespace = "IService", CallbackContract = typeof(IService1Callback))]定义三个接口
public interface IService1 { // TODO: 在此添加您的服务操作 [OperationContract(IsOneWay = true)] void Login(string userName); [OperationContract(IsOneWay = true)] void Logout(string userName); [OperationContract(IsOneWay = true)] void Talk(string userName, string message); }客户端接口: 定义4个客户端需要实现的接口
public interface IService1Callback { [OperationContract(IsOneWay = true)] void ShowLogin(string loginUserName); [OperationContract(IsOneWay = true)] void ShowLogout(string userName); [OperationContract(IsOneWay = true)] void ShowTalk(string userName, string message); [OperationContract(IsOneWay = true)] void InitUsersInfo(string UsersInfo); }4. 实现服务端接口 登录,获得回调接口,用户名,遍历users,调用客户端ShowLogin方法发给客户端
public void Login(string userName) { OperationContext context = OperationContext.Current; IService1Callback callback = context.GetCallbackChannel<IService1Callback>(); Users newUser = new Users(userName, callback); string str = ""; for (int i = 0; i < CC.Users.Count; i++) { str += CC.Users[i].UserName + "、"; } newUser.callback.InitUsersInfo(str.TrimEnd('、')); CC.Users.Add(newUser); foreach (var user in CC.Users) { user.callback.ShowLogin(userName); } }退出,清除CC中保存的该用户的数据,调用客户端方法发给客户端退出信息
public void Logout(string userName) { Users logoutUser = CC.GetUser(userName); CC.Users.Remove(logoutUser); logoutUser = null; foreach (var user in CC.Users) { user.callback.ShowLogout(userName); } }发消息,获得用户名和内容后,调用ShowTalk方法发给各个客户端
public void Talk(string userName, string message) { Users user = CC.GetUser(userName); foreach (var v in CC.Users) { v.callback.ShowTalk(userName, message); } }客户端
5. 聊天框口的启动 关闭时调用ChatCline_Closing方法,开启时隐藏canvas内容
public ChatCline() { InitializeComponent(); this.Closing += ChatCline_Closing; box.Visibility = System.Windows.Visibility.Hidden; }调用Logout方法进行用户退出的清除,并关闭该客户端
private void ChatCline_Closing(object sender, CancelEventArgs e) { if (client != null) { client.Logout(UserName); client.Close(); } }6. 客户端普通方法 字符串的整合,添加到聊天框
private void AddMessage(string str) { TextBlock t = new TextBlock(); t.Text = str; listmessage.Items.Add(t); }定义UserName属性
public string UserName { get { return textbox.Text; } set { textbox.Text = value; } }登录点击事件,获得用户名,调用服务端方法,连接失败则抛出异常
private void login_Click(object sender, RoutedEventArgs e) { UserName = textbox.Text; InstanceContext context = new InstanceContext(this); client = new Service1Client(context); try { client.Login(textbox.Text); login.IsEnabled = false; } catch (Exception ex) { MessageBox.Show("与服务端连接失败:" + ex.Message); return; } }发送,点击发送和回车发送,调用同一个服务端方法
private void launch_Click(object sender, RoutedEventArgs e) { client.Talk(UserName, messagebox.Text); messagebox.Text = ""; } private void messagebox_KeyDown(object sender, KeyEventArgs e) { if (e.Key == Key.Enter) { client.Talk(UserName, messagebox.Text); messagebox.Text = ""; } }7. 客户端实现的方法 初始化用户列表
public void InitUsersInfo(string UsersInfo) { if (UsersInfo.Length == 0) return; string[] users = UsersInfo.Split('、'); for (int i = 0; i < users.Length; i++) { listbox.Items.Add(users[i]); } }登录后现实聊天框口下部分内容,并添加用户名于列表
public void ShowLogin(string loginUserName) { if (loginUserName == UserName) { box.Visibility = System.Windows.Visibility.Visible; } listbox.Items.Add(loginUserName); }显示退出和现实聊天内容
public void ShowLogout(string userName) { listbox.Items.Remove(userName); } public void ShowTalk(string userName, string message) { AddMessage(userName+"说: "+message); }结果: 问题讨论: 1.在服务端App.config中contract第一个为项目名 2.由于控件较多,命名省事不规范,造成输出混乱的现象。
