WCF和TCP消息通信
要求:
在同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.由于控件较多,命名省事不规范,造成输出混乱的现象。
1639

被折叠的 条评论
为什么被折叠?



