马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有账号?立即注册
×
概述
IP 所在与域名
IP 所在是网络中的主机所在,用于两台网络主机可以大概相互找到相互,这也是网络通讯可以大概乐成举行的根本。IP 所在一样平常以点分十进制的字符串来表现,如192.168.1.1。
我们一样平常访问的网站,其所在的服务器主机都有唯一的 IP 所在,网络中的主机不可胜数,靠记 IP 所在的方式来区分差异的主机显然比力困难,而且同一个网站大概有多个差异的 IP 所在,大概 IP 所在会由于某种缘故原由而更换。
因此,用域名表现网站所在的方式便应运而生,如我们常见的www.baidu.com比 IP 所在更容易被记着。由于实际的网络通讯报文中利用的仍然是 IP 所在,以是须要利用域名分析协议去获取域名背后所对应的 IP 所在。
下文的解说均以 IPv4 协议为根本。
OSI 七层模子
国际尺度化构造(ISO)订定的一个用于盘算机或通讯体系的尺度体系,一样平常被称为 OSI(Open System Interconnection)七层模子。它为网络通讯协议的实现提供了一个尺度,通讯两边在雷同的层利用雷同的协议,即可举行通讯;就同一台装备而言,下层协议为上层协议提供了调用接口,将上层协议打包为底层协议,终极发送到网络上举行传输。
这七层分别为:应用层、表现层、会话层、传输层、网络层、数据链路层、物理层。
为了简化协议实现大概方便明确,五层模子大概四层模子的概念也诞生了。四层模子一样平常被提及的比力多,包罗:应用层、传输层、网络层、网络接口层。
上文中的 IP 所在则属于网络层。
网络层用于把该主机全部的网络数据转发到网卡,颠末物理层电路发送到网络中去。
为了方便论述,下文将按照四层模子来举行解说。
传输层协议
IP 所在办理了网络中两台主机怎样可以大概找到相互,进而举行报文收发的题目。
试想下,一台主机上大概运行着多个应用步伐,实验着差异的网络任务。这时,某台 IP 所在的主机收到了另一台主机的报文,这个报文数据要通报给哪个应用步伐呢?
为相识决这个题目,人们基于网络层协议演化出了传输层协议,传输层协议为当地的网络应用分配差异的端口。收到网络层的报文后,根据差异的端标语,将数据递交给差异的应用。
为了应对差异的场景,传输层协议分为 UDP 和 TCP 协议。
UDP 协议
UDP 协议具有以下特点:
- 无毗连
- 支持一对一、一对多和多对多通讯
- 不包管可靠交付
- 全双工通讯
- 面向报文
根据差异的需求,基于 UDP 衍生出了一些应用层协议,差异的应用会默认指定一个端标语。端标语亦可根据实际环境更换。
TCP 协议具有以下特点:
- 面向毗连
- 每条毗连只能有两个端点,即点对点
- 提供可靠的数据交付
- 全双工通讯
- 面向字节省
根据差异的需求,基于 TCP 衍生出了一些应用层协议,差异的应用会默认指定一个端标语。端标语亦可根据实际环境更换
TCP 网络编程
在开始 TCP 网络编程之前,我们先通过下图,开端相识下 TCP 服务器与客户端的 socket 编程模子:
TCP 客户端网络编程
上图的右侧是最简的 TCP 客户端编程的接口调用流程:
- 调用socket()接口创建 socket 对象。
- 调用connect()接口毗连服务器。
- 调用send()接口向服务器发送数据。
- 调用recv()接口吸收服务器下发的数据。
- 循环实验第 3 步和第 4 步,业务满意肯定条件或毗连断开,调用close()接口关闭 socket,开释资源。
险些全部编程语言实现的 socket 接口,默认都是壅闭模式的,即全部涉及到网络报文收发的接口,如connect()、send()、recv()、close()等,默认都是壅闭式接口。
TCP 服务器与客户端的 socket 编程模子表现图的左侧展示了服务器编程的接口调用流程:
- 调用socket()接口创建 socket 对象。
- 调用bind()接口绑定当地的所在和端口。
- 调用listen()接口监听客户端毗连哀求。
- 调用accept()接口担当客户端毗连哀求。
- 调用recv()接口吸收客户端上行的数据。
- 调用send()接口向客户端发送数据。
- 每一个客户端毗连中,循环实验第 5 步和第 6 步,业务满意肯定条件或毗连断开,调用close()接口关闭 socket,开释资源。
- 在担当客户端毗连哀求的线程中,循环实验第 4 步,以担当更多的客户端接入。
TCP 服务器编程调用的接口相比客户端,多了bind()、listen()、accept()三个接口。
TCP 服务器代码如下:- import usocket
- import _thread
- def _client_conn_proc(conn, ip_addr, port):
- while True:
- try:
- # Receive data sent by the client
- data = conn.recv(1024)
- print('[server] [client addr: %s, %s] recv data:' % (ip_addr, port), data)
- # Send data back to the client
- conn.send(data)
- except:
- # Exception occurred and connection closed
- print('[server] [client addr: %s, %s] disconnected' % (ip_addr, port))
- conn.close()
- break
- def tcp_server(address, port):
- # Create a socket object
- sock = usocket.socket(usocket.AF_INET, usocket.SOCK_STREAM, usocket.IPPROTO_TCP_SER)
- print('[server] socket object created.')
- # Bind the server IP address and port
- sock.bind((address, port))
- print('[server] bind address: %s, %s' % (address, port))
- # Listen for client connection requests
- sock.listen(10)
- print('[server] started, listening ...')
- while True:
- # Accept a client connection request
- cli_conn, cli_ip_addr, cli_port = sock.accept()
- print('[server] accept a client: %s, %s' % (cli_ip_addr, cli_port))
- # Create a new thread for each client connection for concurrent processing
- _thread.start_new_thread(_client_conn_proc, (cli_conn, cli_ip_addr, cli_port))
复制代码 TCP客户端代码如下- import usocket
- import _thread
- def _client_conn_proc(conn, ip_addr, port):
- while True:
- try:
- # Receive data sent by the client
- data = conn.recv(1024)
- print('[server] [client addr: %s, %s] recv data:' % (ip_addr, port), data)
- # Send data back to the client
- conn.send(data)
- except:
- # Exception occurred and connection closed
- print('[server] [client addr: %s, %s] disconnected' % (ip_addr, port))
- conn.close()
- break
- def tcp_server(address, port):
- # Create a socket object
- sock = usocket.socket(usocket.AF_INET, usocket.SOCK_STREAM, usocket.IPPROTO_TCP_SER)
- print('[server] socket object created.')
- # Bind the server IP address and port
- sock.bind((address, port))
- print('[server] bind address: %s, %s' % (address, port))
- # Listen for client connection requests
- sock.listen(10)
- print('[server] started, listening ...')
- while True:
- # Accept a client connection request
- cli_conn, cli_ip_addr, cli_port = sock.accept()
- print('[server] accept a client: %s, %s' % (cli_ip_addr, cli_port))
- # Create a new thread for each client connection for concurrent processing
- _thread.start_new_thread(_client_conn_proc, (cli_conn, cli_ip_addr, cli_port))
复制代码 主流程代码如下:- import checkNet
- import _thread
- import utime
- import dataCall
- if __name__ == '__main__':
- stage, state = checkNet.waitNetworkReady(30)
- if stage == 3 and state == 1: # Network connection is normal
- print('[net] Network connection successful.')
- # Get the IP address of the module
- server_addr = dataCall.getInfo(1, 0)[2][2]
- server_port = 80
- # Start the server thread to listen for client connection requests
- _thread.start_new_thread(udp_server, (server_addr, server_port))
- # Delay for a while to ensure that the server starts successfully
- print('sleep 3s to ensure that the server starts successfully.')
- utime.sleep(3)
- # Start the client
- udp_client(server_addr, server_port)
- else:
- print('[net] Network connection failed, stage={}, state={}'.format(stage, state))
复制代码 UDP网络编程
在开始 UDP 网络编程之前,我们先通过下图,开端相识下 UDP 服务器与客户端的 socket 编程模子:
从图中可以看出,UDP 服务器也须要调用bind()接口,绑定当地的 IP 所在和端标语,这是作为服务器所必须的接口调用。
同时,UDP 编程在接口调用上也有与 TCP 编程差异之处:
- socket()接口参数差异:
- TCP 编程时,第二个参数type为usocket.SOCK_STREAM,而 UDP 编程时,第二个参数type为usocket.SOCK_DGRAM。
- TCP 编程时,第三个参数proto为usocket.IPPROTO_TCP或usocket.IPPROTO_TCP_SER,而 UDP 编程时,第三个参数proto为usocket.IPPROTO_UDP。
- 由于 UDP 是无毗连的,客户端无需调用connect()接口去毗连服务器。
- 数据发送方只要直接调用sendto()接口将数据发送出去即可。
- 数据吸收方调用recvfrom()接口吸收数据。
sendto()接口是否能真正将数据发送到目的地,视网络环境而定,如果无法找到目的 IP 所在对应的主机,则数据被扬弃。
接下来,我们做一个实验:在模组中分别编写一个 UDP 服务器步伐和一个 UDP 客户端步伐,客户端周期性向服务器发送数据,而后等候服务器回送数据。
有了前面 TCP 编程的履历,我们直接给出实验代码- import usocket
- import _thread
- import utime
- import checkNet
- import dataCall
- def udp_server(address, port):
- # Create a socket object
- sock = usocket.socket(usocket.AF_INET, usocket.SOCK_DGRAM, usocket.IPPROTO_UDP)
- print('[server] socket object created.')
- # Bind server IP address and port
- sock.bind((address, port))
- print('[server] bind address: %s, %s' % (address, port))
- while True:
- # Read client data
- data, sockaddr = sock.recvfrom(1024)
- print('[server] [client addr: %s] recv data: %s' % (sockaddr, data))
- # Send data back to the client
- sock.sendto(data, sockaddr)
- def udp_client(address, port):
- # Create a socket object
- sock = usocket.socket(usocket.AF_INET, usocket.SOCK_DGRAM, usocket.IPPROTO_UDP)
- print('[client] socket object created.')
- data = b'1234567890'
- while True:
- # Send data to the server
- sock.sendto(data, (address, port))
- print('[client] send data:', data)
- # Read data sent back from the server
- data, sockaddr = sock.recvfrom(1024)
- print('[client] [server addr: %s] recv data: %s' % (sockaddr, data))
- print('[client] -------------------------')
- # Delay for 1 second
- utime.sleep(1)
- if __name__ == '__main__':
- stage, state = checkNet.waitNetworkReady(30)
- if stage == 3 and state == 1: # Network connection is normal
- print('[net] Network connection successful.')
- # Get the IP address of the module
- server_addr = dataCall.getInfo(1, 0)[2][2]
- server_port = 80
- # Start the server thread
- _thread.start_new_thread(udp_server, (server_addr, server_port))
- # Delay for a while to ensure that the server starts successfully
- print('sleep 3s to ensure that the server starts successfully.')
- utime.sleep(3)
- # Start the client
- udp_client(server_addr, server_port)
- else:
- print('[net] Network connection failed, stage={}, state={}'.format(stage, state))
复制代码 常见题目:
1. 为什么毗连服务器会失败?
服务器必须是公网所在(毗连模组当地的 server 除外)。
利用 PC上 的 TCP/UDP 测试工具客户端、大概 mqtt.fx,毗连服务器确认一下是否可以毗连乐成,打扫服务器故障。
2. TCP 有主动重连功能吗?
底层没有主动重连,重连机制在应用层处置惩罚。
3.为什么我一包数据只有不到 50B,一天斲丧的流量要远弘大于实际传输值
如果利用的是 TCP 协议,须要三次握手四次挥手才算完成了一次数据交互,原始数据不多但是由于 TCP 协议决定的一包数据必须要加包头包尾帧校验等,以是实际斲丧的流量不止 50B,部门运营商有限定每一包数据必须 1KB 起发,不敷 1KB 也会加各种校验凑足 1KB。
|