C# Socket服務(wù)端與客戶端通信(包含大文件的斷點(diǎn)傳輸、端口復(fù)用技術(shù))
當(dāng)前位置:點(diǎn)晴教程→知識(shí)管理交流
→『 技術(shù)文檔交流 』
步驟: 一、服務(wù)端的建立 1.服務(wù)端的項(xiàng)目建立以及頁(yè)面布局 2.各功能按鍵的事件代碼 1)傳輸類(lèi)型說(shuō)明以及全局變量 2)Socket通信服務(wù)端具體步驟: (1)建立一個(gè)Socket (2)接收信息 (3)發(fā)送數(shù)據(jù)(這里分發(fā)送字符串、文件(包含大文件)、震動(dòng)) 二、客戶端的建立 1.服務(wù)端的項(xiàng)目建立以及頁(yè)面布局 2.各功能按鍵的事件代碼 1)傳輸類(lèi)型說(shuō)明以及全局變量 2)Socket通信服務(wù)端具體步驟: (1)建立一個(gè)Socket (2)接收信息 (3)發(fā)送數(shù)據(jù)(這里分發(fā)送字符串、文件(包含大文件)、震動(dòng)) 注意:此圖是Socket通信的精華,在使用Socket通信時(shí),有什么迷惑的可以看看此圖,下面我們講解的時(shí)候也是參照此圖
Socket大家肯定很熟悉,對(duì)已內(nèi)部的通信邏輯,肯定也有一定得了解--- 對(duì)于Socket研究了兩天寫(xiě)了一個(gè)小程序,通過(guò)Socket服務(wù)端與客戶端的通信,以及大文件之間斷點(diǎn)的傳輸(這里只做了服務(wù)端給客戶端傳送大文件,如果想把客戶端的大文件傳送給服務(wù)端也是一樣的道理,看了文章,大家肯定可以自己實(shí)現(xiàn))······ (自己才疏學(xué)淺,如有bug請(qǐng)諒解,但功能還是能實(shí)現(xiàn)的) 下面根據(jù)步驟進(jìn)入正題: 一、服務(wù)端的建立 1.服務(wù)端的項(xiàng)目建立以及頁(yè)面布局 新建解決方案“Socket通信”以及兩個(gè)Winform項(xiàng)目(1)SockeClient——客戶端 (2)SocketServer——服務(wù)器 給服務(wù)端界面布局——參照上圖(這個(gè)大家肯定都是手到擒來(lái)就不累贅了······) 2.各功能按鍵的事件代碼 先把整個(gè)服務(wù)端的代碼貼出來(lái),然后我們?cè)谝灰恢v解 namespace SocketServer { public partial class Form1 : Form { //說(shuō)明:在傳遞信息的時(shí)候,會(huì)在需要傳遞的信息前面加一個(gè)字符來(lái)標(biāo)識(shí)傳遞的是不同的信息 // 0:表示傳遞的是字符串信息 // 1:表示傳遞的是文件信息 // 2:表示的是震動(dòng)
/// <summary> /// 用來(lái)存放連接服務(wù)的客戶端的IP地址和端口號(hào),對(duì)應(yīng)的Socket /// </summary> Dictionary<string, Socket> dicSocket = new Dictionary<string, Socket>();
public Form1() { InitializeComponent(); }
private void Form1_Load(object sender, EventArgs e) { //不檢測(cè)跨線程之間的空間調(diào)用 Control.CheckForIllegalCrossThreadCalls = false; }
/// <summary> /// 開(kāi)啟監(jiān)聽(tīng) /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void btnStart_Click(object sender, EventArgs e) { try { //當(dāng)點(diǎn)擊開(kāi)始監(jiān)聽(tīng)的時(shí)候 在服務(wù)器端創(chuàng)建一個(gè)負(fù)責(zé)監(jiān)IP地址跟端口號(hào)的Socket Socket socketWatch = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); //獲取IP IPAddress ip = IPAddress.Any; //創(chuàng)建端口號(hào) IPEndPoint port = new IPEndPoint(ip, Convert.ToInt32(txtPort.Text)); //監(jiān)聽(tīng) socketWatch.Bind(port); ShowMsg("監(jiān)聽(tīng)成功"); socketWatch.Listen(10); //新建線程,去接收客戶端發(fā)來(lái)的信息 Thread td = new Thread(AcceptMgs); td.IsBackground = true; td.Start(socketWatch); } catch { } }
/// <summary> /// 接收客戶端發(fā)送的信息 /// </summary> /// <param name="o"></param> private void AcceptMgs(object o) { try { Socket socketWatc = (Socket)o; while (true) { ////負(fù)責(zé)跟客戶端通信的Socket Socket socketSend = socketWatc.Accept(); //將遠(yuǎn)程連接的客戶端的IP地址和Socket存入集合中 dicSocket.Add(socketSend.RemoteEndPoint.ToString(), socketSend); //將遠(yuǎn)程連接的客戶端的IP地址和端口號(hào)存儲(chǔ)下拉框中 cboUsers.Items.Add(socketSend.RemoteEndPoint.ToString()); ShowMsg(socketSend.RemoteEndPoint.ToString() + ": 連接成功"); //新建線程循環(huán)接收客戶端發(fā)來(lái)的信息 Thread td = new Thread(Recive); td.IsBackground = true; td.Start(socketSend); } } catch { } }
/// <summary> /// 接收客戶端發(fā)來(lái)的數(shù)據(jù),并顯示出來(lái) /// </summary> private void Recive(object o) { Socket socketSend = (Socket)o; try { while (true) { //客戶端連接成功后,服務(wù)器應(yīng)該接受客戶端發(fā)來(lái)的消息
if (socketSend == null) { MessageBox.Show("請(qǐng)選擇要發(fā)送的客戶端"); continue; } byte[] buffer = new byte[1024 * 1024 * 2]; //實(shí)際接受到的有效字節(jié)數(shù) int r = socketSend.Receive(buffer); //如果客戶端關(guān)閉,發(fā)送的數(shù)據(jù)就為空,然后就跳出循環(huán) if (r == 0) { break; } if (buffer[0] == 0) //如果接收的字節(jié)數(shù)組的第一個(gè)字節(jié)是0,說(shuō)明接收的字符串信息 { string strMsg = Encoding.UTF8.GetString(buffer, 1, r - 1); ShowMsg(socketSend.RemoteEndPoint.ToString() + ": " + strMsg); } else if (buffer[0] == 1) //如果接收的字節(jié)數(shù)組的第一個(gè)字節(jié)是1,說(shuō)明接收的是文件 { string filePath = ""; SaveFileDialog sfd = new SaveFileDialog(); sfd.Title = "保存文件"; sfd.InitialDirectory = @"C:\Users\Administrator\Desktop"; sfd.Filter = "文本文件|*.txt|圖片文件|*.jpg|視頻文件|*.avi|所有文件|*.*"; //如果沒(méi)有選擇保存文件路徑就一直打開(kāi)保存框 while (true) { sfd.ShowDialog(this); filePath = sfd.FileName; if (string.IsNullOrEmpty(filePath)) { continue; } else { break; } } //保存接收的文件 using (FileStream fsWrite = new FileStream(filePath, FileMode.OpenOrCreate, FileAccess.Write)) { fsWrite.Write(buffer, 1, r - 1); } ShowMsg(socketSend.RemoteEndPoint + ": 接收文件成功");
} else if (buffer[0] == 2) //如果接收的字節(jié)數(shù)組的第一個(gè)字節(jié)是2,說(shuō)明接收的是震動(dòng) { ZD(); } } } catch{} }
/// <summary> /// 顯示信息 /// </summary> /// <param name="message"></param> private void ShowMsg(string message) { txtLog.AppendText(message + "\r\n"); }
/// <summary> /// 發(fā)送信息 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void btnSend_Click(object sender, EventArgs e) {
//獲得選中客戶端ip對(duì)應(yīng)的通信Socket if (cboUsers.SelectedItem == null) { MessageBox.Show("請(qǐng)選擇要發(fā)送的客戶端"); return; } Socket socketSend = dicSocket[cboUsers.SelectedItem.ToString()]; if (socketSend == null) { MessageBox.Show("請(qǐng)選擇要發(fā)送的客戶端"); return; } string strSend=txtMsg.Text; try { byte[] buffer = Encoding.UTF8.GetBytes(strSend); //獲得發(fā)送的信息時(shí)候,在數(shù)組前面加上一個(gè)字節(jié) 0 List<byte> list = new List<byte>(); list.Add(0); list.AddRange(buffer); //將泛型集合轉(zhuǎn)換為數(shù)組 byte[] newBuffer = list.ToArray(); //將了標(biāo)識(shí)字符的字節(jié)數(shù)組傳遞給客戶端 socketSend.Send(newBuffer); txtMsg.Text = ""; } catch { } }
/// <summary> /// 選擇文件 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void btnSelect_Click(object sender, EventArgs e) { //打開(kāi)文件 OpenFileDialog ofd = new OpenFileDialog(); ofd.Title = "選擇要傳的文件"; ofd.InitialDirectory = @"C:\Users\Administrator\Desktop"; ofd.Filter = "文本文件|*.txt|圖片文件|*.jpg|視頻文件|*.avi|所有文件|*.*"; ofd.ShowDialog(); //得到選擇文件的路徑 txtPath.Text = ofd.FileName; }
/// <summary> /// 發(fā)送文件 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void btnSendFile_Click(object sender, EventArgs e) { //判斷是否選擇了要發(fā)送的客戶端 if (cboUsers.SelectedItem == null) { MessageBox.Show("請(qǐng)選擇要發(fā)送的客戶端"); return; } Socket socketSend = dicSocket[cboUsers.SelectedItem.ToString()]; if (socketSend == null) { MessageBox.Show("請(qǐng)選擇要發(fā)送的客戶端"); return; } string filePath = txtPath.Text; if (string.IsNullOrEmpty(filePath)) { MessageBox.Show("請(qǐng)選擇文件"); return; } Thread td = new Thread(SendBigFile); td.IsBackground = true; td.Start(); }
/// <summary> /// 大文件斷點(diǎn)傳送 /// </summary> private void SendBigFile() { string filePath = txtPath.Text; Socket socketSend = dicSocket[cboUsers.SelectedItem.ToString()]; try { //讀取選擇的文件 using (FileStream fsRead = new FileStream(filePath, FileMode.OpenOrCreate, FileAccess.Read)) { //1. 第一步:發(fā)送一個(gè)包,表示文件的長(zhǎng)度,讓客戶端知道后續(xù)要接收幾個(gè)包來(lái)重新組織成一個(gè)文件 long length = fsRead.Length; byte[] byteLength = Encoding.UTF8.GetBytes(length.ToString()); //獲得發(fā)送的信息時(shí)候,在數(shù)組前面加上一個(gè)字節(jié) 1 List<byte> list = new List<byte>(); list.Add(1); list.AddRange(byteLength); socketSend.Send(list.ToArray()); // //2. 第二步:每次發(fā)送一個(gè)1MB的包,如果文件較大,則會(huì)拆分為多個(gè)包 byte[] buffer = new byte[1024 * 1024]; long send = 0; //發(fā)送的字節(jié)數(shù) while (true) //大文件斷點(diǎn)多次傳輸 { int r = fsRead.Read(buffer, 0, buffer.Length); if (r == 0) { break; } socketSend.Send(buffer, 0, r, SocketFlags.None); send += r; ShowMsg(string.Format("{0}: 已發(fā)送:{1}/{2}", socketSend.RemoteEndPoint, send, length)); } ShowMsg("發(fā)送完成"); txtPath.Text = ""; } } catch { } }
/// <summary> /// 震動(dòng) /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void btnZD_Click(object sender, EventArgs e) { //判斷是否選擇了要發(fā)送的客戶端 if (cboUsers.SelectedItem == null) { MessageBox.Show("請(qǐng)選擇要發(fā)送的客戶端"); return; } Socket socketSend = dicSocket[cboUsers.SelectedItem.ToString()]; if (socketSend == null) { MessageBox.Show("請(qǐng)選擇要發(fā)送的客戶端"); return; } try { // 首字節(jié)是2說(shuō)明是震動(dòng) byte[] buffer = new byte[1]; buffer[0] = 2; socketSend.Send(buffer); } catch { } }
/// <summary> /// 震動(dòng) /// </summary> private void ZD() { //獲取當(dāng)前窗體的坐標(biāo) Point point = this.Location; //反復(fù)給窗體坐標(biāo)復(fù)制一百次,達(dá)到震動(dòng)的效果 for (int i = 0; i < 100; i++) { this.Location = new Point(point.X - 5, point.Y - 5); this.Location = new Point(point.X + 5, point.Y + 5); } this.Location = point; } } } 1)傳輸類(lèi)型說(shuō)明以及全局變量 這些說(shuō)明以及全局變量,說(shuō)的也比較清楚,也不累贅了。 2)Socket通信服務(wù)端具體步驟: (這些步驟都是根據(jù)第一個(gè)圖來(lái)的) (1)建立一個(gè)Socket /// <summary> /// 開(kāi)啟監(jiān)聽(tīng) /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void btnStart_Click(object sender, EventArgs e) { try { //當(dāng)點(diǎn)擊開(kāi)始監(jiān)聽(tīng)的時(shí)候 在服務(wù)器端創(chuàng)建一個(gè)負(fù)責(zé)監(jiān)IP地址跟端口號(hào)的Socket Socket socketWatch = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); //獲取IP IPAddress ip = IPAddress.Any; //創(chuàng)建端口號(hào) IPEndPoint port = new IPEndPoint(ip, Convert.ToInt32(txtPort.Text)); //監(jiān)聽(tīng) socketWatch.Bind(port); ShowMsg("監(jiān)聽(tīng)成功"); socketWatch.Listen(10); //新建線程,去接收客戶端發(fā)來(lái)的信息 Thread td = new Thread(AcceptMgs); td.IsBackground = true; td.Start(socketWatch); } catch { } } 在開(kāi)啟監(jiān)聽(tīng)按鈕里,我們建立了Socket,以及監(jiān)聽(tīng)的最大客戶端數(shù) socketWatch.Listen(10) 由于服務(wù)端會(huì)不停的去監(jiān)視接收客戶端發(fā)來(lái)的信息,如果把這個(gè)工作放到主線程里,程序會(huì)出現(xiàn)假死的現(xiàn)象,所以這里給他放到一個(gè)新的線程里。 (2)接收信息 /// <summary> /// 接收客戶端發(fā)送的信息 /// </summary> /// <param name="o"></param> private void AcceptMgs(object o) { try { Socket socketWatc = (Socket)o; while (true) { ////負(fù)責(zé)跟客戶端通信的Socket Socket socketSend = socketWatc.Accept(); //將遠(yuǎn)程連接的客戶端的IP地址和Socket存入集合中 dicSocket.Add(socketSend.RemoteEndPoint.ToString(), socketSend); //將遠(yuǎn)程連接的客戶端的IP地址和端口號(hào)存儲(chǔ)下拉框中 cboUsers.Items.Add(socketSend.RemoteEndPoint.ToString()); ShowMsg(socketSend.RemoteEndPoint.ToString() + ": 連接成功"); //新建線程循環(huán)接收客戶端發(fā)來(lái)的信息 Thread td = new Thread(Recive); td.IsBackground = true; td.Start(socketSend); } } catch { } } 接收信息是會(huì)根據(jù)接收到字節(jié)數(shù)字的第一個(gè)字節(jié)來(lái)判斷接收到的是什么,這個(gè)在方法Recive里進(jìn)行判斷: /// <summary> /// 接收客戶端發(fā)來(lái)的數(shù)據(jù),并顯示出來(lái) /// </summary> private void Recive(object o) { Socket socketSend = (Socket)o; try { while (true) { //客戶端連接成功后,服務(wù)器應(yīng)該接受客戶端發(fā)來(lái)的消息
if (socketSend == null) { MessageBox.Show("請(qǐng)選擇要發(fā)送的客戶端"); continue; } byte[] buffer = new byte[1024 * 1024 * 2]; //實(shí)際接受到的有效字節(jié)數(shù) int r = socketSend.Receive(buffer); //如果客戶端關(guān)閉,發(fā)送的數(shù)據(jù)就為空,然后就跳出循環(huán) if (r == 0) { break; } if (buffer[0] == 0) //如果接收的字節(jié)數(shù)組的第一個(gè)字節(jié)是0,說(shuō)明接收的字符串信息 { string strMsg = Encoding.UTF8.GetString(buffer, 1, r - 1); ShowMsg(socketSend.RemoteEndPoint.ToString() + ": " + strMsg); } else if (buffer[0] == 1) //如果接收的字節(jié)數(shù)組的第一個(gè)字節(jié)是1,說(shuō)明接收的是文件 { string filePath = ""; SaveFileDialog sfd = new SaveFileDialog(); sfd.Title = "保存文件"; sfd.InitialDirectory = @"C:\Users\Administrator\Desktop"; sfd.Filter = "文本文件|*.txt|圖片文件|*.jpg|視頻文件|*.avi|所有文件|*.*"; //如果沒(méi)有選擇保存文件路徑就一直打開(kāi)保存框 while (true) { sfd.ShowDialog(this); filePath = sfd.FileName; if (string.IsNullOrEmpty(filePath)) { continue; } else { break; } } //保存接收的文件 using (FileStream fsWrite = new FileStream(filePath, FileMode.OpenOrCreate, FileAccess.Write)) { fsWrite.Write(buffer, 1, r - 1); } ShowMsg(socketSend.RemoteEndPoint + ": 接收文件成功");
} else if (buffer[0] == 2) //如果接收的字節(jié)數(shù)組的第一個(gè)字節(jié)是2,說(shuō)明接收的是震動(dòng) { ZD(); } } } catch{} } (3)發(fā)送數(shù)據(jù)(這里分發(fā)送字符串、文件(包含大文件)、震動(dòng)) 發(fā)送字符串信息 /// <summary> /// 發(fā)送信息 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void btnSend_Click(object sender, EventArgs e) {
//獲得選中客戶端ip對(duì)應(yīng)的通信Socket if (cboUsers.SelectedItem == null) { MessageBox.Show("請(qǐng)選擇要發(fā)送的客戶端"); return; } Socket socketSend = dicSocket[cboUsers.SelectedItem.ToString()]; if (socketSend == null) { MessageBox.Show("請(qǐng)選擇要發(fā)送的客戶端"); return; } string strSend=txtMsg.Text; try { byte[] buffer = Encoding.UTF8.GetBytes(strSend); //獲得發(fā)送的信息時(shí)候,在數(shù)組前面加上一個(gè)字節(jié) 0 List<byte> list = new List<byte>(); list.Add(0); list.AddRange(buffer); //將泛型集合轉(zhuǎn)換為數(shù)組 byte[] newBuffer = list.ToArray(); //將了標(biāo)識(shí)字符的字節(jié)數(shù)組傳遞給客戶端 socketSend.Send(newBuffer); txtMsg.Text = ""; } catch { } } 發(fā)送震動(dòng) /// <summary> /// 震動(dòng) /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void btnZD_Click(object sender, EventArgs e) { //判斷是否選擇了要發(fā)送的客戶端 if (cboUsers.SelectedItem == null) { MessageBox.Show("請(qǐng)選擇要發(fā)送的客戶端"); return; } Socket socketSend = dicSocket[cboUsers.SelectedItem.ToString()]; if (socketSend == null) { MessageBox.Show("請(qǐng)選擇要發(fā)送的客戶端"); return; } try { // 首字節(jié)是2說(shuō)明是震動(dòng) byte[] buffer = new byte[1]; buffer[0] = 2; socketSend.Send(buffer); } catch {
}
}
/// <summary> /// 震動(dòng) /// </summary> private void ZD() { //獲取當(dāng)前窗體的坐標(biāo) Point point = this.Location; //反復(fù)給窗體坐標(biāo)復(fù)制一百次,達(dá)到震動(dòng)的效果 for (int i = 0; i < 100; i++) { this.Location = new Point(point.X - 5, point.Y - 5); this.Location = new Point(point.X + 5, point.Y + 5); } this.Location = point; } 發(fā)送文件(包含大文件) 首先要選擇文件 /// <summary> /// 選擇文件 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void btnSelect_Click(object sender, EventArgs e) { //打開(kāi)文件 OpenFileDialog ofd = new OpenFileDialog(); ofd.Title = "選擇要傳的文件"; ofd.InitialDirectory = @"C:\Users\Administrator\Desktop"; ofd.Filter = "文本文件|*.txt|圖片文件|*.jpg|視頻文件|*.avi|所有文件|*.*"; ofd.ShowDialog(); //得到選擇文件的路徑 txtPath.Text = ofd.FileName; } 然后在發(fā)送文件 /// <summary> /// 發(fā)送文件 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void btnSendFile_Click(object sender, EventArgs e) { //判斷是否選擇了要發(fā)送的客戶端 if (cboUsers.SelectedItem == null) { MessageBox.Show("請(qǐng)選擇要發(fā)送的客戶端"); return; } Socket socketSend = dicSocket[cboUsers.SelectedItem.ToString()]; if (socketSend == null) { MessageBox.Show("請(qǐng)選擇要發(fā)送的客戶端"); return; } string filePath = txtPath.Text; if (string.IsNullOrEmpty(filePath)) { MessageBox.Show("請(qǐng)選擇文件"); return; } Thread td = new Thread(SendBigFile); td.IsBackground = true; td.Start();
}
/// <summary> /// 大文件斷點(diǎn)傳送 /// </summary> private void SendBigFile() { string filePath = txtPath.Text; Socket socketSend = dicSocket[cboUsers.SelectedItem.ToString()]; try { //讀取選擇的文件 using (FileStream fsRead = new FileStream(filePath, FileMode.OpenOrCreate, FileAccess.Read)) { //1. 第一步:發(fā)送一個(gè)包,表示文件的長(zhǎng)度,讓客戶端知道后續(xù)要接收幾個(gè)包來(lái)重新組織成一個(gè)文件 long length = fsRead.Length; byte[] byteLength = Encoding.UTF8.GetBytes(length.ToString()); //獲得發(fā)送的信息時(shí)候,在數(shù)組前面加上一個(gè)字節(jié) 1 List<byte> list = new List<byte>(); list.Add(1); list.AddRange(byteLength); socketSend.Send(list.ToArray()); // //2. 第二步:每次發(fā)送一個(gè)4KB的包,如果文件較大,則會(huì)拆分為多個(gè)包 byte[] buffer = new byte[1024 * 1024]; long send = 0; //發(fā)送的字節(jié)數(shù) while (true) //大文件斷點(diǎn)多次傳輸 { int r = fsRead.Read(buffer, 0, buffer.Length); if (r == 0) { break; } socketSend.Send(buffer, 0, r, SocketFlags.None); send += r; ShowMsg(string.Format("{0}: 已發(fā)送:{1}/{2}", socketSend.RemoteEndPoint, send, length)); } ShowMsg("發(fā)送完成"); txtPath.Text = ""; } } catch {
} } 注意:(1)發(fā)送文件的時(shí)候會(huì)分兩步發(fā)送 :第一步:發(fā)送一個(gè)包,表示文件的長(zhǎng)度,讓客戶端知道后續(xù)要接收幾個(gè)包來(lái)重新組織成一個(gè)文件 第二步:每次發(fā)送一個(gè)1MB的包,如果文件較大,則會(huì)拆分為多個(gè)包 (2)每個(gè)客戶端連接服務(wù)端的啥時(shí)候,都會(huì)把客戶端的ip以及端口號(hào),放到下拉框里,想給那個(gè)客戶端發(fā)信息,就選擇對(duì)應(yīng)的客戶端 二、客戶端的建立 1.客戶端的項(xiàng)目建立以及頁(yè)面布局 客戶端的界面布局與服務(wù)端很像,就是把對(duì)應(yīng)的開(kāi)始監(jiān)聽(tīng)換成連接,當(dāng)然代碼也會(huì)有所改變,后面會(huì)講到····· 2.各功能按鍵的事件代碼 先把整個(gè)服客戶端的代碼貼出來(lái),然后我們?cè)谝灰恢v解 namespace SocketClient { public partial class Form1 : Form { //說(shuō)明:在傳遞信息的時(shí)候,會(huì)在需要傳遞的信息前面加一個(gè)字符來(lái)標(biāo)識(shí)傳遞的是不同的信息 // 0:表示傳遞的是字符串信息 // 1:表示傳遞的是文件信息 // 2:表示的是震動(dòng) /// <summary> /// 用來(lái)存放連接服務(wù)的IP地址和端口號(hào),對(duì)應(yīng)的Socket (這個(gè)為了以后的擴(kuò)展用,現(xiàn)在暫時(shí)沒(méi)用) /// </summary> Dictionary<string, Socket> dicSocket = new Dictionary<string, Socket>(); /// <summary> /// 存儲(chǔ)保存文件的路徑 /// </summary> string filePath = ""; /// <summary> /// 負(fù)責(zé)通信的Socket /// </summary> Socket socketSend; public Form1() { InitializeComponent(); } private void Form1_Load(object sender, EventArgs e) { //不檢測(cè)跨線程之間的空間調(diào)用 Control.CheckForIllegalCrossThreadCalls = false; } /// <summary> /// 建立連接 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void btnStart_Click(object sender, EventArgs e) { try { //創(chuàng)建負(fù)責(zé)通信的Socket socketSend = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); //獲取服務(wù)端的IP IPAddress ip = IPAddress.Parse(txtServer.Text.Trim()); //獲取服務(wù)端的端口號(hào) IPEndPoint port = new IPEndPoint(ip, Convert.ToInt32(txtPort.Text)); //獲得要連接的遠(yuǎn)程服務(wù)器應(yīng)用程序的IP地址和端口號(hào) socketSend.Connect(port); ShowMsg("連接成功"); //新建線程,去接收客戶端發(fā)來(lái)的信息 Thread td = new Thread(AcceptMgs); td.IsBackground = true; td.Start(); } catch { } } /// <summary> /// 接收數(shù)據(jù) /// </summary> private void AcceptMgs() { try { /// <summary> /// 存儲(chǔ)大文件的大小 /// </summary> long length = 0; long recive = 0; //接收的大文件總的字節(jié)數(shù) while (true) { byte[] buffer = new byte[1024 * 1024]; int r = socketSend.Receive(buffer); if (r == 0) { break; } if (length > 0) //判斷大文件是否已經(jīng)保存完 { //保存接收的文件 using (FileStream fsWrite = new FileStream(filePath, FileMode.Append, FileAccess.Write)) { fsWrite.Write(buffer, 0, r); length -= r; //減去每次保存的字節(jié)數(shù) ShowMsg(string.Format("{0}: 已接收:{1}/{2}", socketSend.RemoteEndPoint, recive-length, recive)); if (length <= 0) { ShowMsg(socketSend.RemoteEndPoint + ": 接收文件成功"); } continue; } } if (buffer[0] == 0) //如果接收的字節(jié)數(shù)組的第一個(gè)字節(jié)是0,說(shuō)明接收的字符串信息 { string strMsg = Encoding.UTF8.GetString(buffer, 1, r - 1); ShowMsg(socketSend.RemoteEndPoint.ToString() + ": " + strMsg); } else if (buffer[0] == 1) //如果接收的字節(jié)數(shù)組的第一個(gè)字節(jié)是1,說(shuō)明接收的是文件 { length = int.Parse(Encoding.UTF8.GetString(buffer,1,r-1)); recive = length; filePath = ""; SaveFileDialog sfd = new SaveFileDialog(); sfd.Title = "保存文件"; sfd.InitialDirectory = @"C:\Users\Administrator\Desktop"; sfd.Filter = "文本文件|*.txt|圖片文件|*.jpg|視頻文件|*.avi|所有文件|*.*"; //如果沒(méi)有選擇保存文件路徑就一直打開(kāi)保存框 while (true) { sfd.ShowDialog(this); filePath = sfd.FileName; if (string.IsNullOrEmpty(filePath)) { continue; } else { break; } } } else if (buffer[0] == 2) //如果接收的字節(jié)數(shù)組的第一個(gè)字節(jié)是2,說(shuō)明接收的是震動(dòng) { ZD(); } } } catch { }
} /// <summary> /// 顯示信息 /// </summary> /// <param name="message"></param> private void ShowMsg(string message) { txtLog.AppendText(message + "\r\n"); } /// <summary> /// 發(fā)送數(shù)據(jù) /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void btnSend_Click(object sender, EventArgs e) { try { byte[] buffer = Encoding.UTF8.GetBytes(txtMsg.Text); //獲得發(fā)送的信息時(shí)候,在數(shù)組前面加上一個(gè)字節(jié) 0 List<byte> list = new List<byte>(); list.Add(0); list.AddRange(buffer); //將泛型集合轉(zhuǎn)換為數(shù)組 byte[] newBuffer = list.ToArray(); //將了標(biāo)識(shí)字符的字節(jié)數(shù)組傳遞給客戶端 socketSend.Send(newBuffer); txtMsg.Text = ""; } catch{} } /// <summary> /// 選擇文件 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void btnSelect_Click(object sender, EventArgs e) { //打開(kāi)文件 OpenFileDialog ofd = new OpenFileDialog(); ofd.Title = "選擇要傳的文件"; ofd.InitialDirectory = @"C:\Users\Administrator\Desktop"; ofd.Filter = "文本文件|*.txt|圖片文件|*.jpg|視頻文件|*.avi|所有文件|*.*"; ofd.ShowDialog(); //得到選擇文件的路徑 txtPath.Text = ofd.FileName; } /// <summary> /// 發(fā)送文件 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void btnSendFile_Click(object sender, EventArgs e) { try { string filePath = txtPath.Text; if (string.IsNullOrEmpty(filePath)) { MessageBox.Show("請(qǐng)選擇文件"); return; } //讀取選擇的文件 using (FileStream fsRead = new FileStream(filePath, FileMode.OpenOrCreate, FileAccess.Read)) { byte[] buffer = new byte[1024 * 1024 * 2]; int r = fsRead.Read(buffer, 0, buffer.Length); //獲得發(fā)送的信息時(shí)候,在數(shù)組前面加上一個(gè)字節(jié) 1 List<byte> list = new List<byte>(); list.Add(1); list.AddRange(buffer); byte[] newBuffer = list.ToArray(); //將了標(biāo)識(shí)字符的字節(jié)數(shù)組傳遞給客戶端 socketSend.Send(newBuffer, 0, r + 1, SocketFlags.None); txtPath.Text = ""; } } catch{ } } /// <summary> /// 震動(dòng) /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void btnZD_Click(object sender, EventArgs e) { try { // 首字節(jié)是2說(shuō)明是震動(dòng) byte[] buffer = new byte[1]; buffer[0] = 2; socketSend.Send(buffer); } catch{ } } /// <summary> /// 震動(dòng) /// </summary> private void ZD() { //獲取當(dāng)前窗體的坐標(biāo) Point point = this.Location; //反復(fù)給窗體坐標(biāo)復(fù)制一百次,達(dá)到震動(dòng)的效果 for (int i = 0; i < 100; i++) { this.Location = new Point(point.X - 5, point.Y - 5); this.Location = new Point(point.X + 5, point.Y + 5); } this.Location = point; } } } 1)傳輸類(lèi)型說(shuō)明以及全局變量 這些說(shuō)明以及全局變量,說(shuō)的也比較清楚,也不累贅了。 2)Socket通信服務(wù)端具體步驟: (這些步驟都是根據(jù)第一個(gè)圖來(lái)的) (1)建立一個(gè)通信的Socket /// <summary> /// 建立連接 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void btnStart_Click(object sender, EventArgs e) { try { //創(chuàng)建負(fù)責(zé)通信的Socket socketSend = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); //獲取服務(wù)端的IP IPAddress ip = IPAddress.Parse(txtServer.Text.Trim()); //獲取服務(wù)端的端口號(hào) IPEndPoint port = new IPEndPoint(ip, Convert.ToInt32(txtPort.Text)); //獲得要連接的遠(yuǎn)程服務(wù)器應(yīng)用程序的IP地址和端口號(hào) socketSend.Connect(port); ShowMsg("連接成功"); //新建線程,去接收客戶端發(fā)來(lái)的信息 Thread td = new Thread(AcceptMgs); td.IsBackground = true; td.Start(); } catch { } } 在連接按鈕里,我們建立了Socket 由于客戶端會(huì)不停的去監(jiān)視接收服務(wù)端發(fā)來(lái)的信息,如果把這個(gè)工作放到主線程里,程序會(huì)出現(xiàn)假死的現(xiàn)象,所以這里給他放到一個(gè)新的線程里。 (2)接收信息 /// <summary> /// 接收數(shù)據(jù) /// </summary> private void AcceptMgs() { try { /// <summary> /// 存儲(chǔ)大文件的大小 /// </summary> long length = 0; long recive = 0; //接收的大文件總的字節(jié)數(shù) while (true) { byte[] buffer = new byte[1024 * 1024]; int r = socketSend.Receive(buffer); if (r == 0) { break; } if (length > 0) //判斷大文件是否已經(jīng)保存完 { //保存接收的文件 using (FileStream fsWrite = new FileStream(filePath, FileMode.Append, FileAccess.Write)) { fsWrite.Write(buffer, 0, r); length -= r; //減去每次保存的字節(jié)數(shù) ShowMsg(string.Format("{0}: 已接收:{1}/{2}", socketSend.RemoteEndPoint, recive-length, recive)); if (length <= 0) { ShowMsg(socketSend.RemoteEndPoint + ": 接收文件成功"); } continue; } } if (buffer[0] == 0) //如果接收的字節(jié)數(shù)組的第一個(gè)字節(jié)是0,說(shuō)明接收的字符串信息 { string strMsg = Encoding.UTF8.GetString(buffer, 1, r - 1); ShowMsg(socketSend.RemoteEndPoint.ToString() + ": " + strMsg); } else if (buffer[0] == 1) //如果接收的字節(jié)數(shù)組的第一個(gè)字節(jié)是1,說(shuō)明接收的是文件 { length = int.Parse(Encoding.UTF8.GetString(buffer,1,r-1)); recive = length; filePath = ""; SaveFileDialog sfd = new SaveFileDialog(); sfd.Title = "保存文件"; sfd.InitialDirectory = @"C:\Users\Administrator\Desktop"; sfd.Filter = "文本文件|*.txt|圖片文件|*.jpg|視頻文件|*.avi|所有文件|*.*"; //如果沒(méi)有選擇保存文件路徑就一直打開(kāi)保存框 while (true) { sfd.ShowDialog(this); filePath = sfd.FileName; if (string.IsNullOrEmpty(filePath)) { continue; } else { break; } } } else if (buffer[0] == 2) //如果接收的字節(jié)數(shù)組的第一個(gè)字節(jié)是2,說(shuō)明接收的是震動(dòng) { ZD(); } } } catch { } } 接收信息是會(huì)根據(jù)接收到字節(jié)數(shù)字的第一個(gè)字節(jié)來(lái)判斷接收到的是什么,如果接收的是個(gè)大文件,首先會(huì)接收大文件的大小,然后根據(jù)大小接收相同大小的字節(jié)數(shù)組追加保存到一個(gè)文件里去。 (3)發(fā)送數(shù)據(jù)(這里分發(fā)送字符串、文件(包含大文件)、震動(dòng)) 發(fā)送字符串信息 /// <summary> /// 發(fā)送數(shù)據(jù) /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void btnSend_Click(object sender, EventArgs e) { try { byte[] buffer = Encoding.UTF8.GetBytes(txtMsg.Text); //獲得發(fā)送的信息時(shí)候,在數(shù)組前面加上一個(gè)字節(jié) 0 List<byte> list = new List<byte>(); list.Add(0); list.AddRange(buffer); //將泛型集合轉(zhuǎn)換為數(shù)組 byte[] newBuffer = list.ToArray(); //將了標(biāo)識(shí)字符的字節(jié)數(shù)組傳遞給客戶端 socketSend.Send(newBuffer); txtMsg.Text = ""; } catch{} } 發(fā)送震動(dòng) /// <summary> /// 震動(dòng) /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void btnZD_Click(object sender, EventArgs e) { try { // 首字節(jié)是2說(shuō)明是震動(dòng) byte[] buffer = new byte[1]; buffer[0] = 2; socketSend.Send(buffer); } catch{ } }
/// <summary> /// 震動(dòng) /// </summary> private void ZD() { //獲取當(dāng)前窗體的坐標(biāo) Point point = this.Location; //反復(fù)給窗體坐標(biāo)復(fù)制一百次,達(dá)到震動(dòng)的效果 for (int i = 0; i < 100; i++) { this.Location = new Point(point.X - 5, point.Y - 5); this.Location = new Point(point.X + 5, point.Y + 5); } this.Location = point; } 發(fā)送文件(不包含大文件) 首先要選擇文件 /// <summary> /// 選擇文件 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void btnSelect_Click(object sender, EventArgs e) { //打開(kāi)文件 OpenFileDialog ofd = new OpenFileDialog(); ofd.Title = "選擇要傳的文件"; ofd.InitialDirectory = @"C:\Users\Administrator\Desktop"; ofd.Filter = "文本文件|*.txt|圖片文件|*.jpg|視頻文件|*.avi|所有文件|*.*"; ofd.ShowDialog(); //得到選擇文件的路徑 txtPath.Text = ofd.FileName; } 然后在發(fā)送文件 /// <summary> /// 發(fā)送文件 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void btnSendFile_Click(object sender, EventArgs e) { try { string filePath = txtPath.Text; if (string.IsNullOrEmpty(filePath)) { MessageBox.Show("請(qǐng)選擇文件"); return; } //讀取選擇的文件 using (FileStream fsRead = new FileStream(filePath, FileMode.OpenOrCreate, FileAccess.Read)) { byte[] buffer = new byte[1024 * 1024 * 2]; int r = fsRead.Read(buffer, 0, buffer.Length); //獲得發(fā)送的信息時(shí)候,在數(shù)組前面加上一個(gè)字節(jié) 1 List<byte> list = new List<byte>(); list.Add(1); list.AddRange(buffer); byte[] newBuffer = list.ToArray(); //將了標(biāo)識(shí)字符的字節(jié)數(shù)組傳遞給客戶端 socketSend.Send(newBuffer, 0, r + 1, SocketFlags.None); txtPath.Text = ""; } } catch{ } } 該文章在 2021/3/3 12:26:08 編輯過(guò)
|
關(guān)鍵字查詢
相關(guān)文章
正在查詢... |