编写基于TCP的应用程序
TCP是一种流式的协议, 简单的说, TCP不检查数据的语义, 更不会检查数据的边界, 而应用层一般使用的是报文协议, 所以会有所谓的"粘包""拆包"问题. 为此, 产生了一些特定的用法和模式.
任何应用程序, 都必须先进行报文协议设计. 虽然有些人捂上耳朵叫道"我不需要报文协议", 但是, 他还是需要进行报文协议设计. 有几种方式可用来设计报文协议:
1. 明确声明报文数据的长度.
2. 使用分隔符.
3. 发送方发送完数据后关闭连接.
第3种是socket的特定用法.
报文设计方法1: 明确声明报文数据的长度
此种方法一般较为常用, 因为兼容性好性能高. 一会介绍方法2的时候你就知道了. 一般会在数据的最前面用固定的几个字节存储一个二进制整数, 显示后面的数据的长度. 不过, 这是比较接近硬件底层报文协议设计. 应用层一般不这样, 在数据的前端固定几个字节存储ASCII数字, 前端补字符串'0', 或者在数字串后面跟换行符'\n', 这是一种和2的方法的混用.
报文设计方法2: 使用分隔符
前面介绍方法1的时候提过了, 使用分隔符来分隔报文, 然后在一般的语言都有 split() 函数, 用起来简单. 不过, 使用分隔符有一个缺点, 就是要进行数据转义, 避免报文数据中带有分隔符, 那就不好了. 此种方法还有一个缺点, 就是要遍历每一个字节, 查找分隔符, 性能不好. 介绍方法1的时候, 因为我们明确知道是数字串后面跟换行符, 所以不需要转义, 不会有转义性能损失, 同时数字串一般很短, 也可以忽略遍历性能损失.
报文设计方法3: 发送方发送完数据后关闭连接
这是 HTTP 1.0 采用的方式, HTTP 1.0 会在发送完响应后关闭连接(当然, 发送完请求后不能关闭连接, 所以可想而知, HTTP 1.0 必然使用方法1或者方法2, 你可以自己去学习了解). 这种方法不常用, 因为适用场景非常窄, 功能差.
很难被理解的常用的TCP应用程序惯用法:
1. 必须使用循环来发送数据
对于原始的socket, 发送数据的函数是write:
ssize_t write(int fd, const void *buf, size_t count);
但write可能只发送你请求的数据的前面一部分, 也就是说, write返回值(表示已发送的的字节数)可能小于参数中的count. 所以, 你应该在循环中调用 write, 并检查返回值. 请认真的看看 APUE(Advanced Programming in the UNIX)的相关内容.
2. 必须使用循环来接收数据
读取数据的接口函数:
ssize_t read(int fd, void *buf, size_t count);
我常常见到有些人, 因为没有完整地接收到的发送方发送的数据, 而报怨发送方调用了多次write方法. 这是一种错误的报怨, 基于对TCP的错误理解. *无论对方调用多少次write, 你都不能只调用一次read! 即使你把接收缓冲设置为1GB也不行!*
首先, 发送方调用write, 把数据拷贝到发送方的发送缓冲区, 然后发送方的网络子系统一段(fragment)一段地发送缓冲区中的数据. 接收方的网络子系统将这些数据片段按顺序组装到接收缓冲区中, 一旦进入接收缓冲, 就不存在片段的说法. 接收方调用read方法, 可能读取部分或者全部缓冲区中的数据后返回, 如果只是部分, 这部分的数据和分段没有任何联系 - 记住这一点!
3. 标准IO接口只调用一次fgets/fputs
标准IO的gets/puts向上提供了基于报文的接口, 它们检查缓冲区中数据的分隔符'\n', 以便分隔出报文. 所以, 当你只调用一次gets就能读取对方调用一次puts发送的数据时, 不要感到惊讶. 标准IO帮你封装了循环读和写.
4. 总是在字符串的结尾加上'\0'
如果你想把某一段字节数组当作C字符串来处理, 那么你必须手动地在字符串应该是'\0'的地方加上'\0'. 例如, 如果你认为ptr[0-5](共6个字节的数据, 最后一个字节的值应该是'\0')是一个字符串, 那么, 在进行处理之前, 应该执行ptr[5] = '\0'; 注意, 千万不要执行ptr[strlen(ptr)] = '\0'! 这样, 才能保证无论对方是无意或者恶意地没有包含'\0', 你都能安全地进行处理. 另外, 不必在接收前执行类似memset(ptr, 0, BUFLEN)的语句, 这样会浪费一丁点的性能, 只修改一个字节总比修改6个或者更多的字节速度更快.
Related posts:
编写基于TCP的应用程序的更多相关文章
- 《HBase in Action》 第三章节的学习总结 ---- 如何编写和运行基于HBase的MapReduce程序
HBase之所以与Hadoop是最好的伙伴,我理解就因为两点:1.HADOOP的HDFS,为HBase提供了分布式的存储方式:2.HADOOP的MR为HBase提供的分布式的计算方法.u 其中第一点, ...
- 03-案例——多任务版TCP服务端程序开发
案例——多任务版TCP服务端程序开发 1. 需求 目前我们开发的TCP服务端程序只能服务于一个客户端,如何开发一个多任务版的TCP服务端程序能够服务于多个客户端呢?完成多任务,可以使用线程 ...
- Java NIO通信的基础,基于TCP C/S例子介绍
为了更好的理解Netty异步事件驱动网络通信框架,有必要先了解一点Java NIO原生的通信理论,下面将结合基于TCP的例子程序,含客户端和服务端的源码,实现了Echo流程. Java NIO的核心概 ...
- 如何编写一个稳定的网络程序(TCP)
本节我们看一下怎样才能编写一个基于TCP稳定的客户端或者服务器程序,主要以试验抓包的方式观察数据包的变化,对网络中出现的多种情况进行分析,分析网络程序中常用的技术及它们出现的原因,在之后的编程中能早一 ...
- 基于TCP的字符串传输程序
---恢复内容开始--- LINUX中的网络编程是通过SOCKET接口来进行的. Socket(套接字) Socket相当于进行网络通信两端的插座,只要对方的Socket和自己的Socket有通信联接 ...
- 网络编程应用:基于TCP协议【实现一个聊天程序】
要求: 基于TCP协议实现一个聊天程序,客户端发送一条数据,服务器端发送一条数据 客户端代码: package Homework1; import java.io.IOException; impor ...
- Linux网络编程:基于TCP的程序开发回顾篇《转》
面向连接的TCP程序设计 基于TCP的程序开发分为服务器端和客户端两部分,常见的核心步骤和流程: 其实按照上面这个流程调用系统API确实可以完全实现应用层程序的开发,一点问题没有.可随着时间的推移,你 ...
- 初识Modbus TCP/IP-------------C#编写Modbus TCP客户端程序(一)
转自:http://blog.csdn.net/thebestleo/article/details/52269999 首先我要说明一下,本人新手一枚,本文仅为同样热爱学习的同学提供参考,有不 对的地 ...
- 模拟一个简单的基于tcp的远程关机程序(转)
最近在学习unix网络编程,现在正在学习tcp的通信.其实,只要建立起了tcp通信,操作远端的计算机就不是什么问题了.正向telnet一样,也是基于tcp/IP协议的.所以这个实验,也算是对telne ...
随机推荐
- prometheus学习系列十: Prometheus AlertManager配置文件说明
alertmanager配置文件说明 alertmanager是通过命令行标记和配置文件配置的,命令行标记配置不可变的系统参数,配置文件定义抑制规则.通知路由和通知接收器.可以通过官方提供的routi ...
- WingIDE Pro 7如何检查Python集成?
在开始使用某些代码之前,让我们确保Wing已成功找到您的Python安装.立即从“ 工具”菜单中打开Python Shell工具.如果一切顺利,它应该启动Python并向您显示Python命令提示符, ...
- JAVA线程池的创建与使用
为什么要用线程池? 我们都知道,每一次创建一个线程,JVM后面的工作包括:为线程建立虚拟机栈.本地方法栈.程序计数器的内存空间(下图可看出),所以线程过多容易导致内存空间溢出.同时,当频繁的创建和销毁 ...
- docker学习7-Dockerfile制作自己的镜像文件
前言 如果你是一个python自动化测试人员,某天你在公司终于完成了一个项目的接口自动化脚本工作,在你自己常用的本机或者服务器上调试完成了脚本,稳稳地没问题. 可是晚上下班回家,你自己找了个linux ...
- html中定位详解
首先,我们来讨论一下html中共有几种定位方式:静态定位(static),相对定位(relative),绝对定位(absolute,fixed).其中fixed又叫固定定位,它是属于绝对定位的一种,但 ...
- 201671030111 李蓉 实验十四 团队项目评审&课程学习总结
项目 内容 这个作业属于哪个课程 软件工程 这个作业的要求在哪里 实验十四 团队项目评审&课程学习总结 作业学习目标 掌握软件项目评审会流程,反思总结课程学习内容. 任务一:结合本学期课程学习 ...
- linux查找与替换练习
查找和替换-举例 删除/tmp/abc文件中第 2 至 5 行的内容 在第 2 行后面添加 123456 这一行 在文件的最后一行前面添加 123456 将文件中的 cat全部替换成 dog 注以上操 ...
- 11.07图论水题Test
11.07图论水题Test 题目 描述 做法 \(BSOJ6378\) 在\(i\)位置可以到\(i+a_i\)或\(i+b_i\)求\(1\rightarrow n\)字典序最小路径 判可达性后贪心 ...
- graphql-compose graphql schema 生成工具集
graphql-compose 是一个强大的graphql schema 生成工具集 包含以下特性 快速便捷的复杂类型生成 类型仓库,类型可以存储在schemacomposer 存储中 包含flowt ...
- 洛谷 P1786 帮贡排序 题解
P1786 帮贡排序 题目背景 在absi2011的帮派里,死号偏多.现在absi2011和帮主等人联合决定,要清除一些死号,加进一些新号,同时还要鼓励帮贡多的人,对帮派进行一番休整. 题目描述 目前 ...