一、网络的三个火枪手
1、IP简介
1.1、ip的作用
为了让在不同的电脑上运行的软件,之间能够互相传递数据,就需要借助网络的功能,ip是用来在网络中标记一台电脑,比如192.168.1.1;在本地局域网上是唯一的。
网络通信也就是让在不同的电脑上的软件能够进行数据传递,即进程之间的通信。
1.2、 ip的分类
-
A类IP地址
一个A类IP地址由1字节的网络地址和3字节主机地址组成,网络地址的最高位必须是“0”,
地址范围1.0.0.1-126.255.255.254
二进制表示为:00000001 00000000 00000000 00000001 - 01111110 11111111 11111111 11111110
可用的A类网络有126个,每个网络能容纳1677214个主机
-
B类IP地址
一个B类IP地址由2个字节的网络地址和2个字节的主机地址组成,网络地址的最高位必须是“10”,
地址范围128.1.0.1-191.255.255.254
二进制表示为:10000000 00000001 00000000 00000001 - 10111111 11111111 11111111 11111110
可用的B类网络有16384个,每个网络能容纳65534主机
-
C类IP地址
一个C类IP地址由3字节的网络地址和1字节的主机地址组成,网络地址的最高位必须是“110”
范围192.0.1.1-223.255.255.254
二进制表示为: 11000000 00000000 00000001 00000001 - 11011111 11111111 11111110 11111110
C类网络可达2097152个,每个网络能容纳254个主机
-
D类地址用于多点广播
D类IP地址第一个字节以“1110”开始,它是一个专门保留的地址。
它并不指向特定的网络,目前这一类地址被用在多点广播(Multicast)中
多点广播地址用来一次寻址一组计算机 s 地址范围224.0.0.1-239.255.255.254
-
E类IP地址
以“1111”开始,为将来使用保留
E类地址保留,仅作实验和开发用
1.3、私有ip
在这么多网络IP中,国际规定有一部分IP地址是用于我们的局域网使用,也就
是属于私网IP,不在公网中使用的,它们的范围是:
10.0.0.0~10.255.255.255
172.16.0.0~172.31.255.255
192.168.0.0~192.168.255.255
1.4、本机ip地址
IP地址127.0.0.1~127.255.255.255用于回路测试,
如:127.0.0.1可以代表本机IP地址。
1.5、常用指令
ifconfig:查看所有网卡的信息,也能通过sudo ifconfig ens33 192.168.1.100修改网卡的ip
ping用来检测网络是否正常
2、PORT简介
2.1、port的作用
一台拥有IP地址的主机可以提供许多服务,比如HTTP(万维网服务)、FTP(文件传输)、SMTP(电子邮件)等,这些服务完全可以通过1个IP地址来实现。
那么,主机是怎样区分不同的网络服务呢?显然不能只靠IP地址,因为IP地址与网络服务的关系是一对多的关系。实际上是通过“IP地址+端口号”来区分不同的服务的。
需要注意的是,端口并不是一一对应的。比如你的电脑作为客户机访问一台WWW服务器时,WWW服务器使用“80”端口与你的电脑通信,但你的电脑则可能使用“3457”这样的端口。
端口范围是从0到65535。
2.2、port的分配
- 知名端口
知名端口是众所周知的端口号,范围从0到1023。
80端口分配给HTTP服务
21端口分配给FTP服务
- 动态端口
动态端口的范围是从1024到65535,之所以称为动态端口,是因为它一般不固定分配某种服务,而是动态分配。
动态分配是指当一个系统程序或应用程序程序需要网络通信时,它向主机申请一个端口,主机从可用的端口号中分配一个供它使用。
当这个程序关闭时,同时也就释放了所占用的端口号。
查看端口状态:
- 用“netstat -an”查看端口状态
2.3、SOCKET
socket(简称 套接字
) 是进程间通信的一种方式,它与其他进程间通信的一个主要不同是:它能实现不同主机间的进程间通信,我们网络上各种各样的服务大多都是基于 Socket 来完成通信的。
函数 socket.socket 创建一个 socket,该函数带有两个参数:
- Address Family:可以选择 AF_INET(用于 Internet 进程间通信) 或者 AF_UNIX(用于同一台机器进程间通信),实际工作中常用AF_INET
- Type:套接字类型,可以是 SOCK_STREAM(流式套接字,主要用于 TCP 协议)或者 SOCK_DGRAM(数据报套接字,主要用于 UDP 协议)
二、udp编程
1、udp的工作流程
192.168.1.215:8080 192.168.1.82:7788
2、udp的发送程序
#! /usr/bin/python3
import time
import socket
send_data = 'Hello world!!'
send_cnt = 0
udp_socket = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
dest_addr = ('192.168.1.82',8080)
while 1:
send_cnt += 1
send_data += str(send_cnt)
udp_socket.sendto(send_data.encode('utf-8'),dest_addr)
time.sleep(1)
udp_socket.close()
运行结果:向ip为192.168.1.215的8080每隔1s发送一次数据。
3、添加UDP的接收程序
#! /usr/bin/python3
import time
import socket
send_data = 'Hello world!!'
send_cnt = 0
udp_socket = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
local_addr= ('',7788)
dest_addr = ('192.168.1.82',8080)
udp_socket.bind(local_addr)
while 1:
send_cnt += 1
send_data += str(send_cnt)
udp_socket.sendto(send_data.encode('utf-8'),dest_addr)
recv_data = udp_socket.recvfrom(1024)
print(recv_data[0].decode('gbk'))
print(recv_data[1])
udp_socket.close()
运行结果:
linbo@linbo:~/python$ ./6net.py
hello python!!
('192.168.1.82', 8080)
hello python!!
('192.168.1.82', 8080)
hello python!!
('192.168.1.82', 8080)
hello python!!
('192.168.1.82', 8080)
hello python!!
('192.168.1.82', 8080)
hello python!!
('192.168.1.82', 8080)
hello python!!
('192.168.1.82', 8080)
hello python!!
('192.168.1.82', 8080)
hello python!!
('192.168.1.82', 8080)
hello python!!
('192.168.1.82', 8080)
对端现象:
关键词:
- 一个udp网络程序,可以不绑定,此时操作系统会随机进行分配一个端口,如果重新运行次程序端口可能会发生变化
- 一个udp网络程序,也可以绑定信息(ip地址,端口号),如果绑定成功,那么操作系统用这个端口号来进行区别收到的网络数据是否是此进程的
- recv_data = udp_socket.recvfrom(1024)函数阻塞等待直到接收到数据(最多单次接收1024字节)
- recv_data[0]为对方发送的数据,recv_data[1]为对方的ip和端口。
- encode编码:str->bytes
- decode解码:bytes->str
三、tcp简介
TCP协议,传输控制协议(英语:Transmission Control Protocol,缩写为 TCP)是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。
TCP通信需要经过创建连接、数据传送、终止连接三个步骤。
1、tcp特点
-
面向连接
通信双方必须先建立连接才能进行数据的传输,双方都必须为该连接分配必要的系统内核资源,以管理连接的状态和连接上的传输。
双方间的数据传输都可以通过这一个连接进行。
完成数据交换后,双方必须断开此连接,以释放系统资源。
这种连接是一对一的,因此TCP不适用于广播的应用程序,基于广播的应用程序请使用UDP协议。
-
可靠传输
1)TCP采用发送应答机制
TCP发送的每个报文段都必须得到接收方的应答才认为这个TCP报文段传输成功
2)超时重传
发送端发出一个报文段之后就启动定时器,如果在定时时间内没有收到应答就重新发送这个报文段。
TCP为了保证不发生丢包,就给每个包一个序号,同时序号也保证了传送到接收端实体的包的按序接收。然后接收端实体对已成功收到的包发回一个相应的确认(ACK);
如果发送端实体在合理的往返时延(RTT)内未收到确认,那么对应的数据包就被假设为已丢失将会被进行重传。
3)错误校验
TCP用一个校验和函数来检验数据是否有错误;在发送和接收时都要计算校验和。
4) 流量控制和阻塞管理
流量控制用来避免主机发送得过快而使接收方来不及完全收下。
2、tcp与udp的不同点
- 面向连接(确认有创建三方交握,连接已创建才作传输)
- 有序数据传输
- 重发丢失的数据包
- 舍弃重复的数据包
- 无差错的数据传输
- 阻塞/流量控制
3、tcp的工作模型
四、tcp编程
1、tcp客户端发送数据
#! /usr/bin/python3
from socket import *
tcp_client_socket = socket(AF_INET,SOCK_STREAM)
server_ip = '192.168.1.82'
server_port= 8080
send_data ='hello server!!'
tcp_client_socket.connect((server_ip,server_port))
tcp_client_socket.send(send_data.encode('gbk'))
recv_data = tcp_client_socket.recv(1024)
print(recv_data.decode('gbk'))
tcp_client_socket.close()
运行结果:在对端tcp服务器显示,已经连接远程ip为192.168.1.215:45800,而且收到客户端发送的数据hello sercer!!
linbo@linbo:~/python$ ./7tcp.py
hello python!!
关键词:
- 服务器端没有监听时候,客户端阻塞在connnect()地方。
- 客户端在没有收到数据之前,也是阻塞在recv()函数。
- connnect参数为元组(ip字符串,port整数)。
2、tcp服务端编程
1).socket创建一个套接字。
2).bind绑定ip和port(服务器端需要绑定端口便于客户端访问,客户端不需要)。
3).listen使套接字变为可以被动链接。
4).accept等待客户端的链接。
5).recv/send接收发送数据。
#! /usr/bin/python3
from socket import *
tcp_server_socket = socket(AF_INET,SOCK_STREAM)
#本地信息
address = ('',7788)
#绑定
tcp_server_socket.bind(address)
#使用listen这样可以接收别人的连接了
tcp_server_socket.listen(128)
print("============1=============")
#如果有新的客户端来连接服务器,那么会产生一个新的套接字专门为这个客户端服务
#client_socket专门为这次来连接的客户端服务
#tcp_server_socket就可以省下来专门等待其他新的客户端服务
client_socket,clientAddr = tcp_server_socket.accept()
print("============2=============")
recv_data = client_socket.recv(1024)
print(recv_data.decode('gbk'))
client_socket.send('thank you'.encode('gbk'))
client_socket.close()
运行结果:
============1=============
============2=============
hello python!!
客户端数据如下:
关键词:
- tcp服务器一般情况下都需要绑定,否则客户端找不到这个服务器。
- tcp客户端一般不绑定,因为是主动连接服务器,所以只要确定好服务器的ip、port等信息就好,本地客户端可以随机。
- tcp服务器中通过listen可以将socket创建出来的主动套接字变为被动的,这是做tcp服务器时必须要做的。
- 当客户端需要链接服务器时,就需要使用connect进行链接,udp是不需要链接的而是直接发送,但是tcp必须先链接,只有链接成功才能通信。
- 当一个tcp客户端连接服务器时,服务器端会有1个新的套接字,这个套接字用来标记这个客户端,单独为这个客户端服务。
- listen后的套接字是被动套接字,用来接收新的客户端的链接请求的,而accept返回的新套接字是标记这个新客户端的。
- 关闭listen后的套接字意味着被动套接字关闭了,会导致新的客户端不能够链接服务器,但是之前已经链接成功的客户端正常通信。
- 关闭accept返回的套接字意味着这个客户端已经服务完毕。
- 当客户端的套接字调用close后,服务器端会recv解堵塞,并且返回的长度为0,因此服务器可以通过返回数据的长度来区别客户端是否已经下线。