Ip地址分类
Ip地址由网络地址和主机地址组成分为5类:
1.A类IP地址
一个A类IP地址由1字节的网络地址和3字节主机地址组成,
网络地址的最高位必须是“0”(位运算), 地址范围从1.0.0.0 到126.0.0.0。
可用的A类网络有126个,每个网络能容纳1千多万个主机。2.B类IP地址
一个B类IP地址由2个字节的网络地址和2个字节的主机地址组成,
网络地址的最高位必须是“10”,地址范围从128.0.0.0到191.255.255.255。
可用的B类网络有16382个,每个网络能容纳6万多个主机 。3.C类IP地址
一个C类IP地址由3字节的网络地址和1字节的主机地址组成,网络地址的
最高位必须是“110”。范围从192.0.0.0到223.255.255.255。C类网络
可达209万余个,每个网络能容纳254个主机。4.D类地址用于多点广播(Multicast)。
D类IP地址第一个字节以“lll0”开始,它是一个专门保留的地址。
它并不指向特定的网络,目前这一类地址被用在多点广播(Multicast)中。
多点广播地址用来一次寻址一组计算机,它标识共享同一协议的一组计算机。5.E类IP地址
以“llll0”开始,为将来使用保留。
全零(“0.0.0.0”)地址对应于当前主机。全“1”的
IP地址(“255.255.255.255”)是当前子网的广播地址。
在IP地址3种主要类型里,各保留了3个区域作为私有地址,
其地址范围如下:
A类地址:10.0.0.0~10.255.255.255
私有网络
- iPv4地址枯竭为何还能支撑?
这就要说起NAT(Network Address Translation)网络地址转换技术
当我们在家或者在公司上网时,你的电脑肯定有一个类似192.168.0.1的地址,
这种地址属于私网地址,不属于公共互联网地址,每一个小的局域网都会使用
一个网段的私网地址,在与外界联系时变成公网,这样几十台上百台电脑只需
要一个公网地址。甚至还能私网套私网NAT套NAT一层一层,节约公网ip数量
但是NAT也有很多限制:私网访问公网虽然方便,但是公网访问私网就很困难,
很多服务受到限制,也影响了网络处理的效率。
具体百度iPv4和ipv6
- 私有ip
1
2
310.0.0.0 – 10.255.255.255
172.16.0.0 – 172.31.255.255
192.168.0.0 – 192.168.255.255
Port端口
当你发送一段信息给别的电脑ip时,你这段信息主要是要访问别的电脑的哪个进程呢?如何确定是哪个进程呢?
当进程运行时,会告诉自身电脑给它的信息要从哪个端口传入,如:7788,7890等等
1 | Dest ip:192.168.1.2 |
端口号
0到2^16(65535)知名端口号 不能随便用
0到1023
80端口分配给HTTP
21端口分配给FTP大于1024随便用
动态端口 Dynamic ports
1024到65535
Socket简介
socket是网络通信的必须物,Socket是应用层
与TCP/IP协议族通信的中间软件抽象层,它是一组接口。
在设计模式中,Socket其实就是一个门面模式,它把复
杂的TCP/IP协议族隐藏在Socket接口后面,对用户来说,
一组简单的接口就是全部。
1 | import socket |
- ubuntu和windows通讯
1
2
3
4
5
6
7
8vim缩进:shift V选择,v缩退 >缩进
ctrl n补全
如果要实现ubuntu和我们windows的通讯
如ip:172.154.2.12和192.168.33.23的通讯
改成桥接模式,sudo dhclient等待分配ip
然后ip会自动变为和另一个ip同一个网段
ping 192.168.33.53
10.167.2.111
socket简单使用
1 | import socket |
想要一个程序有一个固定的端口接收数据
1
2
3
4
5
6
7
8from socket import *
s = socket(AF_INET, SOCK_DGRAM)
local_addr = ('', 7788)
s.bind(local_addr)
recv_data = s.recvfrom(1024)
print(recv_data)
print(recv_data[0].decode('utf-8'))
s.close()绑定发送数据端口
不绑定端口会随机端口分配!1
2s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.bind(('', 8080))
udp聊天器
- 单工:收音机只能收不能发,只能单向
- 半双工:对讲机,我在收的时候发不了,发的时候收不了,同一时刻单向
- 全双工:手机,同一时刻既可以发,也可以收
socket套接字是全双工 但现在没有多进程线程或协程的情况下,是半双工
1 | import socket |
TCP通信模型
udp通信模型
在通信开始前,不需要建立相关连接,
只需要发送数据即可,类似于生活中写信
udp发的消息可能丢失,不安全,简单tcp类似于打电话,稳定,面向连接
tcp模型简介
1.通信双方必须先建立连接
- 1.创建连接
- 2.数据传送
- 3.终止连接
2.发送应答机制:
当主机发送数据之后,接收方会告诉主机是否接收到数据,(没收到会显示超时上传)
网购都是tcp,qq:tcp,抢票tcp。保证数据可靠3.tcp严格区分服务器和客户端
tcp客户端构建流程
- 1.创建套接字
- 2.目的信息
- 3.链接服务器 多了这个
- 4.显示用户输入数据
- 5.接收对方发来数据
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17import socket
def main():
# 1.创建tcp套接字
tcp_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 2.链接服务器
server_ip = input('请输入服务器的ip:')
server_port = int(input("请输入服务器的port:"))
server_addr = (server_ip, server_port)
tcp_socket.connect(server_addr)
# 3.发送/接收数据
send_data = input('请输入要发送的数据:')
tcp_socket.send(send_data.encode('utf-8'))
# 4.关闭套接字
tcp_socket.close()
if __name__ == '__main__':
main()
tcp服务器创建流程
- 1.套接字
- 2.ip port
- 3.listen使套接字变为可以被动链接
- 4.accept等待客户端的链接
- 5.recv/send接收发送的数据
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37import socket
# tcp服务器端
def main():
# 1.买个手机(创建套接字)
tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 2.拆入手机卡(绑定本地信息)
tcp_server_socket.bind(("", 7890) )
# 3.将手机设置为响铃(让默认套接字由主动变为被动)
tcp_server_socket.listen(128)
while True:
# 4.等待电话到来(等待客户端链接 accept)
print('================1================')
client_socket, client_addr = tcp_server_socket.accept()
print('================2================')
print("一个新的客户端已经到来%s" % str(client_addr))
while True:
#接收客户端发送的数据
try:
recv_data = client_socket.recv(1024)
except:
break
# 客户端调用close或者发出退出信息
if str(recv_data.decode('utf-8')) == '退出' or not recv_data:
break
print("客户端发送过来的请求是%s" % str(recv_data.decode('utf-8')))
# 回送一部分数据给客户端
client_socket.send("shadiao========OK=========shadiao".encode('utf-8'))
# 关闭套接字
client_socket.close()
print('服务完毕')
tcp_server_socket.close()
if __name__ == '__main__':
main()
TCP文件下载器
服务器端
1 | import socket |
客户端
1 | from socket import * |
简单TCP文件上传器
服务端
1 | import socket |
客户端
1 | import errno |
TCP注意点
- 1.tcp服务器一般情况下都需要绑定:否则客户端找不到服务器
- 2.客户端为什么不绑定?因为是主动连接服务器,所以只要确定
服务器ip,port信息就行,本地客户端随机就行,还可以多开 - 3.tcp通过lisent将套接字socket由主动变为被动,这是tcp服务
器必须要做的 - 4.当客户端需要链接服务器是,需要使用connect进行链接,而udp
不需要,tcp只要有链接成功才能通信 - 5.当tcp客户端链接服务器后,服务器会用一个新的套接字来标记客
户端,单独为此客户服务 - 6.listen后的套接字是被动套接字,专门用来接收客户端的链接请求,
而accept返回的新的套接字是标记这个客户端的服务 - 7.关闭listen后的套接字意味着被动套接字关闭了,会导致新的客户
端不能够链接服务器,但是之前链接成功的可以正常通信 - 8.关闭accept这个套接字意味着这个客户端服务完毕
- 9.当客户端套接字close,服务器端会recv解阻塞,并且返回recv长
度为0,因此服务器可以通过返回数据的长度来区别客户端是否已经下线
多线程
没有多任务就不能同时执行,单核cpu的时间片轮转
单核cpu只能同一时间执行一个任务,但只要够快,就像多线程一样
并行:真的多任务
并发:假的多任务
任务数多于cpu的核数,就是并发1
2threading
threading.enumerate()查看线程两种方法调用线程
1.直接调用函数名
1
2t1 = treading.Thread(target=sing)
t1.start()2.创建类继承thread
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16class MyThread(threading.Thread): 必须继承threading
def run(self): 必须有run方法
/…..
t = MyThread()
t.start()这个start自动调用run
多线程共享全局变量
!!全局变量不一定要加global
全局变量改了地址指向,需要global
若[1, 2]列表append,指向地址未变,不需要global
[1, 2] + [100, 200] 就变了
指向地址变化用global,仅仅修改了指向空间中的数据
不用global
t1 = treading.Thread(target=sing, args=(,))一定是一个元组
target指定这个线程去哪个函数执行代码
args指定调用函数的时候,传递什么数据过去
共享变量问题
大家都用同一个变量,资源竞争,比如两个线程同时写一个变量
如果出现资源竞争,就会出错
- 线程在执行如g_num += 1时,会分为三步:
- 1.获取g_num的值
- 2.把获取的值+1
- 3.把第二步的结果存储到g_num中
当线程同时调用,g_num都是一起加,还没开始存储
如何解决共享变量资源竞争的问题
原子性:
我做的时候你不做,要么做完,要么不做同步协调:
就像走路,一左一右协调行走互斥锁X:
当多个线程同时修改一个数据时,需要进行同步控制
某个线程进行修改共享数据时,先将其锁定,此时资源锁定状态,
其他线程不可修改,直到该线程释放资源,将资源的状态变为非锁定。
锁方案一
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42mutex = threading.Lock()
上锁
mutex.acquire()
释放锁
mutex.release()
import threading
import time
g_num = 0
def test1(num):
global g_num
# 上锁,若之前没上锁,上锁成功,若之前上锁了,会堵塞在这儿,直到这个锁被解开
mutex.acquire()
for i in range(num):
g_num += 1
# 解锁
mutex.release()
print("-------------in test1 g_num=%d------" % g_num)
def test2(num):
global g_num
mutex.acquire()
for i in range(num):
g_num += 1
# 解锁
mutex.release()
print("-------------in test2 g_num=%d------" % g_num)
# 创建一个互斥锁 默认未上锁
mutex = threading.Lock()
def main():
t1 = threading.Thread(target=test1, args=(1000000,))
t2 = threading.Thread(target=test2, args=(1000000,))
t1.start()
t2.start()
time.sleep(2)
print("-------------in main Thread g_num=%d------" % g_num)
if __name__ == '__main__':
main()锁方案二
上锁的代码越少越好1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38import threading
import time
g_num = 0
def test1(num):
global g_num
# 上锁,若之前没上锁,上锁成功,若之前上锁了,会堵塞在这儿,直到这个锁被解开
mutex.acquire()
for i in range(num):
g_num += 1
# 解锁
mutex.release()
print("-------------in test1 g_num=%d------" % g_num)
def test2(num):
global g_num
mutex.acquire()
for i in range(num):
g_num += 1
# 解锁
mutex.release()
print("-------------in test2 g_num=%d------" % g_num)
# 创建一个互斥锁 默认未上锁
mutex = threading.Lock()
def main():
t1 = threading.Thread(target=test1, args=(1000000,))
t2 = threading.Thread(target=test2, args=(1000000,))
t1.start()
t2.start()
time.sleep(1)
print("-------------in main Thread g_num=%d------" % g_num)
if __name__ == '__main__':
main()
互斥锁死锁问题
如果两个线程分别占有一部分资源并且同时等待对方的资源,就会造成死锁。
如在线程A中acquireB 同时在B中acquireA就会出现死锁无法走动
解决死锁:
- 1.程序设计时尽量避免
- 2.添加超时时间
银行家算法:
银行10单位,c1 9 c2 3 c3 8, 如何借款,先给c1 2, c2 2, c3 4
c2还钱 c3 4, c3还钱 c1 7