聊天程序开发
2013-06-20 15:37
148 查看
开发前小笔记:
Socket的通信过程
服务器端:
l 申请一个Socket
l 绑定到一个IP地址和一个端口上
l 开启侦听,等待接受连接
客户端:
l 申请一个socket
l 连接服务器(指明IP地址和端口号)
服务器端接到连接请求后,产生一个新的socket(端口大于1024)与客户端建立连接并
进行通讯,原监听socket继续监听。
Socket方法
-------------------相关类------------------
![](http://images.cnitblog.com/blog/521559/201306/20155642-a5f86035a145416197b421275373b208.x-png)
分析结构图:
![](http://images.cnitblog.com/blog/521559/201306/20152408-486bd2493e03420b89a1a93427d498a6.x-png)
服务端:
![](http://images.cnitblog.com/blog/521559/201306/20154941-f97bd017aebc4645b21285eada7cfe36.x-png)
客户端:
![](http://images.cnitblog.com/blog/521559/201306/20155036-23ac353176d1487289e58e72297bd08a.x-png)
Socket的通信过程
服务器端:
l 申请一个Socket
l 绑定到一个IP地址和一个端口上
l 开启侦听,等待接受连接
客户端:
l 申请一个socket
l 连接服务器(指明IP地址和端口号)
服务器端接到连接请求后,产生一个新的socket(端口大于1024)与客户端建立连接并
进行通讯,原监听socket继续监听。
Socket方法
-------------------相关类------------------
分析结构图:
服务端:
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; using System.Net;//包含IP,IPAdress,IPEndPoint(ip和端口)类 using System.Net.Sockets;//导入套接字 using System.Threading; using System.IO; namespace MyCharRoomServer { public partial class FChatServer : Form { public FChatServer() { InitializeComponent(); TextBox.CheckForIllegalCrossThreadCalls = false;//关闭对文本框的跨线程操作检查 } Thread threadWatch = null;//负责监听客户端连接请求的线程 Socket socketWatch = null;//负责监听的套接字 private void btnBeginListem_Click(object sender, EventArgs e) { //创建服务端负责监听的套接字,参数(使用IP4寻址协议,使用流式连接,使用TCP协议传输数据) socketWatch = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); //获得文本框中的IP地址对象 IPAddress address = IPAddress.Parse(txtIP.Text.Trim()); //创建包含ip和port的网络节点对象 IPEndPoint endpoint = new IPEndPoint(address, int.Parse(txtPort.Text.Trim())); //将负责监听的套接字 绑定到唯一的IP和端口上 socketWatch.Bind(endpoint); //设置监听队列的长度 socketWatch.Listen(10); //创建负责监听的线程,并传入监听方法 threadWatch = new Thread(WatchConnecting); threadWatch.IsBackground = true;//设置为后台线程 threadWatch.Start();//开启线程 ShowMsg("服务器启动监听成功!"); //socketWatch.Bind //IPEndPoint } //保存了服务器端 所有负责和客户端通信的套接字 Dictionary<string, Socket> dict = new Dictionary<string, Socket>(); //保存了服务器端所有负责调用通信套接字.Recive方法的线程 Dictionary<string, Thread> dictThread = new Dictionary<string, Thread>(); /// <summary> /// 监听客户端请求的方法 /// </summary> void WatchConnecting() { while (true) //持续不断监听新的客户端的连接请求 { //开始监听客户端连接请求,注意:Accept方法,会阻断当前的线程! Socket sokConnetion = socketWatch.Accept();//一旦监听到客户端的请求,就返回一个负责和该客户端通信的套接字 sokConnetion //向列表控件中 添加一个客户端的ip端口字符串 作为客户端的唯一标识 lbOnline.Items.Add(sokConnetion.RemoteEndPoint.ToString()); //将与客户端通信的套接字对象 sokConnection添加到键值对集合中,并与客户端IP端口作为键值 dict.Add(sokConnetion.RemoteEndPoint.ToString(),sokConnetion); //创建通信线程 ParameterizedThreadStart pts = new ParameterizedThreadStart(RecMsg); Thread thr = new Thread(pts); thr.IsBackground = true;//设置为 q thr.Start(sokConnetion); dictThread.Add(sokConnetion.RemoteEndPoint.ToString(), thr); ShowMsg("客户端连接成功!"+sokConnetion.RemoteEndPoint.ToString());//★ //sokConnection.RemoteEndPoint中保存的是当前连接客户端的Ip和端口 } } /// <summary> /// 服务端负责监听客户端发送来的数据的方法 /// </summary> void RecMsg(object socketClientPara) { Socket socketClient = socketClientPara as Socket; while (true) { //定义一个接收用的缓存区(2M字节数组) byte[] arrMsgRec = new byte[1024 * 1024 * 2]; //将接收到的数据存入arrMsgRec数组,并返回真正接收到的数据长度 int length = -1; try { length = socketClient.Receive(arrMsgRec); } catch (SocketException ex) { ShowMsg("异常:" + ex.Message); //从通信套接字集合中删除被中断连接的通信套接字 dict.Remove(socketClient.RemoteEndPoint.ToString()); //从通信线程集合中删除被中断连接的线程对象 dictThread.Remove(socketClient.RemoteEndPoint.ToString()); //从列表中移除被中断的连接ip:Port lbOnline.Items.Remove(socketClient.RemoteEndPoint.ToString()); break; //dict.Remove( } catch (Exception ex) { ShowMsg("异常:" + ex.Message); break; } if (arrMsgRec[0] == 0)//判断发送过来的数组的第一个元素是0 则代表发送来的是文本文字数据 { //此时是将数组所有的元素都转成字符串,而真正接收到的只有服务端的几个字符 string strMsgRec = System.Text.Encoding.UTF8.GetString(arrMsgRec, 1, length-1); ShowMsg(strMsgRec); } else if (arrMsgRec[0] == 1)//如果是1,则代表发送过来的文件数据(图片/视频/文件……) { SaveFileDialog sfd = new SaveFileDialog();//保存文件选择框对象 if (sfd.ShowDialog() == System.Windows.Forms.DialogResult.OK)//用户选择文件路径后 { string fileSavePath = sfd.FileName;//获得要保存的文件路径 //创建文件流,然后让文件流来根据路径创建一个文件 using (FileStream fs = new FileStream(fileSavePath, FileMode.Create)) { fs.Write(arrMsgRec, 1, length-1); ShowMsg("文件保存成功:" + fileSavePath); } } } } } //发送消息到客户端 private void btnSend_Click(object sender, EventArgs e) { if (string.IsNullOrEmpty(lbOnline.Text)) { MessageBox.Show("请选择要发送的好友"); } else { string strMsg = txtMsgSend.Text.Trim(); //将要发送的字符串转成utf8对应的字节数组 byte[] arrMsg = System.Text.Encoding.UTF8.GetBytes(strMsg); //获得列表中 选中的Key string strClientKey = lbOnline.Text; //通过key,找到字典集合中对应的与某个客户端通信的套接字send方法,发送数据给对方 try { dict[strClientKey].Send(arrMsg); } catch (SocketException ex) { ShowMsg("发送时异常:" + ex.Message); return; } catch (Exception ex) { ShowMsg("发送时异常:" + ex.Message); } //sokConnetion.Send(arrMsg); ShowMsg("发送了数据出去:" + strMsg); } } //服务器端群发消息 private void btnSendToAll_Click(object sender, EventArgs e) { string strMsg = txtMsgSend.Text.Trim(); //将要发送的字符串转成utf8对应的字节数组 byte[] arrMsg = System.Text.Encoding.UTF8.GetBytes(strMsg); foreach(Socket s in dict.Values) { s.Send(arrMsg); } ShowMsg("群发完毕::)"); } void ShowMsg(string msg) { txtMsg.AppendText(msg + "\r\n"); } } }
客户端:
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; using System.Net.Sockets; using System.Net; using System.Threading; using System.IO; namespace MyChatClient { public partial class FChatClient : Form { public FChatClient() { InitializeComponent(); TextBox.CheckForIllegalCrossThreadCalls = false; } Thread threadClient = null;//客户端负责接收服务端发来的数据消息的线程 Socket socketClient = null;//客户端套接字 //客户端发送连接请求到服务器 private void btnConnect_Click(object sender, EventArgs e) { //获得IP IPAddress address= IPAddress.Parse(txtIP.Text.Trim()); //网络节点 IPEndPoint endpoint =new IPEndPoint(address,int.Parse(txtPort.Text.Trim())); //创建客户端套接字 socketClient=new Socket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp); //向指定的IP和端口发送连接请求 socketClient.Connect(endpoint); //创建线程 监听服务端发来的消息 threadClient = new Thread(RecMsg); threadClient.IsBackground = true; threadClient.Start(); } void RecMsg() { while (true) { //定义一个接收用的缓存区(2M字节数组) byte[] arrMsgRec = new byte[1024 * 1024 * 2]; //将接收到的数据存入arrMsgRec数组,并返回真正接收到的数据长度 int length = socketClient.Receive(arrMsgRec); //此时是将数组所有的元素都转成字符串,而真正接收到的只有服务端的几个字符 string strMsgRec = System.Text.Encoding.UTF8.GetString(arrMsgRec,0,length); ShowMsg(strMsgRec); } } //向服务器发送文本消息 private void btnSendMsg_Click(object sender, EventArgs e) { string strMsg = txtMsgSend.Text.Trim(); //将字符串转成方便网络传送的二进制数据 byte[] arrMsg = System.Text.Encoding.UTF8.GetBytes(strMsg); byte[] arrMsgSend = new byte[arrMsg.Length + 1]; arrMsgSend[0] = 0;//设置标识位,0代表发送的是文字 Buffer.BlockCopy(arrMsg, 0, arrMsgSend, 1, arrMsg.Length); socketClient.Send(arrMsgSend); ShowMsg("我说:" + strMsg); } #region 选择要发送的文件- void btnChooseFile_Click //选择要发送的文件 private void btnChooseFile_Click(object sender, EventArgs e) { OpenFileDialog ofd = new OpenFileDialog(); if (ofd.ShowDialog() == System.Windows.Forms.DialogResult.OK) { txtFilePath.Text = ofd.FileName; } } #endregion //向服务端发送文件 private void btnSendFile_Click(object sender, EventArgs e) { //用文件流打开用户选择的文件 if (string.IsNullOrEmpty(txtFilePath.Text)) { MessageBox.Show("请选择要发送的文件路径"); } else { using (FileStream fs = new FileStream(txtFilePath.Text, FileMode.Open)) { byte[] arrFile = new byte[1024 * 1024 * 2];//定义一个2M的数组(缓存区) //将文件数据读到数组arrFile中,并获取读取的真实数组长度length int length = fs.Read(arrFile, 0, arrFile.Length); byte[] arrFileSend = new byte[length + 1]; arrFileSend[0] = 1;//代表发送的是文件数据 //for (int i = 0; i < length; i++) //{ // arrFileSend[i + 1] = arrFile[i]; //} //将arrFile数组里的元素从第0个开始拷贝,拷贝到arrFileSend数组里,从第一个位置开始存放,一共存放length个数组 Buffer.BlockCopy(arrFile, 0, arrFileSend, 1, length); //发送包含标识位的新数组到服务端 socketClient.Send(arrFileSend); //arrFile.CopyTo(arrFileSend, length); } } } #region 在窗体文本框中显示消息-void ShowMsg(string msg) /// <summary> /// 在窗体文本框中显示消息 /// </summary> /// <param name="msg">消息</param> void ShowMsg(string msg) { txtMsg.AppendText(msg + "\r\n"); } #endregion } }
相关文章推荐
- 使用Remoting开发分布式应用(四) ——采用回调实现一个聊天程序
- Android 蓝牙开发实例--蓝牙聊天程序的设计和实现
- Win8 开发:聊天程序
- 如何基于BS架构开发聊天程序
- 如何基于BS架构开发聊天程序
- 用L脚本语言开发一个简单的局域网聊天程序
- 移动应用(手机应用)开发IM聊天程序解决方案
- 如何基于BS架构开发聊天程序
- 概述IE和SQL2k开发一个XML聊天程序
- Android蓝牙聊天程序的扩展开发(基于Google Sample,类QQ设计)
- 使用Xamarin开发手机聊天程序 -- 基础篇(大量图文讲解 step by step,附源码下载)
- Xamarin开发手机聊天程序
- 如何基于BS架构开发聊天程序
- 概述IE和SQL2k开发一个XML聊天程序
- 移动应用(手机应用)开发IM聊天程序解决方案
- 使用Xamarin开发手机聊天程序 -- 基础篇(大量图文讲解 step by step,附源码下载)
- 滚动条一直置于页面底部,开发聊天程序须知。
- 移动应用(手机应用)开发IM聊天程序解决方案
- QT类似于QQ的即时聊天程序用开发QTcpServer,QTcpSocket简单开发
- XMPP聊天程序iPhone版开发(一):初识XMPPFramework