Loading

python网络编程

网络编程

part1:

网络编程基础

  • 2022-5-16新增
socket.AF_INET  # 表示ipv4
socket.AF_INET6  # 表示ipv6

socket.SOCK_STREAM  # 表示是tcp协议
socket.SOCK_DGRAM  # 表示是udp协议
  • 不变的:mac地址能够唯一标识你这台机器的

  • 变化的:ip地址能够更好的更方便的找到你的机器

  • 局域网:

    • 网段交换机不能理解ip地址,只能理解mac地址
  • 局域网和局域网之间通信了:

    • 网关路由器可以理解ip地址
  • ip地址:

    • *ipv4:四位点分十进制
    • *公网地址:需要我们自己申请购买的地址
    • *内网地址:保留字段(不在这个范围内的都是公网ip)
    • *特殊的ip地址
      • 127.0.0.1本地回环地址(表示我自己)测试的时候用的(过网卡不过交换机)
  • *查看自己的ip地址ipconfig(window10)/ifconfig(mac/linux)

  • *子网掩码也是一个ip地址用来判断两台机器在不在一个局域网内

  • IPV6(一般小公司ipv4大公司ipv6)

  • ip/mac确认机器的

  • 端口确认机器上的具体应用程序的

  • 概念的整理

    • 局域网的概念
      • 交换机
        • 在同一个局域网内的机器由交换机负责通信
        • 交换机只认识mac地址
        • 可以完成广播 组播 单播
      • 单播--mac地址(在网卡上)
    • 局域网之间通信
      • 路由器
        • 提供网关ip,同一个局域网的所有机器共享一个网关
        • 我们不能访问除了本局域网之外的其他内网的IP地址
      • 子网掩码
        • 用来判断两台机器是不是在一个网段内
    • ip地址:ipv4协议ipv6协议
    • mac地址:arp协议(通过ip找mac)
  • 端口port:用来确认一台机器上的具体应用

osi五(七)层协议

osi五层协议

  • 应用层 python代码
  • 传输层 port tcp udp 四层路由器 四层交换机
  • 网络层 ipv4 ipv6 路由器三层交换机
  • 数据链路层 mac arp协议 网卡 二层交换机(单播,广播,组播arp协议用到前两种)
  • 物理层

tcp和udp

  • tcp(语音聊天/视频聊天)-线下缓存高强电影\qq远程控制\发邮件
    • 需要先建立连接然后才能通信的

    • 占用连接\可靠(消息不会丢失)\实时性高\慢

    • 建立连接-三次握手(握手有客户段发起请求)

    • 断开连接-四次挥手

    • 什么是三次握手?什么是四次挥手?为什么握手是三次挥手是四次?这个过程都传递了哪些信号

    • 因为可能我有些话可能没跟他说完/他有些话可能没跟我说完

	tcp协议有
	# 三次握手
	  # 客户端向服务器端发送syn请求,
	  # 服务端向客户端回复ack并发送syn请求,
	  # 客户端接收到请求之后再回复ack表示建立连接
	  # 由客户端的connect + 服务端的accept
	# 四次挥手
	  # 客户端向服务端发送fin请求,
	  # 服务端回复ack确认
	  # 服务端向客户端发送fin请求,
	  # 客户端回复ack确认
      # 有客户端的close和服务端的close
  • udp(发消息)-在线播放视频\qq发消息\微信消息
    • 不需要建立连接就可以通信的
    • 不占用连接\不可靠(消息因为网络不稳定丢失)\快

最简单的网络通信(基于tcp协议)

  1. 客户端(client):
import  socket

sk=socket.socket()
sk.connect(('127.0.0.1',9001))

msg=sk.recv(1024)#最多接受1024个
print(msg)

sk.send(b'bebebe')

sk.close()
  1. 服务端(serve):

import  socket

sk=socket.socket()
sk.bind(('127.0.0.1',9001))
sk.listen()

conn,addr=sk.accept()
conn.send(b'hell')
msg=conn.recv(1024)#最多接受1024个字节
print(msg)

conn.close()      #断开连接

sk.close()           #关闭整个serve服务

prat2:

tcp协议

  • tcp是基于链接的,必须先启动服务端,然后再启动客户端去链接服务端
  1. 服务器端(server):(即可服务器先发送也可客户端先发送)
import  socket
sk=socket.socket()
sk.bind(('127.0.0.1',9001))     #申请操作系统的资源,绑定一个ip 和端口(元组)
sk.listen()    # 代表socket服务已经开启

print('sk:',sk)
conn,addr=sk.accept()            #conn里存储的是一个客户端和我serve端的连接信息,等,到有客户端来访问和客户端建立联系
print('coon:',conn)
conn.send(b'hello')                 # send直接通过连接发送消息,不需要写地址
msg=conn.recv(1024)
print(msg)
conn.close()    #四次挥手,断开连接

sk.close()    #关闭服务,归还申请的操作系统资源
  1. 客户端(client):
import  socket

sk=socket.socket()
sk.connect(('127.0.0.1',9001))    # 客户端/tcp协议的方法,和serve端建立连接

msg=sk.recv(1024)    # 只接受消息
print(msg)

sk.send(b'bebebe')

sk.close()

利用tcp协议和多个客户端进行通信

  1. 服务器端(server):
sk=socket.socket()
sk.bind(('127.0.0.1',9001))#申请操作系统的资源
sk.listen()

while  True:#为了和多个客户端进行握手
	conn,addr=sk.accept()#能够和多个客户端握手了
	while  True:#和一个客户使劲聊天
		send_msg=input('>>>')
		conn.send(send_msg.encode('utf-8'))
		if  send_msg.upper()=='Q':break#关闭和该用户通信
		msg=conn.recv(1024).decode('utf-8')
		if  msg.upper()=='Q':break
		print(msg)
	conn.close()#挥手,断开连接

sk.close()#关闭服务,归还申请的操作系统资源
  1. 客户端(client):
import  socket

sk=socket.socket()
sk.connect(('127.0.0.1',9001))
while  True:#和服务器端使劲聊
	msg=sk.recv(1024).decode('utf-8')
	if  msg.upper()=='Q':break#关闭和该服务器通信
	print(msg)
	send_msg=input('>>>')
	sk.send(send_msg.encode('utf-8'))
	if  send_msg.upper()=='Q':break
sk.close()

struct模块

import   struct
#2的23次方都可以转换成4个字节
num1=129469649
num2=123
num3=8

ret1=struct.pack('i',num1)    # 转化为四字节
print(ret1)
ret2=struct.pack('i',num2)
print(ret2)
ret3=struct.pack('i',num3)
print(ret3)#返回的是4位字节

print(struct.unpack('i',ret1))   # 解开为原来长度
print(struct.unpack('i',ret2))
print(struct.unpack('i',ret3))

黏包现象只发生在tcp协议中:

  1. 从表面上看,黏包问题主要是因为发送方和接收方的缓存机制、tcp协议面向流通信的特点。
    • 发生在发送端:发送数据小,间隔短。由于优化机制就合并在一起发送了
    • 发生在接受端:接受不及时,所以数据就在接受端的缓存端粘在一起了
  • 粘包现象发生的本质:tcp协议的传输时流式传输,数据之间没有边界
  1. 实际上,主要还是因为接收方不知道消息之间的界限,不知道一次性提取多少字节的数据所造成的

解决tcp协议中的粘包现象

  1. 服务器端(server):
import  struct
sk=socket.socket()
sk.bind(('127.0.0.1',9001))#申请操作系统的资源
sk.listen()

conn,addr=sk.accept()    #conn里存储的是一个客户端和我serve端的连接信息

msg1=input('>>>').encode()
msg2=input('>>>').encode()
  #num=str(len(msg1))#'6'
  #ret=num.zfill(4)#'0006'
  #conn.send(ret.encode('utf-8'))
blen=struct.pack('i',len(msg1))
print(blen)
conn.send(blen)
conn.send(msg1)
conn.send(msg2)

conn.close()#四次挥手,断开连接

sk.close()#关闭服务,归还申请的操作系统资源
  1. 客户端(client):
import  time
import  socket
import  struct
sk=socket.socket()
sk.connect(('127.0.0.1',9001))

length=sk.recv(4)    # 因为socket发送的是4个字节
length=struct.unpack('i',length)[0]  #因为其返回的是个元组
msg1=sk.recv(length)
print(msg1.decode('utf-8'))

msg2=sk.recv(1024)
print(msg2.decode('utf-8'))
sk.close()

解决粘包的本质:设置边界(发送多少个接受多少个)

  • 粘包现象:本来分开的数据粘合到一起去了
  • 只出现在tcp协议中,因为tcp协议多条消息之间没有边界,并且还有一大推优化算法
  • 发送端:两条消息都很短,发送的间隔时间也非常短
  • 接收端:多条消息由于没有及时接受,而在接受方的缓存短,堆在一起导致的粘包
  • tcp协议数据之间没有边界,可以传输大的数据

udp协议

  • udp是无链接的,启动服务之后可以直接接受消息,不需要提前建立链接(不会粘包)
  1. 服务器端(server):(不需要listen监听)
import  socket
#type=socket.SOCK_DGRAM:udp协议          默认是tcp协议
sk=socket.socket(type=socket.SOCK_DGRAM)
sk.bind(('127.0.0.1',9001))     #申请操作系统的资源
while  True:
	msg,addr=sk.recvfrom(1024)        # sk.recvfrom接受消息和地址 sk.recv(1024)是只接受消息
	print(msg.decode('utf-8'))
	mmsg=input('>>>')
	sk.sendto(mmsg.encode('utf-8'),addr)      # 需要写一个对方的地址
sk.close()  # 关闭服务器套接字 #关闭服务,归还申请的操作系统资源
  1. 客户端(client):(由客户端发起对话)
import  socket
sk=socket.socket(type=socket.SOCK_DGRAM)

serve=('127.0.0.1',9001)
while  True:
	mmmsg=input('>><')
	if  mmmsg.upper()=='Q':break
	sk.sendto(mmmsg.encode('utf-8'),serve)

	msg=sk.recv(1024).decode('utf-8')
	if  msg.upper()=='Q':break
print(msg)

part3:

验证客户端的合法性

  1. serve端:
import  socket
import  os
import  hashlib

secret_key=b'hahaha'
sk=socket.socket()
sk.bind(('127.0.0.1',9001))
sk.listen()

conn,addr=sk.accept()
#创建一个随机的字符串
rand=os.urandom(32)
#发送随机字符串
conn.send(rand)

#根据发送的字符串+secretekey进行摘要
sha=hashlib.sha1(secret_key)
sha.update(rand)
res=sha.hexdigest()

#等待接受客户端的摘要结果
res_client=conn.recv(1024).decode('utf-8')
#做对比
if  res_client==res:
	print('是合法的客户端')
	conn.send(b'hello')
else:
	conn.close()
#如果不一致,这关闭连接
  1. client端:
import  socket
import  hashlib

secret_key=b'hahaha'
sk=socket.socket()
sk.connect(('127.0.0.1',9001))

#接收客户端发送的随机字符串
rand=sk.recv(32)
#根据发送的字符串+secretkey进行摘要
sha=hashlib.sha1(secret_key)
sha.update(rand)
res=sha.hexdigest()
#摘要结果发送回server端
sk.send(res.encode('utf-8'))
#继续和server端进行通信
msg=sk.recv(1024)
print(msg)

sk.close()

socketserver模块--针对并发编程

  • socket底层模块
  • socketserver基于socket完成的
  • tcp协议的server端处理并发的客户端请求
  • 例子:网盘:文件的上传和下载
  1. server端:
import  socketserver

class  Myserver(socketserver.BaseRequestHandler):
	def  handle(self):
		conn=self.request
		while  True:
			try:
				content=conn.recv(1024).decode('utf-8')
				print(content)
				conn.send(content.upper().encode('utf-8'))
			except  ConnectionResetError:
				break

server=socketserver.ThreadingTCPServer(('127.0.0.1',9001),Myserver)
server.serve_forever()
  1. client端:与原来的相同
import  socket
sk=socket.socket()
sk.connect(('127.0.0.1',9001))

while  True:
	sk.send(b'hello')
	content=sk.recv(1024).decode('utf-8')
print(content)
posted @ 2020-09-29 15:11  zranguai  阅读(119)  评论(0编辑  收藏  举报