高分求C#使用TCP发送或接收数据不全,数据重组
用TCP接收数据的时候,经常是发送一次,接收却需要两次,这样便要对接收的数据进行重组,但目前还没想到如何进行数据的重组,望高人指点,最好说的越详细越好!
[解决办法]
我都是自己用Socket来接收数据,没有用过NetStream这种方式。至少我可以肯定,如果用Socket接收byte[]形式的数据,在网络通信正常的情况下,绝对不会出现你说的情况。
你说的情况我要测试一下NetStream的情况后再处理看是不是它有什么特殊的处理,不过我觉得你还是先检查一下自己的通信过程是否有一些比如多线程同步的问题没有处理好。
[解决办法]
看到这个问题,为了回顾一下以前学过的这些知识,刚刚写了个最最简单的通信,我用了2种方法,一种是用socket,另一种是用TcpListener,而且我都只写了服务端,希望对你有所帮助
*******第一个用socket
Socket server; Socket client; Thread s; IPAddress ip = IPAddress.Parse("127.0.0.1"); int prot = 6000; /// <summary> /// 开始监听 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void button1_Click(object sender, EventArgs e) { //输入IP try { ip = IPAddress.Parse(textBox1.Text); } catch { textBox3.Text = "你输入的IP地址格式不正确,请重新输入!"; } //输入端口 try { int i = Int32.Parse(textBox2.Text); if (i >= 0 && i <= 65535) { prot = i; } else { textBox3.Text = "请输入0--65535之间的数字"; } } catch { textBox3.Text = "请输入端口号!"; } //开始监听 try { s = new Thread(new ThreadStart(start)); s.Start(); } catch (Exception ee) { textBox3.Text = ee.Message; } } /// <summary> /// 监听请求 /// </summary> void start() { //创建套接字 server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); //绑定到本地 IPEndPoint ipPoint = new IPEndPoint(ip, prot); server.Bind(ipPoint); //开始侦听 server.Listen(5); //接受客户端请求 client = server.Accept(); textBox3.Text = "主机" + Dns.GetHostName() + "端口" + textBox2.Text + "开始监听........"; if (client.Connected) { textBox3.Text = "与客户端建立了连接"; while (true) { //接受数据 byte[] data = new byte[1024]; int size = client.Receive(data); string result = Encoding.Unicode.GetString(data, 0, size); if (result == "[---===退出===---]") { textBox3.Text = "你失去了与客户端的联系"; server.Shutdown(SocketShutdown.Both); server.Close(); } //richTextBox1.AppendText(result); else { string[] aa = result.Split('~'); richTextBox1.AppendText(aa[0] + "说:" + aa[1] + "\r"); } } } } private void button4_Click(object sender, EventArgs e) { try { //发送数据 //byte[] msg = Encoding.Unicode.GetBytes(richTextBox2.Text); byte[] msg = Encoding.Unicode.GetBytes(Dns.GetHostName() + "~" + richTextBox2.Text); client.Send(msg); //这里既可以将BYTE类型的数组从新解码再输出也可以直接输出richTextBox2中的文本 string aa = Encoding.Unicode.GetString(msg); //richTextBox1.AppendText("本机" + aa + "\r"); richTextBox1.AppendText(Dns.GetHostName() + "说:" + richTextBox2.Text + "\r"); richTextBox2.Clear(); } catch { textBox3.Text = "尚未进行连接,无法发送信息!"; } } private void button2_Click(object sender, EventArgs e) { richTextBox2.Clear(); } private void button3_Click(object sender, EventArgs e) { if (MessageBox.Show("你是否打开了监听?", "提示信息", MessageBoxButtons.YesNo, MessageBoxIcon.Information) == DialogResult.Yes) { if (MessageBox.Show("你确定要关闭窗体吗?", "提示信息", MessageBoxButtons.YesNo, MessageBoxIcon.Information) == DialogResult.Yes) { try { server.Shutdown(SocketShutdown.Both); server.Close(); Application.Exit(); } catch { textBox3.Text = "你尚未打开监听,此操作无效"; } } } else { Application.Exit(); } }
[解决办法]
TCP与UDP的差别就在于前者是可靠的面向连接的流协议,后者是不可靠的无连接的包协议。
所以,采用TCP协议发送的数据,到达接收端时只保证字节流的顺序,但是每一个字节何时到达是不能预知的。
而UDP发送的数据,是作为一个完整的包到达接收端的,不会出现分包的问题。
因此,为了接收TCP协议的数据包,必须由应用层判断数据包的开始和结束。
技术上有很多种手段,比如采用固定的起始、结束标志,或者按照固定的数据结构来解析。
[解决办法]
fengyecsdn说的很对!我说一下我的方法,也是学别人的:
发送端:每次发送正式数据前,先计算一下要发送的字节数(长度),然后先发送这个长度值(设定为4个字节),然后接着发送数据;
接收端:先接受4个字节,转化为长度值,然后再始接受。
[解决办法]
其实 就是你一顶要有自己的报头
我一般是这样
协议标记(4字节,一个INT32)本协议内是固定的
频道号(4字节,一个INT32)我的个人习惯,我一般将一套程序的不同部分的数据传输用频道区分,这样在数据重组和转发上很方便
指令标记(4字节,一个INT32)本协议内制定了几种,但总归是有限的几种或者只有一种
数据体加密压缩KEY(8字节,一个LONG)根据需要这里存的是用来加解密或者压缩的KEY值。
数据包本身长度(8字节,一个LONG)就是有效数据的长度(我一般是实际长度然后补加上N个空字节,使数据体长度成为16字节的倍数 N=16+x ,X是16减去数据体本身长度除16的余数的结果)。
保留段(4字节,一个INT32)备用其他特色信息
数据体(N字节,N是16的整数倍数) 比如数据体序列化本身为 165字节,那么数据体包装后是192字节(165+11+16)
加上数据报头的32字节 一共发送224字节
接收放先读32字节头 分析后再读192