中秋节假期没事继续搞了搞
做了各聊天的模块,需要继续优化第一步画页面
页面参考仿微信的页面
加了截图发送图片
效果这样,点击右下角图标显示,可以拖动位置、隐藏、最大化(请忽略工具条里的颜色选择器,做的颜色选择器在这里试试,没啥意义)
截图大概这样
发送消息
截图代码也是网上找的,做了些改动,不记得是哪里了,有需要的可以找我要,这里就不贴了
页面画好了下面就要些服务端了
第二步 socket服务
写的比较简单
直接贴代码把,这个自己写的随便拿去用
程序入口
Program.csusing System;using System.Collections;using System.Collections.Generic;using System.IO;using System.Linq;using System.Net;using System.Net.Sockets;using System.Text;using System.Threading;using System.Threading.Tasks;using System.Web.Script.Serialization;using System.Windows.Forms;namespace PW.SocketServer{ static class Program { static MyServer myServer = null; ////// 应用程序的主入口点。 /// [STAThread] static void Main() { //Application.EnableVisualStyles(); //Application.SetCompatibleTextRenderingDefault(false); myServer = MyServer.GetInstance(); //myServer.ReceivedMsg = new MyServer.ReceivedMsgHandler(socketClient_ReceivedMsg); //Application.Run(new FormMain()); FormMain main = null; new Thread((ThreadStart)delegate { main = new FormMain(); Application.Run(main); }).Start(); myServer.BeginServer(); Console.ReadLine(); } }}
在启动服务的时候,同时启动的一个窗口,用来测试的
MyServer.cs
using System;using System.Collections;using System.Collections.Generic;using System.IO;using System.Linq;using System.Net;using System.Net.Sockets;using System.Text;using System.Threading;using System.Threading.Tasks;using System.Windows.Forms;namespace PW.SocketServer{ public class MyServer { Thread threadWatch = null;// 负责监听客户端的线程 Socket socketWatch = null;// 负责监听客户端的套接字 DictionaryclientList = null; Int32 con_msg_length = 10240; private static MyServer uniqueInstance; public delegate void ReceivedMsgHandler(string msg); public ReceivedMsgHandler ReceivedMsg;//自定义事件 public delegate void ClientChangeHandler(ClientUser client, Dictionary clientList); public ClientChangeHandler ClientAdd;//自定义事件 public ClientChangeHandler ClientRemove;//自定义事件 /// /// 获取单例 /// ///public static MyServer GetInstance() { // 如果类的实例不存在则创建,否则直接返回 if (uniqueInstance == null) { uniqueInstance = new MyServer(); } return uniqueInstance; } private MyServer() { try { string ServerIP = System.Configuration.ConfigurationManager.AppSettings["ServerIP"].ToString(); string ServerPort = System.Configuration.ConfigurationManager.AppSettings["ServerPort"].ToString(); con_msg_length = Int32.Parse(System.Configuration.ConfigurationManager.AppSettings["MsgLength"].ToString()); IPAddress ip = null; if (string.IsNullOrEmpty(ServerIP)) { ip = IPAddress.Any;//创建IP//Any 字段等效于以点分隔的四部分表示法格式的 0.0.0.0 这个IP地址,实际是一个广播地址。//对于SOCKET而言,使用 any ,表示,侦听本机的所有IP地址的对应的端口(本机可能有多个IP或只有一个IP) } else { ip = IPAddress.Parse(ServerIP);//监听指定ip } IPEndPoint point = new IPEndPoint(ip, Convert.ToInt32(ServerPort));//创建终结点(EndPoint) clientList = new Dictionary (); // 定义一个套接字用于监听客户端发来的消息,包含三个参数(ipv4寻址协议,流式连接,tcp协议) socketWatch = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); // 监听绑定的网路节点 socketWatch.Bind(point); // 将套接字的监听队列长度设置限制为0,0表示无限 socketWatch.Listen(0); // 创建一个监听线程 threadWatch = new Thread(WatchConnecting); threadWatch.IsBackground = true; } catch (Exception ex) { WriteTxtLog(ex.Message); } } public void BeginServer() { try { threadWatch.Start(); AppendText("成功启动监听!"); } catch (Exception ex) { WriteTxtLog(ex.Message); } } /// /// 监听客户端发来的请求 /// private void WatchConnecting() { //持续不断监听客户端发来的请求 while (true) { try { Socket clientSocket = socketWatch.Accept(); byte[] recMsg = new byte[con_msg_length]; if (clientSocket != null && clientSocket.Connected) { int length = clientSocket.Receive(recMsg); string msg = ""; if (length > 0) { //将机器接受到的字节数组转换为人可以读懂的字符串 msg = Encoding.UTF8.GetString(recMsg, 0, length); } AppendText("==[" + msg + "]==" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + "\r\n"); SocketMsg smsg = new SocketMsg(); try { smsg.setAllParametersJsonStr(msg); } catch { } Hashtable param = smsg.getAllParameters(); ClientUser cu = new ClientUser(); cu.ClientSocket = clientSocket; IPEndPoint ip = (IPEndPoint)clientSocket.RemoteEndPoint; cu.CliIp = ip.Address.ToString(); cu.CliPort = ip.Port.ToString(); cu.LoginTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"); AppendText("客户端连接!" + cu.CliIp + ":" + cu.CliPort + "\r\n"); string userno = ""; if (param != null && param.Count > 0) { userno = param["UserNo"].ToString(); cu.UserNo = userno; cu.UserName = param["UserName"].ToString(); } if (!clientList.ContainsKey(userno)) { clientList.Add(userno, cu); if (ClientAdd != null) { ClientAdd(cu, clientList); } } else { if (ClientRemove != null) { ClientRemove(clientList[userno], clientList); } clientList.Remove(userno); clientList.Add(userno, cu); if (ClientAdd != null) { ClientAdd(cu, clientList); } } AppendText("客户端连接成功!" + msg + "\r\n"); // 创建一个通信线程 ParameterizedThreadStart pts = new ParameterizedThreadStart(acceptMsg); Thread thr = new Thread(pts); thr.IsBackground = true; thr.Start(clientSocket); } } catch (Exception ex) { WriteTxtLog(ex.Message); } } } ////// 接收客户端发来的消息 /// /// 客户端套接字对象 private void acceptMsg(object socket) { Socket socketServer = socket as Socket; while (true) { try { //创建一个内存缓冲区 其大小为1024*1024字节 即1M byte[] recMsg = new byte[con_msg_length]; //将接收到的信息存入到内存缓冲区,并返回其字节数组的长度 int length = socketServer.Receive(recMsg); //将机器接受到的字节数组转换为人可以读懂的字符串 if (length > 0) { try { string msg = Encoding.UTF8.GetString(recMsg, 0, length); AppendText("==[" + msg + "]==" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + "\r\n"); SocketMsg smsg = new SocketMsg(); try { smsg.setAllParametersJsonStr(msg); } catch { } Hashtable param = smsg.getAllParameters(); if (param != null && param.Count > 0) { AppendText("客户端(" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + "):" + smsg.ConvertJson2Str(param["msgContent"]) + "\r\n"); string usernos = param["UserNo"] == null ? "" : param["UserNo"].ToString(); string[] nos = usernos.Split(','); if (nos != null && nos.Length > 0) { //转发消息到目标客户端 foreach (string no in nos) { if (clientList.ContainsKey(no) && clientList[no].ClientSocket != socketServer) { serverSendMsg(clientList[no].ClientSocket, smsg.ConvertJson2Str(param["msgContent"])); } } } } } catch (Exception exx) { AppendText("" + exx.Message + "(" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + "):\r\n"); } } else { string clin = ""; foreach (string key in clientList.Keys) { if (clientList[key].ClientSocket == socketServer) { clin = "userno:" + clientList[key].UserNo + " name:" + clientList[key].UserName + " ip:" + clientList[key].CliIp; if (ClientRemove != null) { ClientRemove(clientList[key], clientList); } clientList.Remove(key); break; } } AppendText("客户端[" + clin + "]断开(" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + "):\r\n"); socketServer.Disconnect(true); socketServer.Close(); break; } } catch (Exception ex) { string clin = ""; foreach (string key in clientList.Keys) { if (clientList[key].ClientSocket == socketServer) { clin = "userno:" + clientList[key].UserNo + " name:" + clientList[key].UserName + " ip:" + clientList[key].CliIp; if (ClientRemove != null) { ClientRemove(clientList[key], clientList); } clientList.Remove(key); break; } } AppendText("客户端[" + clin + "]断开(" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + "):\r\n"); socketServer.Disconnect(true); socketServer.Close(); break; } } } ////// 发送消息到客户端 /// /// public void serverSendMsg(Socket clientSocket, string msg) { try { byte[] sendMsg = Encoding.UTF8.GetBytes(msg); clientSocket.Send(sendMsg); AppendText("服务端(" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + "):" + msg + "\r\n"); } catch (Exception ex) { WriteTxtLog(ex.Message); } } private void AppendText(String theContent) { Console.WriteLine(theContent); WriteTxtLog(theContent); } private void WriteTxtLog(String theContent) { try { String theFileName = DateTime.Now.ToString("yyyyMMdd") + ".txt"; String theFilePath = Application.StartupPath + "\\MsgLog\\"; String theFullName = theFilePath + theFileName; //判断文件夹是否存在 if (Directory.Exists(theFilePath) == false)//如果不存在就创建file文件夹 { Directory.CreateDirectory(theFilePath); } StreamWriter sw = null; //判断文件是否存在 if (File.Exists(theFullName)) { sw = File.AppendText(theFullName); } else { sw = File.CreateText(theFullName);//创建该文件 } //导出传盘文件 //开始写入 sw.WriteLine(theContent); //关闭流 sw.Close(); } catch (Exception ex) { } } public DictionarygetClient() { return clientList; } }}
SocketMsg.cs
using System;using System.Collections.Generic;using System.Collections;using System.Linq;using System.Text;using System.Web.Script.Serialization;namespace PW.SocketServer{ public class SocketMsg { ////// 请求的参数 /// protected Hashtable parameters; ////// 获取参数值 /// /// 参数名 ///public string getParameter(string parameter) { string s = (string)parameters[parameter]; return (null == s) ? "" : s; } /// /// 设置参数值 /// /// 参数名 /// 参数值 public void setParameter(string parameter, string parameterValue) { if (parameter != null && parameter != "") { if (parameters.Contains(parameter)) { parameters.Remove(parameter); } parameters.Add(parameter, parameterValue); } } ////// 获取所有参数 /// ///public Hashtable getAllParameters() { return this.parameters; } /// /// 获取所有参数Json /// ///public String getAllParametersJsonStr() { JavaScriptSerializer jsser = new JavaScriptSerializer(); String jsonStr = jsser.Serialize(this.parameters); return jsonStr; } /// /// 获取所有参数Json /// ///public void setAllParametersJsonStr(String jsonStr) { JavaScriptSerializer jsser = new JavaScriptSerializer(); parameters = jsser.Deserialize (jsonStr); } /// /// /// ///public String ConvertJson2Str(object json) { try { JavaScriptSerializer jsser = new JavaScriptSerializer(); String jsonStr = jsser.Serialize(json); return jsonStr; } catch { return json.ToString(); } } }}
ClientUser.cs
using System;using System.Collections.Generic;using System.Linq;using System.Net.Sockets;using System.Text;using System.Threading.Tasks;namespace PW.SocketServer{ public class ClientUser { public string UserNo { get; set; } public string UserName { get; set; } public string LoginTime { get; set; } public string CliIp { get; set; } public string CliPort { get; set; } public Socket ClientSocket { get; set; } }}
不太会贴form代码
来个截图,就是来显示一下在线人员,和像指定的人发个消息,myserver,那里留的有口儿,这里只是用
cs代码
FormMain.cs
using System;using System.Collections.Generic;using System.Linq;using System.Windows.Forms;using System.IO;namespace PW.SocketServer{ public partial class FormMain : Form { MyServer myServer = null; public FormMain() { myServer = MyServer.GetInstance(); myServer.ClientAdd = new MyServer.ClientChangeHandler(myServer_ClientAdd); myServer.ClientRemove = new MyServer.ClientChangeHandler(myServer_ClientRemove); InitializeComponent(); //关闭对文本框的非法线程操作检查 TextBox.CheckForIllegalCrossThreadCalls = false; DataGridView.CheckForIllegalCrossThreadCalls = false; } void myServer_ClientAdd(ClientUser client, DictionaryclientList) { LoadGrid(); } void myServer_ClientRemove(ClientUser client, Dictionary clientList) { LoadGrid(); } private void FormMain_Load(object sender, EventArgs e) { } private void LoadGrid() { try { dataGridView1.Rows.Clear(); Dictionary tmps = myServer.getClient(); foreach (ClientUser cu in tmps.Values) { dataGridView1.Rows.Add(1); dataGridView1.Rows[dataGridView1.Rows.Count - 1].Cells["UserNo"].Value = cu.UserNo; dataGridView1.Rows[dataGridView1.Rows.Count - 1].Cells["UserName"].Value = cu.UserName; dataGridView1.Rows[dataGridView1.Rows.Count - 1].Cells["CliIp"].Value = cu.CliIp; dataGridView1.Rows[dataGridView1.Rows.Count - 1].Cells["CliPort"].Value = cu.CliPort; dataGridView1.Rows[dataGridView1.Rows.Count - 1].Cells["LoginTime"].Value = cu.LoginTime; } } catch (Exception ex) { } } private void button1_Click(object sender, EventArgs e) { if(dataGridView1.SelectedRows.Count>0) { string msg = "{title:'" + txtTitle.Text + "',content:'" + txtContent.Text + "'}"; Dictionary tmps = myServer.getClient(); try { foreach (DataGridViewRow row in dataGridView1.SelectedRows) { if (tmps.ContainsKey(row.Cells["UserNo"].Value.ToString())) { myServer.serverSendMsg(tmps[row.Cells["UserNo"].Value.ToString()].ClientSocket, msg); } } } catch { foreach (DataGridViewRow row in dataGridView1.SelectedRows) { var query = tmps.SingleOrDefault(p => p.Value.CliIp == row.Cells["CliIp"].Value.ToString()); if (query.Value != null) { myServer.serverSendMsg(query.Value.ClientSocket, msg); } } } } } private void AppendText(String theContent) { Console.WriteLine(theContent); WriteTxtLog(theContent); } private void WriteTxtLog(String theContent) { try { String theFileName = DateTime.Now.ToString("yyyyMMdd") + ".txt"; String theFilePath = Application.StartupPath + "\\MsgLog\\"; String theFullName = theFilePath + theFileName; //判断文件夹是否存在 if (Directory.Exists(theFilePath) == false)//如果不存在就创建file文件夹 { Directory.CreateDirectory(theFilePath); } StreamWriter sw = null; //判断文件是否存在 if (File.Exists(theFullName)) { sw = File.AppendText(theFullName); } else { sw = File.CreateText(theFullName);//创建该文件 } //导出传盘文件 //开始写入 sw.WriteLine(theContent); //关闭流 sw.Close(); } catch (Exception ex) { } } }}
配置文件--比较简单
到这里一个简单的socket已经写好了,写的比较i简单,可能不太稳定,本来想用之前用java写的那套服务呢,想想最后还是用C#写了
第三步 连通前后台
主要就是一个类
SocketClient.cs
using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Net.Sockets;using System.Threading;using System.Net;namespace PW.Infrastructure{ public class SocketClient { private static SocketClient uniqueInstance; // 创建一个客户端套接字 Socket clientSocket = null; // 创建一个监听服务端的线程 Thread threadServer = null; string start_msg = ""; public delegate void ReceivedMsgHandler(string msg); public ReceivedMsgHandler ReceivedMsg;//自定义事件 private SocketClient() { try { start_msg = "{UserNo:'" + GlobalData.UserName + "',UserName:'" + GlobalData.NickName + "'}"; } catch(Exception ex) { WriteTxtLog(ex.Message); } } ////// 获取单例 /// ///public static SocketClient GetInstance() { // 如果类的实例不存在则创建,否则直接返回 if (uniqueInstance == null) { uniqueInstance = new SocketClient(); } return uniqueInstance; } public void BeginConnect() { try { clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); IPAddress ip = IPAddress.Parse(GlobalData.SocketIP); clientSocket.BeginConnect(ip, GlobalData.SocketPort, (args) => { if (args.IsCompleted) //判断该异步操作是否执行完毕 { Byte[] bytesSend = new Byte[GlobalData.SocketMsgSize]; bytesSend = Encoding.UTF8.GetBytes(start_msg); //用户名,这里是刚刚连接上时需要传过去 if (clientSocket != null && clientSocket.Connected) { clientSocket.Send(bytesSend); WriteTxtLog("链接成功(" + GetCurrentTime() + "):\r\n"); // 创建一个线程监听服务端发来的消息 threadServer = new Thread(recMsg); threadServer.IsBackground = true; threadServer.Start(); } else { WriteTxtLog("服务器未开启(" + GetCurrentTime() + "):\r\n"); // 创建一个线程监听服务端发来的消息 threadServer = new Thread(recMsg); threadServer.IsBackground = true; threadServer.Start(); } } }, null); } catch (Exception ex) { WriteTxtLog(ex.Message); } } /// /// 接收服务端发来的消息 /// private void recMsg() { while (true) //持续监听服务端发来的消息 { //定义一个1M的内存缓冲区 用于临时性存储接收到的信息 byte[] arrRecMsg = new byte[GlobalData.SocketMsgSize]; int length = 0; try { //将客户端套接字接收到的数据存入内存缓冲区, 并获取其长度 length = clientSocket.Receive(arrRecMsg); //将套接字获取到的字节数组转换为人可以看懂的字符串 string strRecMsg = Encoding.UTF8.GetString(arrRecMsg, 0, length); if (ReceivedMsg != null) { ReceivedMsg(strRecMsg); } //将发送的信息追加到聊天内容文本框中 WriteTxtLog("服务端(" + GetCurrentTime() + "):" + strRecMsg + "\r\n"); } catch(Exception ex) { WriteTxtLog("" + ex.Message+ ":\r\n"); WriteTxtLog("服务器已关闭(" + GetCurrentTime() + "):\r\n"); //重连 Thread.Sleep(10 * 1000); if (!clientSocket.Connected) { clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); clientSocket.BeginConnect(IPAddress.Parse(GlobalData.SocketIP), GlobalData.SocketPort, (args) => { if (args.IsCompleted) //判断该异步操作是否执行完毕 { Byte[] bytesSend = new Byte[GlobalData.SocketMsgSize]; bytesSend = Encoding.UTF8.GetBytes(start_msg); //用户名,这里是刚刚连接上时需要传过去 if (clientSocket != null && clientSocket.Connected) { clientSocket.Send(bytesSend); WriteTxtLog("重连链接成功(" + GetCurrentTime() + "):\r\n"); } else { WriteTxtLog("重连失败服务器已关闭(" + GetCurrentTime() + "):\r\n"); } } }, null); } } } } ////// 发送消息到服务端 /// /// public void SendMsg(string title, string content, string userno) { try { string msg = "{title:'" + title + "',content:'" + content + "'}"; //userno:"001,002,003" if (string.IsNullOrEmpty(userno)) { userno = "0"; } string msgcon = "{msgType:'msg',msgContent:'" + msg + "',UserNo:'" + userno + "'}"; clientSendMsg(msgcon); } catch (Exception ex) { WriteTxtLog(ex.Message); } } public void CloseConnect() { try { if (clientSocket != null) clientSocket.Close(); if (threadServer != null && threadServer.IsAlive) { threadServer.Abort(); } clientSocket = null; threadServer = null; } catch (Exception ex) { WriteTxtLog(ex.Message); } } ////// 发送消息到服务端 /// /// public void clientSendMsg(string msg) { byte[] sendMsg = Encoding.UTF8.GetBytes(msg); clientSocket.Send(sendMsg); WriteTxtLog("客户端(" + GetCurrentTime() + "):" + msg + "\r\n"); } ////// 获取当前系统时间的方法 /// ///当前时间 private DateTime GetCurrentTime() { DateTime currentTime = new DateTime(); currentTime = DateTime.Now; return currentTime; } public static void WriteTxtLog(String theContent) { try { //String theFileName = DateTime.Now.ToString("yyyyMMdd") + ".txt"; //String theFilePath = Application.StartupPath +"\\MsgLog\\"; //String theFullName = theFilePath + theFileName; 判断文件夹是否存在 //if (Directory.Exists(theFilePath) == false)//如果不存在就创建file文件夹 //{ // Directory.CreateDirectory(theFilePath); //} //StreamWriter sw = null; 判断文件是否存在 //if (File.Exists(theFullName)) //{ // sw = File.AppendText(theFullName); //} //else //{ // sw = File.CreateText(theFullName);//创建该文件 //} 导出传盘文件 开始写入 //sw.WriteLine(theContent); 关闭流 //sw.Close(); } catch (Exception ex) { } } }}
用的话就就是在登录系统后连接服务器
SocketClient socketClient = null;
socketClient = SocketClient.GetInstance(); //socketClient.ReceivedMsg = new SocketClient.ReceivedMsgHandler(socketClient_ReceivedMsg); socketClient.BeginConnect();发送消息
SocketClient sc = SocketClient.GetInstance();
sc.SendMsg("test", msg.Msg,this._userName);聊天接收消息
SocketClient socketClient = SocketClient.GetInstance();
socketClient.ReceivedMsg = new SocketClient.ReceivedMsgHandler(socketClient_ReceivedMsg);这里是在聊天的模块接口socket的消息,这是一个比较懒的写法,按说把应该把消息统一接收,然后用prism的模块之间的通信机制来实现聊天模块的消息接收,因为现在SocketClient是一个单例,你在一个模块设置ReceivedMsg之后,另一个模块设置的ReceivedMsg应该就无效了,,,,这里实际用的话不能像我这样写,,,,后续我也会改掉
然后看一下测试结果
未登录状态
登录成功后
这些吃了吗。。。。都是代码写死的调用发消息的接口,可以忽略
然后手动发一个消息看看
没问题,可以发送成功,没有启动多个客户端,只是能看到服务端收到了消息
再看看表情消息
也可以
然后试试接收消息
没问题
然后试试左侧的消息数量
先点击一边把之前的都去掉
这里因为是测试,直接代码指定的是张三3发来的消息
然后关闭程序看服务端
基本的功能都实现了,后续就是不断的完善了