发送和接收数据
合集下载
相关主题
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
12
在流中包装TCP套接字(2)
13
结构覆盖:对齐与填充(1)
构造包含二进制数据(即多字节整数)的 消息的最常用的方法是把C结构覆盖在一块 内存区域上,并直接分配给结构的字段。 例如地址信息结构如下: struct addressInfo { uint16_t streetAddress; //街道编号 int16_t aptNumber; //公寓编号 uint32_t postalCode; //邮编 14 } addrInfo;
25
成帧
成帧指的是允许接收者定位消息(或其一 部分)的边界的普遍问题。 两种常规技术可以让接收者明确地查找消 息的末尾:
基于定界符 显式长度
26
基于定界符
通过唯一标记指示消息的末尾。 基于定界符的方法的一个特例是:发送者 发送消息后关闭连接,接收者在读取消息 的最后一个字节后,会接收到一个流结束 标志。 基于定界符的方法通常用于编码为文本的 消息。 基于定界符方法的缺点是:消息自身绝对 不能包含定界符。或者采用转义符的方式, 27 但这将导致发送者和接收者必须扫描消息
21
位操作:编码布尔值
位图(bitmap)是编码布尔信息的非常简 洁的方式。位图的思想是:整数类型的每 一个位都可以编码一个布尔值(0或1)。 掩码(mask)是把一个或多个特定的位设 置为1并且清除所有其他的位。 位运算符 & | >> << ~
22
构造、成帧和解析消息
案例:一个简单的“投票”协议
客户发送一条请求(request)消息给服务器, 该消息包含候选人ID,请求类型,是否响应,投 票数。
23
客户端程序
步骤
建立TCP连接 将结构体VoteInfo型数据编码为发送字节序列
成帧并发送数据
接收返回响应信息 解码返回信息并打印
24
服务器程序
步骤
建立侦听套接字 反复接受连接和处理信息 等待连接 在流中包装套接字 接收并处理消息,直至连接关闭
通信应用软件设计
第五章
发送和接收数据
目标
掌握本机字节顺序以及网络字节顺序的区 别 掌握C语言对齐规则以及由此导致的问题 掌握消息成帧的两种方式:文本字符格式 和数据结构体方式。 掌握案例:一个简单的投票系统
2
概述
任何交换信息的程序都必须就如何编码信 息(表示为一个位序列)达成一致,即发 送信息是以何种格式,接收者必须以同样 的信息格式进行解析。这种关于通过通信 信道交换的信息的形式和含义的协定称为 协议(protocol)。 大多数应用程序协议是依据由字段(field) 序列构成的具体消息定义的。每个字段都 包含一份特定的编码为位序列的信息。应 用程序协议准确指定了发送者如何排列这 3 些序列,以及接收者如何解释或解析它们。
29
二进制消息编码(1)
使用数据结构体作为消息的传输格式。
30
二进制消息编码(2)
31
显式长度
在长度可变的字段或消息前附加一个长度 字段,指出包含多少个字节。长度字段一 般具有固定的大小:这会限制可以成帧的 消息的最大大小。 显式长度方法更简单,一般用于二进制消 息编码。
28
基于文本的消息编码
将投票消息表示为文本,由于消息中只需 要表示数字和两个指示器,因此可以使用 基本的C语言字符集ASCII。 空字符作为消息的定界符,每个消息的开 头是一个所谓的“魔术字符串”,它允许 接收者快速将消息设别为投票协议的消息, 并且与通过网络达到的随机垃圾消息区分 开。
9
符号性与符号扩展
负数在C语言中采用补码表示。 负数值赋给无符号的变量时将使负数的符 号位变量值的一部分。 在计算表达式的值时,首先要将变量的值 加宽到本机(int)大小。例如 char a, b ; sizeof(a+b) 的值为4
10
手工编码整数
程序BruteForceCoding.c中的 EncodeIntBigEndian使用大端表示法把任 何给定的基本整数值作为指定字节数的序 列存放在内存中的指定位置。 DecodeIntBigEndian方法用于处理将给定 长度的字节序列解码为64位的整数,并将 其解释为大端序列。 BruteForceCoding.c
7
字节排序(1)
对于需要多个字节进行编码的整数,必须 回答以哪种顺序发送字节的问题。 例如:123456787654321L的十六机制为 0x0000704885F926B1
00 00 70 48 85 F9 26 B1 大端 小端
8
字节排序(2)
网络字节顺序:使用大端字节顺序 本机字节顺序:可能是大端也可能是小端 顺序存储。 本机字节顺序与网络字节顺序之间转换函 数:htons htonl ntohs ntohl
18
隐式字节填充
如果在oneByte字段与twoBytes字段之间插 入一个字节的填充,那么就满足了字节对 齐规则。 由编译器添加为填充的字节内容是未定义 的。发送者以16字节发送,而接收者以15 字节接收,那么很可能出现不正确的结果。
19
显式字节填充
这个结构在内存中的布置方式与原来申明 的integerMessage完全一样,只不过程序 员现在可以控制和访问填充字节的内容。
4
wk.baidu.com
整数的大小(2)
通过TCP连接发送32位整数,在不同的平 台上可能存在不同的解释,可以解释为int, 也可以解释为long。 C99语言标准规范以一组可选类型的形式 提供了一种解决方案:int8_t int16_t int32_t int64_t。这样可以有效地解决整数 存储位数不一致的问题。
11
在流中包装TCP套接字(1)
编码多个字节的整数可以使用流的方式在 TCP套接字上传输。 FILE工具流:通过fdopen()调用把一个或 多个FILE流与套接字描述符相关联。
FILE *fdopen(int socketdes, const char *mode); int fclose(FILE *stream); int fflush(FILE *stream); frwrite(….) fread(…)
结构覆盖:对齐与填充(2)
发送结构体信息 (假设结构体已经赋值)
addrInfo.streetAddress = htons( addrInfo.streetAddress ) addrInfo.aptNumber = htons( addrInfo. aptNumber ) addrInfo.postalCode = htonl( addrInfo. postalCode ) if(send(sock, &addrinfo, sizeof(addrinfo),0) != sizeof(addrinfo)) ….
整数的大小(1)
在某种意义上,所有类型的信息最终都将 被编码为固定大小的整数。 C语言定义的几种整型:char short int long C语言没有指定这些整型的存储位数的准确 大小,而是把它留给实现来完成。因此整 型存储位数大小可能因平台而异。 计算整型存储位数的大小是:sizeof,以字 节为单位,例如: sizeof(int)。
20
字符串和文本
可打印(显示)的字符串是表示信息的最 常用的方式。 C语言的字符集为ASCII码。汉子等多于一 个字节进行编码的字符如何表示?C99扩 展标准定义了一种wchar_t(宽字符)类型, 用于存储可能为每个符号使用多个字节的 字符集中的字符。 函数wcstombs和mbstowcs用于支持字节 顺序与wchar_t的数组之间进行相互转换。
15
结构覆盖:对齐与填充(3)
使用结构体来接收数据
16
结构覆盖:对齐与填充(4)
一个15字节的消息结构体, sizeof(integerMessage)为16字节,而不是 15字节。这是因为C语言特定的对齐规则 造成的。
17
C语言的对齐规则
数据结构是最大限度地对齐,即任何一个 结构类型的变量的地址都可以被其最大的 本机整型字段除尽。 类型为多字节的整数类型要与它们的大小 对齐,即int32_t类型的变量开始地址总是 可以被4除尽,而一个uint16_t的开始地址 则保证可以被2除尽。
5
c99
c99是在c89的基础上发展起来的,增加了 基本数据类型,关键字和一些系统函数等。 其实在初学阶段C89(ANSI C)和C99的 区别是不易察觉的,所以不必太在意这个。 C99有一部分是对于大字符集的优化,还 加入了一些数据库函数,是C89之后的标 准,我们用的C是C89标准的,C++是C89 编写的,目前的C99标准其实在以前的编 译器中就或多或少的支持了,目前完全支 6 持的有这些:GCC、Borland C++等。
在流中包装TCP套接字(2)
13
结构覆盖:对齐与填充(1)
构造包含二进制数据(即多字节整数)的 消息的最常用的方法是把C结构覆盖在一块 内存区域上,并直接分配给结构的字段。 例如地址信息结构如下: struct addressInfo { uint16_t streetAddress; //街道编号 int16_t aptNumber; //公寓编号 uint32_t postalCode; //邮编 14 } addrInfo;
25
成帧
成帧指的是允许接收者定位消息(或其一 部分)的边界的普遍问题。 两种常规技术可以让接收者明确地查找消 息的末尾:
基于定界符 显式长度
26
基于定界符
通过唯一标记指示消息的末尾。 基于定界符的方法的一个特例是:发送者 发送消息后关闭连接,接收者在读取消息 的最后一个字节后,会接收到一个流结束 标志。 基于定界符的方法通常用于编码为文本的 消息。 基于定界符方法的缺点是:消息自身绝对 不能包含定界符。或者采用转义符的方式, 27 但这将导致发送者和接收者必须扫描消息
21
位操作:编码布尔值
位图(bitmap)是编码布尔信息的非常简 洁的方式。位图的思想是:整数类型的每 一个位都可以编码一个布尔值(0或1)。 掩码(mask)是把一个或多个特定的位设 置为1并且清除所有其他的位。 位运算符 & | >> << ~
22
构造、成帧和解析消息
案例:一个简单的“投票”协议
客户发送一条请求(request)消息给服务器, 该消息包含候选人ID,请求类型,是否响应,投 票数。
23
客户端程序
步骤
建立TCP连接 将结构体VoteInfo型数据编码为发送字节序列
成帧并发送数据
接收返回响应信息 解码返回信息并打印
24
服务器程序
步骤
建立侦听套接字 反复接受连接和处理信息 等待连接 在流中包装套接字 接收并处理消息,直至连接关闭
通信应用软件设计
第五章
发送和接收数据
目标
掌握本机字节顺序以及网络字节顺序的区 别 掌握C语言对齐规则以及由此导致的问题 掌握消息成帧的两种方式:文本字符格式 和数据结构体方式。 掌握案例:一个简单的投票系统
2
概述
任何交换信息的程序都必须就如何编码信 息(表示为一个位序列)达成一致,即发 送信息是以何种格式,接收者必须以同样 的信息格式进行解析。这种关于通过通信 信道交换的信息的形式和含义的协定称为 协议(protocol)。 大多数应用程序协议是依据由字段(field) 序列构成的具体消息定义的。每个字段都 包含一份特定的编码为位序列的信息。应 用程序协议准确指定了发送者如何排列这 3 些序列,以及接收者如何解释或解析它们。
29
二进制消息编码(1)
使用数据结构体作为消息的传输格式。
30
二进制消息编码(2)
31
显式长度
在长度可变的字段或消息前附加一个长度 字段,指出包含多少个字节。长度字段一 般具有固定的大小:这会限制可以成帧的 消息的最大大小。 显式长度方法更简单,一般用于二进制消 息编码。
28
基于文本的消息编码
将投票消息表示为文本,由于消息中只需 要表示数字和两个指示器,因此可以使用 基本的C语言字符集ASCII。 空字符作为消息的定界符,每个消息的开 头是一个所谓的“魔术字符串”,它允许 接收者快速将消息设别为投票协议的消息, 并且与通过网络达到的随机垃圾消息区分 开。
9
符号性与符号扩展
负数在C语言中采用补码表示。 负数值赋给无符号的变量时将使负数的符 号位变量值的一部分。 在计算表达式的值时,首先要将变量的值 加宽到本机(int)大小。例如 char a, b ; sizeof(a+b) 的值为4
10
手工编码整数
程序BruteForceCoding.c中的 EncodeIntBigEndian使用大端表示法把任 何给定的基本整数值作为指定字节数的序 列存放在内存中的指定位置。 DecodeIntBigEndian方法用于处理将给定 长度的字节序列解码为64位的整数,并将 其解释为大端序列。 BruteForceCoding.c
7
字节排序(1)
对于需要多个字节进行编码的整数,必须 回答以哪种顺序发送字节的问题。 例如:123456787654321L的十六机制为 0x0000704885F926B1
00 00 70 48 85 F9 26 B1 大端 小端
8
字节排序(2)
网络字节顺序:使用大端字节顺序 本机字节顺序:可能是大端也可能是小端 顺序存储。 本机字节顺序与网络字节顺序之间转换函 数:htons htonl ntohs ntohl
18
隐式字节填充
如果在oneByte字段与twoBytes字段之间插 入一个字节的填充,那么就满足了字节对 齐规则。 由编译器添加为填充的字节内容是未定义 的。发送者以16字节发送,而接收者以15 字节接收,那么很可能出现不正确的结果。
19
显式字节填充
这个结构在内存中的布置方式与原来申明 的integerMessage完全一样,只不过程序 员现在可以控制和访问填充字节的内容。
4
wk.baidu.com
整数的大小(2)
通过TCP连接发送32位整数,在不同的平 台上可能存在不同的解释,可以解释为int, 也可以解释为long。 C99语言标准规范以一组可选类型的形式 提供了一种解决方案:int8_t int16_t int32_t int64_t。这样可以有效地解决整数 存储位数不一致的问题。
11
在流中包装TCP套接字(1)
编码多个字节的整数可以使用流的方式在 TCP套接字上传输。 FILE工具流:通过fdopen()调用把一个或 多个FILE流与套接字描述符相关联。
FILE *fdopen(int socketdes, const char *mode); int fclose(FILE *stream); int fflush(FILE *stream); frwrite(….) fread(…)
结构覆盖:对齐与填充(2)
发送结构体信息 (假设结构体已经赋值)
addrInfo.streetAddress = htons( addrInfo.streetAddress ) addrInfo.aptNumber = htons( addrInfo. aptNumber ) addrInfo.postalCode = htonl( addrInfo. postalCode ) if(send(sock, &addrinfo, sizeof(addrinfo),0) != sizeof(addrinfo)) ….
整数的大小(1)
在某种意义上,所有类型的信息最终都将 被编码为固定大小的整数。 C语言定义的几种整型:char short int long C语言没有指定这些整型的存储位数的准确 大小,而是把它留给实现来完成。因此整 型存储位数大小可能因平台而异。 计算整型存储位数的大小是:sizeof,以字 节为单位,例如: sizeof(int)。
20
字符串和文本
可打印(显示)的字符串是表示信息的最 常用的方式。 C语言的字符集为ASCII码。汉子等多于一 个字节进行编码的字符如何表示?C99扩 展标准定义了一种wchar_t(宽字符)类型, 用于存储可能为每个符号使用多个字节的 字符集中的字符。 函数wcstombs和mbstowcs用于支持字节 顺序与wchar_t的数组之间进行相互转换。
15
结构覆盖:对齐与填充(3)
使用结构体来接收数据
16
结构覆盖:对齐与填充(4)
一个15字节的消息结构体, sizeof(integerMessage)为16字节,而不是 15字节。这是因为C语言特定的对齐规则 造成的。
17
C语言的对齐规则
数据结构是最大限度地对齐,即任何一个 结构类型的变量的地址都可以被其最大的 本机整型字段除尽。 类型为多字节的整数类型要与它们的大小 对齐,即int32_t类型的变量开始地址总是 可以被4除尽,而一个uint16_t的开始地址 则保证可以被2除尽。
5
c99
c99是在c89的基础上发展起来的,增加了 基本数据类型,关键字和一些系统函数等。 其实在初学阶段C89(ANSI C)和C99的 区别是不易察觉的,所以不必太在意这个。 C99有一部分是对于大字符集的优化,还 加入了一些数据库函数,是C89之后的标 准,我们用的C是C89标准的,C++是C89 编写的,目前的C99标准其实在以前的编 译器中就或多或少的支持了,目前完全支 6 持的有这些:GCC、Borland C++等。