中秋節假期沒事繼續搞了搞
作了各聊天的模塊,須要繼續優化css
頁面參考https://github.com/yanchao891012/WPF_WeChat/tree/master/WeChat.NET仿微信的頁面html
加了截圖發送圖片java
效果這樣,點擊右下角圖標顯示,能夠拖動位置、隱藏、最大化(請忽略工具條裏的顏色選擇器,作的顏色選擇器在這裏試試,沒啥意義)git
截圖大概這樣github
發送消息json
截圖代碼也是網上找的,作了些改動,不記得是哪裏了,有須要的能夠找我要,這裏就不貼了數組
頁面畫好了下面就要些服務端了服務器
寫的比較簡單微信
直接貼代碼把,這個本身寫的隨便拿去用框架
程序入口
Program.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.Web.Script.Serialization; using System.Windows.Forms; namespace PW.SocketServer { static class Program { static MyServer myServer = null; /// <summary> /// 應用程序的主入口點。 /// </summary> [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;// 負責監聽客戶端的套接字 Dictionary<String, ClientUser> clientList = 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<String, ClientUser> clientList); public ClientChangeHandler ClientAdd;//自定義事件 public ClientChangeHandler ClientRemove;//自定義事件 /// <summary> /// 獲取單例 /// </summary> /// <returns></returns> 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<string, ClientUser>(); // 定義一個套接字用於監聽客戶端發來的消息,包含三個參數(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); } } /// <summary> /// 監聽客戶端發來的請求 /// </summary> 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); } } } /// <summary> /// 接收客戶端發來的消息 /// </summary> /// <param name="socket">客戶端套接字對象</param> 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; } } } /// <summary> /// 發送消息到客戶端 /// </summary> /// <param name="msg"></param> 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 Dictionary<String, ClientUser> getClient() { 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 { /// <summary> /// 請求的參數 /// </summary> protected Hashtable parameters; /// <summary> /// 獲取參數值 /// </summary> /// <param name="parameter">參數名</param> /// <returns></returns> public string getParameter(string parameter) { string s = (string)parameters[parameter]; return (null == s) ? "" : s; } /// <summary> /// 設置參數值 /// </summary> /// <param name="parameter">參數名</param> /// <param name="parameterValue">參數值</param> public void setParameter(string parameter, string parameterValue) { if (parameter != null && parameter != "") { if (parameters.Contains(parameter)) { parameters.Remove(parameter); } parameters.Add(parameter, parameterValue); } } /// <summary> /// 獲取全部參數 /// </summary> /// <returns></returns> public Hashtable getAllParameters() { return this.parameters; } /// <summary> /// 獲取全部參數Json /// </summary> /// <returns></returns> public String getAllParametersJsonStr() { JavaScriptSerializer jsser = new JavaScriptSerializer(); String jsonStr = jsser.Serialize(this.parameters); return jsonStr; } /// <summary> /// 獲取全部參數Json /// </summary> /// <returns></returns> public void setAllParametersJsonStr(String jsonStr) { JavaScriptSerializer jsser = new JavaScriptSerializer(); parameters = jsser.Deserialize<Hashtable>(jsonStr); } /// <summary> /// /// </summary> /// <returns></returns> 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, Dictionary<String, ClientUser> clientList) { LoadGrid(); } void myServer_ClientRemove(ClientUser client, Dictionary<String, ClientUser> clientList) { LoadGrid(); } private void FormMain_Load(object sender, EventArgs e) { } private void LoadGrid() { try { dataGridView1.Rows.Clear(); Dictionary<String, ClientUser> 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<String, ClientUser> 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); } } /// <summary> /// 獲取單例 /// </summary> /// <returns></returns> 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); } } /// <summary> /// 接收服務端發來的消息 /// </summary> 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); } } } } /// <summary> /// 發送消息到服務端 /// </summary> /// <param name="msg"></param> 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); } } /// <summary> /// 發送消息到服務端 /// </summary> /// <param name="msg"></param> public void clientSendMsg(string msg) { byte[] sendMsg = Encoding.UTF8.GetBytes(msg); clientSocket.Send(sendMsg); WriteTxtLog("客戶端(" + GetCurrentTime() + "):" + msg + "\r\n"); } /// <summary> /// 獲取當前系統時間的方法 /// </summary> /// <returns>當前時間</returns> 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發來的消息
而後關閉程序看服務端
基本的功能都實現了,後續就是不斷的完善了