上一节我们搭建了即时通信程序的登录端,这一节我们要实现即时通信程序的主客户端的搭建,也就是聊天、发文件端的创建。讲完这一节之后,我们就可以自己实现一个即时通信程序了。好了,先上一个图。
该UI布局如下:有一个ListBox用来显示当前在线用户命名为onLineList
三个文本框分别为:txtchatContent、txtsendMsg、txtsendFile,分别表示:聊天的记录、发送信息框、要发送的文件框
四个按钮分别为:btnsendMsg、btnsendAll、button1、button2,分别表示:
发送消息、群发消息、选择要发送的文件、发送文件
下面我们通过具体的示例来向大家一步步的进行讲解。
首先需要创建以下全局变量:
private ClientLogin clientLogin; //用来存储从ClientLogin传过来的client参数 private string ClientName; //表示当前的用户名 private TcpClient tcpClient; //全局的TcpClient对象,用来负责客户端的连接、通信 private NetworkStream netStream; //全局的NetworkStream对象,负责发送、接受信息 |
接着,我们需要在构造函数里做如下处理:
public ChatClient(ClientLogin clogin) { InitializeComponent(); TextBox.CheckForIllegalCrossThreadCalls = false ; //取消跨线程检测,允许非UI线程操作UI元素 skinEngine1.SkinFile = "SteelBlack.ssk" ; //加载皮肤文件 clientLogin = clogin; //将从登陆框传入的参数赋值给clientLogin lbname.Text += clientLogin.LoginName; tcpClient = clogin.TCPClient;将从登陆框传入的clogin的TCPClient属性赋值给全局tcpClient,负责客户端的通信 if (tcpClient != null ) { netStream = tcpClient.GetStream(); txtchatContent.Text = "连接成功!\r\n" ; Thread listenThread = new Thread( new ThreadStart(Listening)); //专门启动一个线程负责与服务端沟通 listenThread.Start(); } |
我们在Listening函数中来处理具体监听操作,我们在第一节中已经定制了客户端的通信,在Listening函数里有用到,在这里我们再温习一下。
1、先判断接收到的第一个字节是否为0,如果为0则直接保存为文件,如果不为0,则进行如下的判断。
2、接收到的第一个字节不为0,即收到的信息为字符串。
2.1、如果接收到的字符串格式为“OnLine|登录用户名|”,则显示登录用户名
2.2、如果接受到的字符串格式为“Off|用户名1、用户名2、用户名3.....|”,如果有用户登录或者离开则更新在线用户列表
2.3、如果收到的为其他格式的字符串,则直接添加在聊天窗口里,显示聊天信息。
public void Listening() { int length=0; while ( true ) { //先创建一个10M的缓存区 byte [] Msg = new byte [1024 * 1024 * 10]; //将传输的流读取到该缓冲区中 length=netStream.Read(Msg, 0, Msg.Length); //如果缓冲区的第一字节为0则为文件,否则,则为消息 if (Msg[0] == 0) { //直接新建一个文件流保存传输过来的文件 SaveFileDialog sfDialog = new SaveFileDialog(); if (sfDialog.ShowDialog() == DialogResult.OK) { FileStream fs = new FileStream(sfDialog.FileName, FileMode.OpenOrCreate, FileAccess.Write); fs.Write(Msg, 1, length - 1); MessageBox.Show( "文件传输完毕!" ); } } else { //第一个字节不为0,肯定为消息字符串 string msg = Encoding.Default.GetString(Msg); string [] results = msg.Split( new char [] { '|' }); if (results[0]== "OnLine" ) //如果有人上线,则显示上线人姓名 { txtchatContent.Text += results[1]+ "\r\n" ; } else if (results[0] == "Off" ) //有人上线或离开,更新在线人员列表 { string [] clients = results[1].Split( new char [] { '、' }); onlineList.Items.Clear(); for ( int i = 0; i < clients.Length-1; i++) { onlineList.Items.Add(clients[i]); } } else { txtchatContent.Text += msg+ "\r\n" ; } } } } |
接受我们需要在btnsendMsg按钮的Click事件的处理函数做如下处理:
需要将发送的消息做特殊的处理,发送信息的格式为:MSG|接受者姓名|发送者姓名|发送内容|
在发送信息前,要选择要发送的用户,如果没有选择用户则不能进行发送信息。
private void btnsendMsg_Click( object sender, EventArgs e) { if (onlineList.SelectedItem!= null ) { if (! string .IsNullOrEmpty(txtsendMsg.Text)) { try { string msg = "MSG|" + onlineList.SelectedItem.ToString() + "|" +clientLogin.LoginName+ "|" + txtsendMsg.Text+ "\r\n|" ; byte [] buffer = Encoding.Default.GetBytes(msg); this .netStream.Write(buffer, 0, buffer.Length); } catch (Exception e2) { MessageBox.Show( "信息发送异常:" + e2.Message); } } else { MessageBox.Show( "要发送的信息不能为空!" ); } } else { MessageBox.Show( "请选择要发送的对象!" ); } } |
如果我们需群发信息,则直接点击群发信息按钮就可以了,所以我们在btnSendAll按钮中做如下处理,将需要群发的信息进行加工,信息格式为:MSG|发送者姓名|发送内容|
private void btnsendAll_Click( object sender, EventArgs e) { string msg = "MSGALL|" +clientLogin.LoginName+ "|" + txtsendMsg.Text+ "\r\n|" ; byte [] buffer = Encoding.Default.GetBytes(msg); this .netStream.Write(buffer, 0, buffer.Length); } |
如果我们需要发送文件,则需要先选择要发送的文件,所以在button1的Click事件的处理函数中做如下处理,选择要发送的文件(文件大小不能超过10M)
private void button1_Click( object sender, EventArgs e) { OpenFileDialog ofDialog = new OpenFileDialog(); if (ofDialog.ShowDialog() == DialogResult.OK) { FileInfo f = new FileInfo(ofDialog.FileName); if (f.Length < 1024 * 1224 * 10) { txtsendFile.Text = ofDialog.FileName; } else { MessageBox.Show( "不好意思!您传输的文件大于10M,请分割后再传!" ); } |
选择文件后就可以点击发送,在button2的Click事件的处理函数中做如下处理,将要发送的文件读取到字节数组中,但是需要将该字节数组的首字节设置为0,然后才进行发送。
具体代码如下:
private void button2_Click( object sender, EventArgs e) { byte [] buffer = new byte [1024 * 1024 * 10]; try { FileStream fs = new FileStream(txtsendFile.Text, FileMode.Open, FileAccess.Read); int length = fs.Read(buffer, 0, buffer.Length); byte [] buffers= new byte [length+1]; buffers[0] = 0; //将bytes里的数据从第0个开始拷贝到filetype里,从第一个位置开始,一共拷贝length个数据 Buffer.BlockCopy(buffer, 0, buffers, 1, length); this .netStream.Write(buffers, 0, buffers.Length); FileStream fsq = new FileStream( @"D:\1.txt" , FileMode.Create, FileAccess.Write); fsq.Write(buffers, 0,length+1); fsq.Close(); } catch (Exception e2) { MessageBox.Show( "文件传输异常:" + e2.Message); } } } } |
好了到这里我们已经完成了整个即时通信程序的设计和实现,希望可以让大家对.NET平台下网络通信有新的认识,也希望能够对大家有所帮助。这个通信程序里我们主要用到的知识要点有TCP/UDP 通信(TCPListener/TCPClient)、Socket套接字、多线程、文件的操作等。如果有对以上知识点不清楚的朋友可以参看我以前写的相关的文章,希望可以对大家有所帮助。好了这一系列的文章就到这里了。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步