Toggle navigation
首页
问答
文章
积分商城
专家
专区
更多专区...
文档中心
返回主站
搜索
提问
会员
中心
登录
注册
网络学习营
【LwIP学习营】【第二周telnet】获取天气预报
发布于 2018-05-26 14:28:06 浏览:2467
订阅该版
[tocm] # LWIP学习笔记 # 2018/5/24 ## TCP/IP 基础知识 ## ### TCP/IP 的分层 ### **应用层:**应用层决定了向用户提供应用服务时通信的活动。比如,FTP(FileTransfer Protocol, 文件传输协议) , DNS(Domain Name System, 域名系统) 服务以及 HTTP (Hypertext Transfer Protocol)协议。 **传输层:**传输层对上层应用层, 提供处于网络连接中的两台计算机之间的数据传输。在传输层有两个性质不同的协议: TCP(Transmission ControlProtocol, 传输控制协议) 和 UDP(User Data Protocol, 用户数据报协议) 。 **网络层:**网络层用来处理在网络上流动的数据包。 数据包是网络传输的最小数据单位。 该层规定了通过怎样的路径(所谓的传输路线) 到达对方计算机, 并把数据包传送给对方。与对方计算机之间通过多台计算机或网络设备进行传输时, 网络层所起的作用就是在众多的选项内选择一条传输路线。 **链路层:**用来处理连接网络的硬件部分。 包括控制操作系统、 硬件的设备驱动、 NIC(Network Interface Card, 网络适配器, 即网卡) , 及光纤等物理可见部分(还包括连接器等一切传输媒介) 。 硬件上的范畴均在链路层的作用范围之内。 | TCP/IP四层概念模型 |对应网络协议| | ---------- | --------- | | 应用层|HTTP、TFTP, FTP, NFS, WAIS、SMTP,Telnet, Rlogin, SNMP, Gopher,SMTP, DNS| | 传输层|TCP, UDP| | 网络层|IP, ICMP, ARP, RARP, AKP, UUCP| | 数据链路层|FDDI, Ethernet, Arpanet, PDN, SLIP, PPP,IEEE 802.1A, IEEE 802.2到IEEE 802.11| ### TCP/IP 通信传输流 ### ![](https://i.imgur.com/NEQ1OXS.png) ---------- 如下图所示,发送端在层与层之间传输数据时, 每经过一层时必定会被打上一个该层所属的首部信息。 反之, 接收端在层与层传输数据时, 每经过一层时会把对应的首部消去。这种把数据信息包装起来的做法称为封装(encapsulate) 。 ![](https://i.imgur.com/PcIELYT.png) ---------- ###ARP协议### 地址解析协议,是根据IP地址获取MAC地址的一个网络层协议。 ARP首先会发起一个请求数据包,数据包的首部包含了目标主机的IP地址,然后这个数据包会在链路层进行再次包装,生成以太网数据包,最终由以太网广播给子网内的所有主机,每一台主机都会接收到这个数据包,并取出标头里的IP地址,然后和自己的IP地址进行比较,如果相同就返回自己的MAC地址,如果不同就丢弃该数据包。ARP接收返回消息,以此确定目标机的MAC地址;与此同时,ARP还会将返回的MAC地址与对应的IP地址存入本机ARP缓存中并保留一定时间,下次请求时直接查询ARP缓存以节约资源。cmd输入 arp -a 就可以查询本机缓存的ARP数据。 ---------- ###IP 协议### 由于MAC地址只与厂商有关,与所处的网络无关,所以无法通过MAC地址来判断两台主机是否属于同一个子网,所以网络层引入了IP协议,制定了一套新地址,使得我们能够区分两台主机是否同属一个网络,这套地址就是网络地址,也就是所谓的IP地址。 IP地址目前有两个版本,分别是IPv4和IPv6,IPv4是一个32位的地址,常采用4个十进制数字表示。IP协议将这个32位的地址分为两部分,前面部分代表网络地址,后面部分表示该主机在局域网中的地址。由于各类地址的分法不尽相同,以C类地址192.168.24.1为例,其中前24位就是网络地址,后8位就是主机地址。因此, 如果两个IP地址在同一个子网内,则网络地址一定相同。为了判断IP地址中的网络地址,IP协议还引入了子网掩码, IP地址和子网掩码通过按位与运算后就可以得到网络地址。 由于发送者和接收者的IP地址是已知的(应用层的协议会传入), 因此我们只要通过子网掩码对两个IP地址进行AND运算后就能够判断双方是否在同一个子网了。 > ####如何判断是否在同一个子网:#### > 例子: 两个主机IP地址分别为202.117.179.158和202.117.179.60,子网掩码都为255.255.255.192 > > 子网掩码都为255.255.255.192,二进制表示形式11111111.11111111.11111111.11000000,子网掩码的前26为是1,所以只需要看IP地址前26位是否相同,相同为同一子网,否则不在。 > IP地址202.117.179.158 为202.117.179(前24位相同不需要写出来了).10011110 > IP地址202.117.179.60 为202.117.179(前24位相同不需要写出来了).00111100 > 看两个IP中二进制表示部分,前两位分别是10、00不相同,所以不在同一子网. > > **总结:子网掩码跟IP地址换算成二进制,然后与运算,结果相同者在同一子网.** ---------- ###TCP 协议采用了"三次握手连接"与"四次挥手断开"### **建立连接的过程**: TCP 连接是通过三次握手进行初始化的。三次握手的目的是同步连接双方的序列号和确认号并交换 TCP 窗口大小信息。以下步骤概述了通常情况下客户端计算机联系服务器计算机的过程: 1. 客户端向服务器发送一个SYN置位的TCP报文,其中包含连接的初始序列号x和一个窗口大小(表示客户端上用来存储从服务器发送来的传入段的缓冲区的大小)。 2. 服务器收到客户端发送过来的SYN报文后,向客户端发送一个SYN和ACK都置位的TCP报文,其中包含它选择的初始序列号y、对客户端的序列号的确认x+1和一个窗口大小(表示服务器上用来存储从客户端发送来的传入段的缓冲区的大小)。 3. .客户端接收到服务器端返回的SYN+ACK报文后,向服务器端返回一个确认号y+1和序号x+1的ACK报文,一个标准的TCP连接完成。 **断开连接的过程**: 由于TCP连接是全双工的,因此每个方向都必须单独进行关闭。这个原则是当一方完成它的数据发送任务后就能发送一个FIN来终止这个方向的接。收到一个 FIN只意味着这一方向上没有数据流动,一个TCP连接在收到一个FIN后仍能发送数据。首先进行关闭的一方将执行主动关闭,而另一方执行被动关闭。 1. 第一次挥手:主机1(可以使客户端,也可以是服务器端),设置Sequence Number和Acknowledgment Number,向主机2发送一个FIN报文段;此时,主机1进入FIN_WAIT_1状态;这表示主机1没有数据要发送给主机2了; 2. 第二次挥手:主机2收到了主机1发送的FIN报文段,向主机1回一个ACK报文段,Acknowledgment Number为Sequence Number加1;主机1进入FIN_WAIT_2状态;主机2告诉主机1,我“同意”你的关闭请求; 3. 第三次挥手:主机2向主机1发送FIN报文段,请求关闭连接,同时主机2进入LAST_ACK状态; 4. 第四次挥手:主机1收到主机2发送的FIN报文段,向主机2发送ACK报文段,然后主机1进入TIME_WAIT状态;主机2收到主机1的ACK报文段以后,就关闭连接;此时,主机1等待2MSL后依然没有收到回复,则证明Server端已正常关闭,那好,主机1也可以关闭连接了。 **建立TCP需要三次握手才能建立,而断开连接则需要四次握手。整个过程如下图所示:** ![](https://i.imgur.com/vUCiEXY.png) ---------- ###域名解析的 DNS 服务### DNS(Domain Name System) 服务是位于应用层的协议。 用户通常使用主机名或域名来访问对方的计算机, 而不是直接通过 IP地址访问。 因为与 IP 地址的一组纯数字相比, 用字母配合数字的表示形式来指定计算机名更符合人类的记忆习惯。DNS 协议提供通过域名查找 IP 地址, 或逆向从 IP 地址反查域名的服务。 ---------- > **HTTP请求头详解** [https://blog.csdn.net/zdw19861127/article/details/53444142](https://blog.csdn.net/zdw19861127/article/details/53444142) ---------- ## 小挑战:获取天气预报 ## 在原来TCPClient的基础上稍加修改. ---------- ``` #include
#include
/* 使用BSD socket,需要包含socket.h头文件 */ #include "netdb.h" #include "lwip/sockets.h" #include "cJSON.h" #define BUFSZ 1024 static char *recv_data; static int sock = -1, bytes_received; static const char send_data[] = "GET /data/sk/101210202.html
" "Host: mobile.weather.com.cn
" ; void tcpweatherclient(const char *url, int port) { struct sockaddr_in server_addr; struct hostent *host; /*用于通过DNS解析服务器端信息*/ /* 通过函数入口参数url获得host地址(如果是域名,会做域名解析) */ host = gethostbyname(url); /* 分配用于存放接收数据的缓冲 */ recv_data = rt_malloc(BUFSZ); if (recv_data == RT_NULL) { rt_kprintf("No memory
"); return; } /* 创建一个socket,类型是SOCKET_STREAM,TCP类型 */ if ((sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) { /* 创建socket失败 */ rt_kprintf("Socket error
"); /* 释放接收缓冲 */ rt_free(recv_data); return; } /* 初始化预连接的服务端地址 */ server_addr.sin_family = AF_INET; server_addr.sin_port = htons(port); server_addr.sin_addr = *((struct in_addr *)host->h_addr); /* 主机IP信息*/ rt_memset(&(server_addr.sin_zero), 0, sizeof(server_addr.sin_zero)); /* 连接到服务端 */ if (connect(sock, (struct sockaddr *)&server_addr, sizeof(struct sockaddr)) == -1) { /* 连接失败 */ rt_kprintf("
Connect fail!
"); closesocket(sock); sock = -1; /*释放接收缓冲 */ rt_free(recv_data); return; } rt_kprintf("
connect success!
"); send(sock, send_data, strlen(send_data), 0); while (1) { /* 从sock连接中接收最大BUFSZ - 1字节数据 */ bytes_received = recv(sock, recv_data, BUFSZ - 1, 0); /* 如果返回的<=0这说明出错,此时需要断开连接. */ if (bytes_received <= 0) { /* 接收失败,关闭这个连接 */ closesocket(sock); sock = -1; rt_kprintf("
close the socket.
"); /* 释放接收缓冲 */ rt_free(recv_data); break; } cJSON *root = cJSON_Parse(recv_data); cJSON *object = cJSON_GetObjectItem(root, "sk_info"); cJSON *item = cJSON_GetObjectItem(object, "cityName"); rt_kprintf("
cityName:%s ", item->valuestring); item = cJSON_GetObjectItem(object, "temp"); rt_kprintf("
temp :%s ", item->valuestring); item = cJSON_GetObjectItem(object, "wd"); rt_kprintf("
wd :%s ", item->valuestring); item = cJSON_GetObjectItem(object, "ws"); rt_kprintf("
ws :%s ", item->valuestring); item = cJSON_GetObjectItem(object, "sd"); rt_kprintf("
sd :%s ", item->valuestring); } return; } void tcp_weather_thread(void *parameter) { rt_kprintf("
please waiting connect.
"); tcpweatherclient("mobile.weather.com.cn", 80); rt_kprintf("
disconnect.
"); } int weather_init() { rt_thread_t tid; /* 创建test线程 */ tid = rt_thread_create("weather", tcp_weather_thread, RT_NULL, 1024, 10, 20); /* 创建成功则启动线程 */ if (tid != RT_NULL) rt_thread_startup(tid); return 0; } void weather(int argc, char **argv) { if (argc >= 1) { weather_init(); } else { rt_kprintf("warning !
"); } } MSH_CMD_EXPORT(weather, weather); ``` ## 解析JSON步骤 ## 1. 调用cJSON_Parse()函数,解析JSON数据包。cJSON*root=cJSON_Parse(json_string); 2. 调用一次cJSON_GetObjectItem()函数,获取到对象person。cJSON *object=cJSON_GetObjectItem(root,"person"); 3. 如果需要使用cJSON结构体中的内容,可通过cJSON结构体中的valueint和valuestring取出有价值的内容(即键的值) 示例如下: ``` cJSON*item; PERSONperson; item=cJSON_GetObjectItem(object,"firstName"); memcpy(person.firstName,item->valuestring,strlen(item->valuestring)); item=cJSON_GetObjectItem(object,"lastName"); memcpy(person.lastName,item->valuestring,strlen(item->valuestring)); item=cJSON_GetObjectItem(object,"email"); memcpy(person.email,item->valuestring,strlen(item->valuestring)); item=cJSON_GetObjectItem(object,"age"); person.age=item->valueint; item=cJSON_GetObjectItem(object,"height"); person.height=item->valuedouble; ``` 实验效果: ![w天气预报.png](https://oss-club.rt-thread.org/uploads/201805/26/143349ss55h5pwt33ss38l.png)
查看更多
2
个回答
默认排序
按发布时间排序
来一颗糖
2018-05-26
这家伙很懒,什么也没写!
天气预报
a741627265
2018-08-20
这家伙很懒,什么也没写!
你上面举得例子中 判断是否在同一子网。明明网络地址相同,主机地址不同而已,怎么就不在同一网段了,是不是笔误?
撰写答案
登录
注册新账号
关注者
0
被浏览
2.5k
关于作者
来一颗糖
这家伙很懒,什么也没写!
提问
7
回答
231
被采纳
1
关注TA
发私信
相关问题
1
【LWIP学习营】第一关开发环境搭建
2
LWIP学习营第一周入门移植问题汇总贴
3
【LWIP学习营】f407+lan8720A小结
4
【LwIP学习营】【第一周】仅零散记录,无主题
5
【LWIP学习营】正点原子探索者F407+LAN8720第一周小结
6
【LwIP学习营】【第一周】网络通信基础及实现TCP 聊天客户端
7
【LwIP学习营】【第一周】LWIP移植
8
【LwIP学习营】【第一周】LWIP移植
9
【LwIP学习营】【第一周】开发板适配
10
【LwIP学习营】【第一周】环境搭建和配置验证
推荐文章
1
RT-Thread应用项目汇总
2
玩转RT-Thread系列教程
3
国产MCU移植系列教程汇总,欢迎查看!
4
机器人操作系统 (ROS2) 和 RT-Thread 通信
5
五分钟玩转RT-Thread新社区
6
【技术三千问】之《玩转ART-Pi》,看这篇就够了!干货汇总
7
关于STM32H7开发板上使用SDIO接口驱动SD卡挂载文件系统的问题总结
8
STM32的“GPU”——DMA2D实例详解
9
RT-Thread隐藏的宝藏之completion
10
【ART-PI】RT-Thread 开启RTC 与 Alarm组件
最新文章
1
使用RC522软件包驱动FM1722
2
常量数据类型和表达式陷阱分享
3
进行i2c驱动移植的经验总结
4
在VSCode中使用clang-format
5
我该如何使用这个微雪的WIFI400 WIFI-LPB-100在rtt里或者我该怎样为它开发驱动
热门标签
RT-Thread Studio
串口
Env
LWIP
SPI
AT
Bootloader
Hardfault
CAN总线
FinSH
ART-Pi
USB
DMA
文件系统
RT-Thread
SCons
RT-Thread Nano
线程
MQTT
STM32
RTC
FAL
rt-smart
ESP8266
I2C_IIC
UART
WIZnet_W5500
ota在线升级
freemodbus
PWM
flash
cubemx
packages_软件包
BSP
潘多拉开发板_Pandora
定时器
ADC
GD32
flashDB
socket
中断
编译报错
Debug
rt_mq_消息队列_msg_queue
SFUD
msh
keil_MDK
ulog
C++_cpp
MicroPython
本月问答贡献
出出啊
1517
个答案
342
次被采纳
小小李sunny
1444
个答案
289
次被采纳
张世争
809
个答案
175
次被采纳
crystal266
547
个答案
161
次被采纳
whj467467222
1222
个答案
148
次被采纳
本月文章贡献
catcatbing
3
篇文章
5
次点赞
qq1078249029
2
篇文章
2
次点赞
xnosky
2
篇文章
1
次点赞
Woshizhapuren
1
篇文章
5
次点赞
YZRD
1
篇文章
2
次点赞
回到
顶部
发布
问题
分享
好友
手机
浏览
扫码手机浏览
投诉
建议
回到
底部