CSharp中Socket网络编程(三)自定义通信传输协议 | 您所在的位置:网站首页 › 网络协议类型和代码 › CSharp中Socket网络编程(三)自定义通信传输协议 |
第一部分:整个通信过程梳理;文末附全套代码;
第二部分:重要bug完善;
本篇为第三部分:消息类型编码:自定义通信协议。
第四部分:全套代码
通信协议这事:
我们希望在通信中除了传输信息以外,还可以发送图片,还可以实现窗口震动。此时涉及发送数据的类型问题。对于接收方来说,并不知道发送方发送出来的格式,因此如果按照原来解码为字符串类型则不再适用。这事需要双方事先约定好。 发送以0开头的字节数组代表字符串类型,发送以1开头的字节数组代表文件,发送以2开头的字节数组代表震动。 0:发送消息:我们原来的代码如此。点击按钮,直接将对话框中的文本转为字节流发送。 private void btnSend_Click(object sender, EventArgs e) { string str = txtMsg.Text; byte[] buffer = Encoding.UTF8.GetBytes(str); dicSocket[cboUsers.SelectedItem.ToString()].Send(buffer); }当前需要在字节之前加上一位0,表示为传输的为字符串。 数组的长度一旦声明就不可改变了。 为此需要声明一个新数组,将协议类型头数据写入,然后再接上原来的数组。实现字节编码。然后解码的时候,先解析字节数组中的协议类型头数据。 解决方案一:新数组的长度为buffer.Length+1。然后NewBuffer[0]=0。将剩余的buffer值循环,赋值给newBuffer。 解决方案二:数组的长度不可改变,但是泛型集合的长度可以改变。并且可以将数组添加到集合中。并且可以将集合转化为数组。 采用第二种方案最简单。 private void btnSend_Click(object sender, EventArgs e) { string str = txtMsg.Text; byte[] buffer = Encoding.UTF8.GetBytes(str); List list = new List(); list.Add(0); list.AddRange(buffer); // 将泛型集合转化为数组。 byte[] NewBuffer = list.ToArray(); //buffer = list.ToArray(); // 因为类型长度不同,无法赋值。所以新建。 // dicSocket[cboUsers.SelectedItem.ToString()].Send(buffer); dicSocket[cboUsers.SelectedItem.ToString()].Send(NewBuffer); }那么相应的接受端也要先解析第一个字节数组情况。注意一点:在转码时,转码长度为r-1。 void Receive() { try { while (true) { byte[] buffer = new byte[1024 * 1024 * 10]; int r = socketSend.Receive(buffer); if (r == 0) { break; } if (buffer[0]==0) { string str = Encoding.UTF8.GetString(buffer, 1, r-1); ShowMsg(socketSend.RemoteEndPoint.ToString() + ":" + str); } else if (buffer[0]==1) { // 见下面文件传输 } else if (buffer[0]==2) { // 见下面抖动 } } } catch { } } 1:发送文件首先通过【选择文件】按钮将文件选中,然后显示在文本框中,然后点击【发送文件】按钮将文件发出。接受端(客户端)解析字节头,识别1后,打开对话框,选择保存文件路径。并保存文件。 设置按钮显示文件目录,选中文件并显示: private void btnSelect_Click(object sender, EventArgs e) { OpenFileDialog ofd = new OpenFileDialog(); ofd.InitialDirectory = @"C:\Users\futurecloud\Desktop"; ofd.Title="请选择你要传输的文件"; ofd.Filter = "所有文件|*.*"; ofd.ShowDialog(); //this.txtPath.Text = ofd.SafeFileName; txtPath.Text = ofd.FileName; }为了发送文件,首先创建一个文件流来读取文件。然后将字节流数组前加上标识符号1,发送给客户端。此操作在【发送文件】中完成。 private void btnSendFile_Click(object sender, EventArgs e) { string path = txtPath.Text; using (FileStream fsRead=new FileStream(path, FileMode.Open, FileAccess.Read)) { byte[] buffer = new byte[1024 * 1024 * 10]; int r = fsRead.Read(buffer, 0, buffer.Length); List list = new List(); list.Add(1); list.AddRange(buffer); byte[] newBuffer = list.ToArray(); // send有很多重载:有限制大小的。 dicSocket[cboUsers.SelectedItem.ToString()].Send(newBuffer, 0, r+1, SocketFlags.None); } }服务器端完成发送,客户端需要将解码,并打开一个保存文件对话框。 // 接续上面Recive()中的代码。 else if (buffer[0]==1) { SaveFileDialog sfd = new SaveFileDialog(); sfd.InitialDirectory = @"C:\Users\futurecloud\Desktop"; sfd.Title = "请选择你要保存的文件路径:"; sfd.Filter = "所有文件|*.*"; sfd.ShowDialog(this); // 不加这个this这个框不弹出来。 // 写入文件: string path = sfd.FileName; using (FileStream fsWrite = new FileStream(path, FileMode.OpenOrCreate, FileAccess.Write)) { fsWrite.Write(buffer,1,r-1); } //ShowMsg("写入文件成功。"); MessageBox.Show("文件写入成功"); }至此文件发送功能完成:后续可以进一步完善的方案:在1后面继续添加一位:如果是10则为图片,如果是11则是txt,如果是12则是word。等等。依次类推。 2:震动点击震动,只需要传输一个2过去就够了。 private void btnZD_Click(object sender, EventArgs e) { byte[] buffer = new byte[1]; buffer[0] = 2; dicSocket[cboUsers.SelectedItem.ToString()].Send(buffer); }如何实现震动效果,说白了,就是在一定时间内,让窗体震动,或者,指定窗体震动次数。 /// /// 实现震动效果 /// void ZD() { for (int i = 0; i < 500; i++) { //this.Location = new Point(200, 200); //this.Location = new Point(210, 210); this.Location = new Point(this.Location.X, this.Location.Y); this.Location = new Point(this.Location.X+10, this.Location.Y+10); this.Location = new Point(this.Location.X-10, this.Location.Y-10); } } else if (buffer[0]==2) { ZD(); }如果有其他设置,一样可以修改字节数组来改变。 我有一个想法:(可能TCP用不到)我们在传输的时候有丢包的担心,所以 我们给我们要发送的报文编号,如果接收方接收到的报文编号不连续,则要求重新发送某个缺失报文。 场景:每次数据更新的时候发送更新的点位数据。 [编号0]+更新数据 [编号1]+更新数据 ...... [编号n]+更新数据 接收方:在接收到某个报文[n]后,先查看编号列表,如果编号[n-1]已经在了,则直接解析,如果[n-1]不存在,则要求重新发送编号[n-1]的报文,同时查看[n-2]是否存在。。以此类推。 类推几个看需求。有时候类推一个,有时候全部类推。 |
CopyRight 2018-2019 实验室设备网 版权所有 |