C#基于Socket的局域网即时通信和传输文件程序
生活随笔
收集整理的這篇文章主要介紹了
C#基于Socket的局域网即时通信和传输文件程序
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
課程設計。
利用了Socket搭建了一個服務端和客戶端,兩個端之間可以通信。
使用了數據庫,密碼學,TCP/UDP通信協議,文件讀寫流等方面的技術。
只支持一對多,文件傳輸,聊天。
一個用來潤色的登錄界面,其實沒有也行。
主界面
點擊啟動按鈕后,會自動默認為服務端。且啟動按鈕會變成紅色
如果點擊連接了就自動默認為是客戶端,即可跟服務端進行通信。
兩個端之間可以進行通信和傳輸文件。
?
?底下兩個按鈕就是可以保存當前的聊天記錄,根據服務端和客戶端可以區分聊天記錄,下次可以讀取記錄進行繼續聊天。(很雞肋,可有可無,因為也沒有具體到給用戶做判斷。)
代碼
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; 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; using System.Xml.Serialization; using STalk;namespace AllClient {public partial class Form1 : Form{public Socket ClientSocket { get; set; }//自定義字段List<Socket> ClientSocketList = new List<Socket>();//服務端代理list集合public Dictionary<string, string> dic = new Dictionary<string, string>();//用戶名字典bool IsStar = false;//是否啟動bool IsConnet = false;//是否連接bool SeverConnet = false;//服務端是否已經添加過字典public Form1(){InitializeComponent();CurrentName.Text = SUserName.Username;CheckForIllegalCrossThreadCalls = false;}private void Bodytxt_KeyPress(object sender, KeyPressEventArgs e){e.Handled = true;//使用戶輸入失效}private void BtnStar_Click(object sender, EventArgs e)//啟動鍵{if (IsStar)//檢測用戶是否已啟動{MessageBox.Show("已啟動,請勿重復啟動", "提示", MessageBoxButtons.OKCancel, MessageBoxIcon.Question);}else{this.BtnStar.BackColor = Color.FromArgb(155, 51, 76);//UI顏色改變this.panel3.BackColor = Color.FromArgb(105, 51, 76);//全部可顯示BodyTxt.Visible = true;MsgTxt.Visible = true;BtnSendMsg.Visible = true;button1.Visible = true;label1.Visible = true;listBox1.Visible = true;Logopic.Visible = false;//logo不可見JsName.Text = "服務端";IsStar = true;//1實例化socket類,尋址方式,類型,協議。Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream,ProtocolType.Tcp);//2實例化端口號IPEndPoint ip = new IPEndPoint(IPAddress.Parse(IpTxt.Text),int.Parse(PortTxt.Text));//3綁定端口號try{socket.Bind(ip);//4開啟監聽socket.Listen(10);//只能處理一個,其余放在連接等待隊列隊列,最多只放10個,多出會返回錯誤//5開始接受客戶端連接//WaitCallback waitCallback = new WaitCallback(this.AcceptClientConnet);//線程池的回調方法ThreadPool.QueueUserWorkItem(AcceptClientConnet, socket);//為了反復連接,使用線程池//ConnetNumber.dic.Add(IpTxt.Text, CurrentName.Text);}catch (Exception){IsStar = false;MessageBox.Show("IP和端口號已使用或者IP輸入錯誤!", "提示", MessageBoxButtons.OKCancel, MessageBoxIcon.Error);}}}private void BtnConnet_Click(object sender, EventArgs e)//連接鍵{if (IsConnet){MessageBox.Show("已連接,請勿重復連接", "提示", MessageBoxButtons.OKCancel, MessageBoxIcon.Information);}else if (IsStar){MessageBox.Show("已作為服務端啟動,不能連接", "提示", MessageBoxButtons.OKCancel, MessageBoxIcon.Information);}else{this.BtnConnet.BackColor = Color.FromArgb(155, 51, 76);//改變UI顏色this.panel3.BackColor = Color.FromArgb(155, 151, 176);//全部可顯示BodyTxt.Visible = true;MsgTxt.Visible = true;BtnSendMsg.Visible = true;button1.Visible = true;label1.Visible = true;listBox1.Visible = true;Logopic.Visible = false;//logo不可見JsName.Text = "客戶端";IsStar = true;// 客戶端連接服務器端//1創建socket對象Socket socket = new Socket(AddressFamily.InterNetwork,SocketType.Stream, ProtocolType.Tcp);ClientSocket = socket;//賦值自定義字段//2連接服務器端try{IsConnet = true;socket.Connect(IPAddress.Parse(IpTxt.Text), int.Parse(PortTxt.Text));this.AppendTextToTxtLog(string.Format("連接上了服務端:{0}", ClientSocket.RemoteEndPoint.ToString()));ClientSocketList.Add(ClientSocket);//當有人連接上了就放到集合里顯示SendUsername();//發送用戶名//ConnetNumber.dic.Add(socket.RemoteEndPoint.ToString(), CurrentName.Text);//添加進靜態字典// listBox1.Items.Add(ClientSocket.RemoteEndPoint.ToString());//添加入列表}catch (Exception ex){IsConnet = false;/*Thread.Sleep(500);BtnConnet_Click(this, e);return;*/MessageBox.Show("連接失敗,請重新連接,連接失敗原因是:" + ex.ToString());}//3發送消息,接收消息Thread thread = new Thread(new ParameterizedThreadStart(ReceiveSeverData));//啟動線程thread.IsBackground = true;//該值指示某個線程是否為后臺線程。thread.Start(ClientSocket);}}private void button1_Click(object sender, EventArgs e)//發送文件按鈕{using (OpenFileDialog ofd = new OpenFileDialog())//打開文件選取框{if (ofd.ShowDialog(this) != DialogResult.OK){return;}byte[] data = File.ReadAllBytes(ofd.FileName);//獲取選擇的文件byte[] result = new byte[data.Length + 1];//創建一個新數組result[0] = 3;//在新數組上加上協議的頭部字節,3代表是文件Buffer.BlockCopy(data, 0, result, 1, data.Length);//把原始的數據放到最終的字節數組里去,buffer是塊拷貝foreach (var proxSocket in ClientSocketList){if (!proxSocket.Connected){continue;}// 把要發送的文件讀取來proxSocket.Send(result, SocketFlags.None);MessageBox.Show("文件已發送,等待對方接收。", "提示", MessageBoxButtons.OKCancel, MessageBoxIcon.Information);AppendTextToTxtLog("你向對方發送了文件。");}}}public void ReceiveSeverData(object socket)//接收服務端消息的方法{var ProxSocket = socket as Socket;byte[] data = new byte[1024 * 1024];//接收消息的緩沖區while (true){int len = 0;//記錄消息長度try{len = ProxSocket.Receive(data, 0, data.Length, SocketFlags.None);}catch (Exception){//異常退出AppendTextToTxtLog(string.Format("服務端:{0}異常斷開",ProxSocket.RemoteEndPoint.ToString()));StopConnet();return;}if (len <= 0){//如果小于0,證明無連接,客戶端正常退出AppendTextToTxtLog(string.Format("服務端:{0}斷開連接了。",ProxSocket.RemoteEndPoint.ToString()));StopConnet();return;//讓方法結束,終結當前接收客戶端數據的異步線程}//把接收到的消息放到了文本框上去//接受的數據第一個字節如果是1就是字符串,3就是文件if (data[0] == 1)//字符串{string str = Encoding.Default.GetString(data, 1, len - 1);//二進制數組轉換成字符串AppendTextToTxtLog(string.Format("{0}:{1}",dic[ProxSocket.RemoteEndPoint.ToString()], str));//調用方法放置消息到文本框中,RemoteEndPoint獲取遠程終結點。}else if (data[0] == 2)//用戶名添加{string str = Encoding.Default.GetString(data, 1, len - 1) + " [服務端]";try{if (SeverConnet == false){dic.Add(ProxSocket.RemoteEndPoint.ToString(), str);SeverConnet = true;//服務端已添加listBox1.Items.Add(str);}}catch{//string str2 = Encoding.Default.GetString(data, 1, len - 1);//listBox1.Items.Add(str2);}}else if (data[0] == 3)//文件{DialogResult r = MessageBox.Show("對方向你發送了文件,是否接收?", "提示", MessageBoxButtons.OKCancel, MessageBoxIcon.Question);if (r == DialogResult.OK){try{ProcessRecieveFile(data, len);AppendTextToTxtLog(string.Format("服務器端:{0}向你發送了文件。",ProxSocket.RemoteEndPoint.ToString()));MessageBox.Show("保存成功!", "提示", MessageBoxButtons.OKCancel, MessageBoxIcon.Information);}catch{MessageBox.Show("保存失敗!", "提示", MessageBoxButtons.OKCancel, MessageBoxIcon.Error);}}}}}public void ReceiveData(object socket)//接收客戶端消息的方法{var ProxSocket = socket as Socket;byte[] data = new byte[1024 * 1024];//接收消息的緩沖區while (true){int len = 0;//記錄消息長度try{len = ProxSocket.Receive(data, 0, data.Length, SocketFlags.None);}catch (Exception){//異常退出AppendTextToTxtLog(string.Format("客戶端:{0}異常斷開",ProxSocket.RemoteEndPoint.ToString()));ClientSocketList.Remove(ProxSocket);StopConnet(ProxSocket);return;}if (len <= 0){//如果小于0,證明無連接,服務端正常退出AppendTextToTxtLog(string.Format("客戶端:{0}斷開連接了。",ProxSocket.RemoteEndPoint.ToString()));ClientSocketList.Remove(ProxSocket);StopConnet(ProxSocket);return;//讓方法結束,終結當前接收服務端數據的異步線程}//把接收到的消息放到了文本框上去if (data[0] == 1)//字符串{string str = Encoding.Default.GetString(data, 1, len - 1);//二進制數組轉換成字符串AppendTextToTxtLog(string.Format("{0}:{1}",dic[ProxSocket.RemoteEndPoint.ToString()], str));//調用方法放置消息到文本框中,RemoteEndPoint獲取遠程終結點。//if(dic[ProxSocket.RemoteEndPoint.ToString()])//foreach (var ProxSocket in ClientSocketList)//{// if (ProxSocket.Connected)// {// byte[] data = Encoding.Default.GetBytes(MsgTxt.Text);// byte[] result = new byte[data.Length + 1];//創建新數組// result[0] = 1;//定義協議頭部為1,1為字符串發送// Buffer.BlockCopy(data, 0, result, 1, data.Length);//把原始的數據放到最終的字節數組里去,buffer是塊拷貝// ProxSocket.Send(result, 0, result.Length, SocketFlags.None);// AppendTextToTxtLog("你說:" + MsgTxt.Text);//調用方法放置消息到文本框中// MsgTxt.Text = "";// }//}}else if (data[0] == 2)//用戶名添加{string str = Encoding.Default.GetString(data, 1, len - 1);dic.Add(ProxSocket.RemoteEndPoint.ToString(), str);listBox1.Items.Add(str);}else if (data[0] == 3)//文件{DialogResult r = MessageBox.Show("對方向你發送了文件,是否接收?", "提示", MessageBoxButtons.OKCancel, MessageBoxIcon.Question);if (r == DialogResult.OK){ProcessRecieveFile(data, len);AppendTextToTxtLog(string.Format("服務器端:{0}向你發送了文件。",ProxSocket.RemoteEndPoint.ToString()));}}}}public void AcceptClientConnet(object socket)//連接方法{var serverSocket = socket as Socket;//強制轉換this.AppendTextToTxtLog("服務器開始接收客戶端連接。");while (true){var ProxSocket = serverSocket.Accept();this.AppendTextToTxtLog(string.Format("客戶端:{0}連接上了", ProxSocket.RemoteEndPoint.ToString()));ClientSocketList.Add(ProxSocket);//當有人連接上了就放到集合里顯示SendUsername();//listBox1.Items.Add(ProxSocket.RemoteEndPoint.ToString());//添加入列表//listBox1.Items.Add(UserCurrentName.Text);//添加入列表//不停地接收當前鏈接客戶端發送來的消息,使用線程池//WaitCallback waitCallback = new WaitCallback(this.ReceiveData);//線程池需要使用方法做變量,定義一個回調,使用自己寫的方法ThreadPool.QueueUserWorkItem(ReceiveData, ProxSocket);//為了反復連接,使用線程池}}public void AppendTextToTxtLog(string txt)//往文本框添加消息的方法{if (BodyTxt.InvokeRequired)//如果控件的 true 是在與調用線程不同的線程上創建的//(說明您必須通過 Invoke 方法對控件進行調用),則為//Handle;否則為 false{//可以使用 Action<T> 委托將方法作為參數傳遞//,而無需顯式聲明自定義委托。 BodyTxt.Invoke(new Action<string>(s =>{this.BodyTxt.Text = string.Format("{0}\r\n{1}", s, BodyTxt.Text);//格式化字符串}), txt);}else{this.BodyTxt.Text = string.Format("{0}\r\n{1}", txt, BodyTxt.Text);}}private void StopConnet()//關閉連接{try{if (ClientSocket.Connected){ClientSocket.Shutdown(SocketShutdown.Both);//禁用某 Socket 上的發送和接收。ClientSocket.Close(100);}}catch (Exception){}}private void StopConnet(Socket proxsocket)//關閉連接{try{if (proxsocket.Connected){proxsocket.Shutdown(SocketShutdown.Both);//禁用某 Socket 上的發送和接收。proxsocket.Close(100);}}catch (Exception){}}public void ProcessRecieveFile(byte[] data, int len)//文件處理{using (SaveFileDialog sfd = new SaveFileDialog()){sfd.DefaultExt = "";sfd.Filter = "文本文件(*.txt)|*.txt|圖像文件(*.jpg)" +"|*.jpg|word文件(*.doc)|*.doc|視頻文件(*.mp4)" +"|*.mp4|所有文件(*.*)|*.*";if (sfd.ShowDialog(this) != DialogResult.OK){return;}byte[] fileData = new byte[len - 1];Buffer.BlockCopy(data, 1, fileData, 0, len - 1);File.WriteAllBytes(sfd.FileName, fileData);}}private void BtnSendMsg_Click(object sender, EventArgs e)//發送消息{try{if (IsStar){foreach (var ProxSocket in ClientSocketList){if (ProxSocket.Connected){byte[] data = Encoding.Default.GetBytes(MsgTxt.Text);byte[] result = new byte[data.Length + 1];//創建新數組result[0] = 1;//定義協議頭部為1,1為字符串發送Buffer.BlockCopy(data, 0, result, 1, data.Length);//把原始的數據放到最終的字節數組里去,buffer是塊拷貝ProxSocket.Send(result, 0, result.Length, SocketFlags.None);AppendTextToTxtLog("你說:" + MsgTxt.Text);//調用方法放置消息到文本框中MsgTxt.Text = "";}}}else if (IsConnet){if (ClientSocket.Connected){byte[] data = Encoding.Default.GetBytes(MsgTxt.Text);byte[] res = new byte[data.Length + 1];res[0] = 1;Buffer.BlockCopy(data, 0, res, 1, data.Length);ClientSocket.Send(res, 0, res.Length, SocketFlags.None);AppendTextToTxtLog("你說:" + MsgTxt.Text);//調用方法放置消息到文本框中MsgTxt.Text = "";/*foreach (var ProxSocket in ClientSocketList){if (ProxSocket.Connected){byte[] data = Encoding.Default.GetBytes(MsgTxt.Text);byte[] result = new byte[data.Length + 1];//創建新數組result[0] = 1;//定義協議頭部為1,1為字符串發送Buffer.BlockCopy(data, 0, result, 1, data.Length);//把原始的數據放到最終的字節數組里去,buffer是塊拷貝ClientSocket.Send(result, 0, result.Length, SocketFlags.None);AppendTextToTxtLog("你說:" + MsgTxt.Text);//調用方法放置消息到文本框中MsgTxt.Text = "";}}*/}}}catch (Exception ex){MessageBox.Show("發送失敗,請重新連接,連接失敗原因是:" + ex.ToString());}}private void MsgTxt_KeyDown(object sender, KeyEventArgs e)//回車鍵發送消息{if (e.KeyCode == Keys.Enter){try{if (IsStar){foreach (var ProxSocket in ClientSocketList){if (ProxSocket.Connected){byte[] data = Encoding.Default.GetBytes(MsgTxt.Text);byte[] result = new byte[data.Length + 1];//創建新數組result[0] = 1;//定義協議頭部為1,1為字符串發送Buffer.BlockCopy(data, 0, result, 1, data.Length);//把原始的數據放到最終的字節數組里去,buffer是塊拷貝ProxSocket.Send(result, 0, result.Length, SocketFlags.None);AppendTextToTxtLog("你說:" + MsgTxt.Text);//調用方法放置消息到文本框中MsgTxt.Text = "";}}}else if (IsConnet){if (ClientSocket.Connected){byte[] data = Encoding.Default.GetBytes(MsgTxt.Text);byte[] res = new byte[data.Length + 1];res[0] = 1;Buffer.BlockCopy(data, 0, res, 1, data.Length);ClientSocket.Send(res, 0, res.Length, SocketFlags.None);AppendTextToTxtLog("你說:" + MsgTxt.Text);//調用方法放置消息到文本框中MsgTxt.Text = "";}}}catch (Exception ex){MessageBox.Show("發送失敗,請重新連接,連接失敗原因是:" + ex.ToString());}}}public void SendUsername()//發送用戶名{string r = CurrentName.Text.Trim();if (IsConnet){byte[] data = Encoding.Default.GetBytes(r);byte[] res = new byte[data.Length + 1];res[0] = 2;Buffer.BlockCopy(data, 0, res, 1, data.Length);ClientSocket.Send(res);}else if (IsStar){foreach (var ProxSocket in ClientSocketList){if (ProxSocket.Connected){byte[] data = Encoding.Default.GetBytes(r);byte[] res = new byte[data.Length + 1];res[0] = 2;Buffer.BlockCopy(data, 0, res, 1, data.Length);ProxSocket.Send(res);}}}}/*public void SendMessage(byte[] buffer)//發送消息給所有用戶{//獲得用戶在下拉框的IP地址var task1 = new Task(() =>{for (int i = 0; i < ClientSocketList.Count; i++){string ip = ClientSocketList[i].ToString();//dic[ip].Send(buffer.ToArray());}});task1.Start();}*/private void button2_Click(object sender, EventArgs e)//修改昵稱{if (NameTxt.Text == ""){MessageBox.Show("昵稱不能為空!", "提示", MessageBoxButtons.OKCancel, MessageBoxIcon.Information);}else{CurrentName.Text = NameTxt.Text.Trim();}}private void NameTxt_KeyDown(object sender, KeyEventArgs e)//回車修改昵稱{if (e.KeyCode == Keys.Enter){if (NameTxt.Text == ""){MessageBox.Show("昵稱不能為空!", "提示", MessageBoxButtons.OKCancel, MessageBoxIcon.Information);}else{CurrentName.Text = NameTxt.Text.Trim();}}}private void SaveChat_Click(object sender, EventArgs e)//保存聊天記錄{if (IsConnet == false && IsStar == false){MessageBox.Show("請啟動或連接后在進行保存");}else{if (IsStar == true){Chat com = new Chat{hao = 1,Smalltalk = BodyTxt.Text};SaveFileDialog save = new SaveFileDialog();save.Title = "請選擇保存路徑";save.InitialDirectory = @"C:";save.Filter = "xml文件|*.xml|所有文件|*.*";save.ShowDialog();if (save.ShowDialog(this) != DialogResult.OK){return;}string filename = save.FileName;StreamWriter sw = new StreamWriter(filename);XmlSerializer serializer = new XmlSerializer(typeof(Chat));serializer.Serialize(sw, com);sw.Close();}if (IsConnet == true){Chat com = new Chat{hao = 2,Smalltalk = BodyTxt.Text};SaveFileDialog save = new SaveFileDialog();save.Title = "請選擇保存路徑";save.InitialDirectory = @"C:";save.Filter = "xml文件|*.xml|所有文件|*.*";save.ShowDialog();if (save.ShowDialog(this) != DialogResult.OK){return;}string filename = save.FileName;StreamWriter sw = new StreamWriter(filename);XmlSerializer serializer = new XmlSerializer(typeof(Chat));serializer.Serialize(sw, com);sw.Close();}}/* private void BtnSendImg_Click(object sender, EventArgs e)//發送圖片{using (OpenFileDialog ofd = new OpenFileDialog())//打開文件選取框{if (ofd.ShowDialog(this) != DialogResult.OK){return;}byte[] data = File.ReadAllBytes(ofd.FileName);byte[] result = new byte[data.Length + 1];result[0] = 4;//4代表圖片Buffer.BlockCopy(data, 0, result, 1, data.Length);foreach (var proxSocket in ClientSocketList){if (!proxSocket.Connected){continue;}// 把要發送的文件讀取來//proxSocket.Send(result, SocketFlags.None);try{string str = Encoding.ASCII.GetString(data);this.BodyTxt.Rtf = str;}catch(Exception ex){MessageBox.Show(ex.ToString());}}}*/}private void LoadChat_Click(object sender, EventArgs e){OpenFileDialog ofd = new OpenFileDialog();ofd.Title = "請選擇您所讀取的聊天記錄文件";ofd.InitialDirectory = @"C:";ofd.Multiselect = true;ofd.Filter = "xml文件|*.xml|所有文件|*.*";ofd.ShowDialog();if (ofd.ShowDialog(this) != DialogResult.OK){return;}string filename = ofd.FileName;//FileStream fs = File.Open("1234.xml", FileMode.Open);StreamReader reader = new StreamReader(filename, Encoding.UTF8);XmlSerializer serializer = new XmlSerializer(typeof(Chat));Chat com2 = (Chat)serializer.Deserialize(reader);if (IsStar == true){if (com2.hao == 1){BodyTxt.Text = com2.Smalltalk;}else{MessageBox.Show("不能讀取客戶端聊天記錄或其他與該程序無關的xml文件!!!!!");}}if (IsConnet == true){if (com2.hao == 2){BodyTxt.Text = com2.Smalltalk;}else{MessageBox.Show("不能讀取服務端聊天記錄或其他與該程序無關的xml文件!!!!!");}}}} }?當時還沒有三層架構和重構代碼的意識,代碼全部擠在控件頁了。
其實只要掛個阿里云,把IP地址和端口改一下,也可以實現內網穿透了。
雖然很稚嫩,但是是當時的心血之作,那就放上來博客了。
總結
以上是生活随笔為你收集整理的C#基于Socket的局域网即时通信和传输文件程序的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【读书笔记】Flickr 网站用户标签的
- 下一篇: 转载Flickr 网站架构分析