上节课复习:
1、tcp协议
今日内容:
1、socket的通信流程介绍
2、基于tcp协议通信的套接字程序
==================================================================
服务端使用的是: NOKIA老年机,
使用流程:
购买一部设置打开网络 指定数据传输为流格式的手机,
拔掉电池插手机卡从而绑定一个手机号,
开机,
接听电话
客户端使用的是: Iphone概念机
使用流程
买来的时候,就是开机状态,只需要打开网络 和 指定数据传输为流格式,
其里面就已经绑定了一个动态的虚拟手机号码(这个虚拟号码,经常会变动), 所以,不需要抠出电池,装手机卡
拨号打电话
建桥 和 拆桥 的ACK SYN等信号 ,不是在要建立的桥上传送的(桥上传输的 只有要传输的数据)
用于确认是否收到 数据的信号,也不是在桥上传送的(相当于 收到快递后,打电话告诉对方我已收到货物;而不是 通过快递再寄过去 '我已收到货物'的一个信息)
类似:
双方通过收发快递,
两端的人打电话询问 是否 收到货物
而不是通过快递的方式 寄送 一张小纸条写着'我已收到'之类的信息
半连接池,暂存发送过来的请求.
如果半连接池满了,则会直接拒绝请求
服务器总共可以容纳请求的数量为 (容量池)n+1
1 表示当前正在服务的哪个请求
udp 没有listen listen是监听建立的连接
而 upd则根本就没有建立连接
SOCKET.AF_INET指定时基于网络的套接字
stream 指定为流式(表示可以分批次收)
conn是一个双向链接 用于收发
服务端必须要绑定 固定地址
客户端没必要绑定 固定地址
客户端的connect 对应 服务端的accept
recv(最大的字节接收个数)
发送,要指定原文件的编码,
中间都是要转成二进制编码 byte类型
接收,要进行解码
服务端的两个循环
1、链接 循环: 与下一个请求建立连接
接客: 用于接通新客户
# 要把conn,client=phone.accept()包括在 循环中
# 因为 每一次都是进行依次新的通话
2、通信 循环: 处理当前请求
服务当前客人
这种方式:接客(一次接待一个人) 与 服务(一次服务一个人) 同一时间只能进行一样
丧失了并发的效果,不过保证了 一直工作的大前提
未引入线程时 进程只能充当一个人
上述 就是如此,前后始终都只要一个人
引入线程后 一个线程可以充当一个人
分出多个人,一波人干 接待 一拨人干 服务
客户端的connect 对应 服务端的accept
在编写客户端时 应该在信息即将要发送的信息都准备好,然后在建立连接并轮转为自己服务的时间段内,快速的完成数据的发送, 而千万不要都为该客户服务了, 而该客户还在磨磨唧唧的,占用宝贵的服务器资源
注意:windows系统返回的信息 的编码为:gbk
对返回的结果进行解码时 要选择正确的编码
规定头的长度 目的:知道 从哪里开始 到哪里结束
socketsever模块 实现并发(tcp 和 upd 都行)
rcvd:收到的东西首先是放在 缓存中的,缓存 是位于内存的, 也不可能把内存都当做缓存用
这个缓存 是可以动态增加的,但是占用的还是内存的存储空间,所以还是不能无限大
一次性发过来的数据,全部存储在这个缓存之中,通过rcvd取出来的时候,可以指定一次从这个缓存中取出多少个字节
在rcvd(字节长度)中可以指定缓存中一次能接收的数据的最大长度
可解决 命令这一种情况的问题,因为命令的结果不会很大,
但是如果要传的是一个文件 大小为几十个G,则内存是不够的
如果客户端发送的内容为 空: 服务端虽然接收到了这个空,但是对服务端的缓存没有任何作用,内容纹丝不动,不会有丝毫增加
私网 ip 是没有办法 往外走的
要使用公网ip
阿里云
服务端监听 0 0 0 0(阿里已做好映射关系)
客户端连的时候 连的是公网ip
============================> 01 基于tcp协议实现简单套接字通信 <===============================
client端:
import socket
# 1、买手机
phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # SOCK_STREAM=》TCP协议
# 2、拨电话
phone.connect(("127.0.0.1", 8080))
# 3、发/收消息
phone.send("hello".encode('utf-8'))
data = phone.recv(1024)
print("服务的返回的数据:", data.decode('utf-8'))
# 4、关闭
phone.close()
sever端:
import socket
# 1、买手机
phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # SOCK_STREAM=》TCP协议
# 2、插手机卡
phone.bind(("127.0.0.1", 8080)) # 本地回环
# 3、开机
phone.listen(5)
print('starting %s:%s' %("127.0.0.1", 8080))
# 4、等电话链接
conn, client_addr = phone.accept()
# 5、收/发消息
data = conn.recv(1024) # 最大接收的字节个数
print("收到的客户端数据:", data.decode('utf-8'))
conn.send(data.upper())
# 6、关闭
conn.close() # 挂电话
phone.close() # 关机
=====================================> 02 加上循环 <=========================================
注意: 老式NOKIA手机使用流程(拔掉电池,插手机卡,然后开机)
client端:
1、买手机,并为买的手机指定参数:
AF_INET 指定是基于网络的套接字
SOCK_STREAM 指定为流式(表示可以分批次收,每次接收的数据大小有最大限制)
phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # SOCK_STREAM=》TCP协议
2、拨电话 # 客户没必要绑定一个固定的手机号
拨号建立连接
# 输入客服的手机号后进行拨号
phone.connect(("127.0.0.1", 8080))
3、发/收消息(通话中)=>通信循环
一直和客服进行通话
while True:
# 客户咨询内容
msg = input(">>: ").strip()
# 手机将咨询内容编码后进行发送
phone.send(msg.encode('utf-8'))
# 手机接收客服的回复
data = phone.recv(1024)
# 将该回复 解码后进行查看
print("服务的返回的数据:", data.decode('utf-8'))
4、关闭(挂电话)
phone.close()
sever端:
"""
服务端应该满足的特性:
1、一直对外提供服务
2、并发地提供服务
"""
1、买手机
# 购买一个手机,并且对手机的工作方式进行设定
# ①指定手机是基于网络工作的
# ②指定手机处理数据是基于流形式的(即一个大数据可以分多次接收)
phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # SOCK_STREAM=》TCP协议
2、插手机卡
客服,要绑定一个手机号
# phone.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) #就是它,在bind前加
phone.bind(("127.0.0.1", 8080)) # 本地回环
3、开机
# 设置 在等待队列中的最大等待个数
# 开机,开启监听模式(并指定可拨入电话的最大数量)
# 实际电话中的客户个数+1 因为当前正在服务的有一个人,还有两个人在线上等待
phone.listen(5)
print('starting %s:%s' %("127.0.0.1", 8080))
4、等电话链接(等其他人的电话拨进来)=>链接循环
一直进行扫描: 如果上一次通话结束, 则从半连接池中取出一个请求,与之建立连接(服务端 到 客户端 的桥)
while True:
conn, client_addr = phone.accept()
本次通话的控制权 客户的手机号 建立的连接
通过本次建立的连接 得到 本次通话连接的控制权(即挂掉电话等权利) 和 客户的手机号
print(client_addr)
5、收/发消息(通话中)=>通信循环
一直与当前对客户进行交流, 如果对方不发送咨询内容,就结束本次服务
while True:
try:
data = conn.recv(1024) # 最大接收的字节个数
如果当前客户,不发送咨询内容了,就结束本次服务
if len(data) == 0: # 针对linux系统
break
对接收的内容,进行翻译查看
print("收到的客户端数据:", data.decode('utf-8'))
对客户的咨询,进行答复
conn.send(data.upper())
except Exception: # 针对windows系统
break
6、关闭(断开本次连接,即 挂掉本次通话)
conn.close()
挂掉电话,并开始从后面的电话中接入一个新电话
7、关机
phone.close()
=======================================> 远程执行命令 <=====================================
client端:
import socket
# 1、买手机
phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # SOCK_STREAM=》TCP协议
# 2、拨电话
phone.connect(("127.0.0.1", 8080))
# 3、发/收消息=>通信循环
while True:
cmd = input("[root@localhost]# ").strip()
phone.send(cmd.encode('utf-8'))
data = phone.recv(1024)
print(data.decode('gbk'))
# 4、关闭
phone.close()
sever端:
"""
服务端应该满足的特性:
1、一直对外提供服务
2、并发地提供服务
"""
import socket
import subprocess
# 1、买手机
phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # SOCK_STREAM=》TCP协议
# 2、插手机卡
phone.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) # 就是它,在bind前加
phone.bind(("127.0.0.1", 8080)) # 本地回环
# 3、开机
phone.listen(5)
print('starting %s:%s' % ("127.0.0.1", 8080))
# 4、等电话链接=>链接循环
while True:
conn, client_addr = phone.accept()
print(client_addr)
# 5、收/发消息=>通信循环
while True:
try:
cmd = conn.recv(1024) # 最大接收的字节个数
if len(cmd) == 0: # 针对linux系统
break
obj = subprocess.Popen(cmd.decode('utf-8'),
shell=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE
)
res=obj.stdout.read()+obj.stderr.read() # ???
print(res)
conn.send(res)
except Exception: # 针对windows系统
break
# 6、关闭
conn.close() # 挂电话
phone.close() # 关机