林海峰博客:https://www.cnblogs.com/linhaifeng/articles/6129246.html
第6个视频,什么是socket
网络通信
物理层:
光缆、双绞线、无线电波
数据链路层:
ethernet,数据分为报头和数据
网络层:MAC地址用于标识本局域网内的计算机
IP地址+子网掩码相与可以计算出网关
如果发送数据中的发送和接收不在一个局域网内,则将数据交给网关,网关进行转发
传输层:
跑TCP或UDP协议,都是基于端口的,
应用层:
开启软件,监听一个端口,端口是和网卡上的IP绑定的
Socket就是将各层封装在一起并隐藏,只提供一个接口
Socket套接字起源:伯克利分校研究的多个程序之间的进程之间通信使用
AF_INET address Family Internet
六、套接字工作流程
import socket
s1=socket.socket(socket.AF_INET,socket_STREAM)#socket.AF_INET代表基于网络通信,socket_STREAM代表TCP
s1.bind(('127.0.0.1',8000))#绑定IP和端口,监听本机的网卡
s1.listen(5)#最多5个连接
conn,addr=s1.accept()#等待连接
msg=conn.recv(1024)#接收数据
print("收到客户端信息",msg)
conn.send(msg)
conn,close()
s1.close()
TCP建立连接要3次握手,断开连接要4次挥手
backlog 半连接池
SYN洪水攻击
cmd中netstat可以查看活动的连接
七、
1、socket收发信息的原理
内核态:存放操作系统代码的区域
用户态:内存中存放用户软件代码的区域
socket工作于用户态内存
网卡硬件工作于内核态
TCP socket发送空,可以发送,但是网卡不会将空发出去
内核态中缓存没有数据,程序就卡在recv()处
recv(1024)一次读最多1024个字节,如果缓冲区中只有一个字节,则只读一个字节
队列:吃了拉
堆栈:吃了吐
2、使用异常捕获处理,当客户端断开连接,服务端会一直卡在recv,此时客户端断开,服务端会报错:connectiontResetError[WinError 10054]远程主机强迫关闭了一个现有的连接
使用异常捕获
try:
except Exception:
break
3.unix 系统下客户端断开连接后服务端不会报错,而是recv接收到的是空数据,则处理方法是添加空判断
if no data:break
4、服务端关闭重启可能会遇到的错误:
OSERROR :[Error 48]Address already in use
在cmd中可以查看此端口的状态:netstat -an |grep 8080
查到改端口还处于FIN_WAIT2状态,所以地址不可以用
方法1:绑定端口之前,添加一句socket配置,重用IP和端口
phone = socket(AF_INET,sock_STREAM)
phone.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)#phone.setsocket(SOC_SCOKET,SO_REUSEADDR,1)#重新使用该地址
phone.bind('127.0.0.1',8080)
方法2:修改内核中FIN_WAIT2的等待时间
八、UDP套接字
UDP不急于链接,直接向某个地址发送,不管是否收到
recvform
sendto
服务端:
from socket import *
ip_port=('127.0.0.1',8080)
buffer_size=1024
udp_server=socket(AF_INET,sock_GGRAM)#数据报
udp_server.bind(ip_port)
while True:
data=udp_server.recvfrom(buffer_size)
print(data)
客户端:
from socket import *
ip_port=('127.0.0.1',8080)
buffer_size=1024
udp_clinet=socket(AF_INET,sock_GGRAM)
while True:
msg=input('>>').strip()#移除字符串头尾的空格
udp_client.sendto(msg.encode('utf-8'),ip_port)
recv在自己这一端的缓冲区为空时阻塞
recvfrom在自己这端的缓冲区为空时就接收一个空
远程执行cmd命令,使用subprocess
使用管道方式实现两个程序之间通信
res=subprocess.Popen('dir',shell=True,stdout=subprocess.PIPE)#将返回数据交给管道
res.stdout.read().decode()#读出的是字节需要解码
如果命令不合法则返回的错误信息需要在stderr中读取
res.stderr.read()
如果发送cd..
应为cd..没有返回值,此时再读取cmd_res=res.stdout.read()卡死
九、粘包
只有TCP有粘包的现象;UDP没有粘包现象
TCP两种粘包:1.多个短包合并;2.一次发送量大,1024读不完
UDP上次没接收完,下次直接丢掉
TCP协议是面向流的协议,应用程序不知道一条消息中有多少字节,所以容易产生粘包
TCP发送字节流
UDP发送字节,但UDP会添加消息头
UDP支持一对多模式,所以接收端的socket(套接字缓冲区)采用了链式结构记录每一个到达的UDP包
在每个UDP包中有消息头(消息的来源地址、端口等信息)
对接收端可以根据消息头区分,在下一次IP地址之前的全是一条消息
TCP是基于数据流的,收发消息不能为空,这就需要在客户端和服务端都添加空消息处理机制,防止程序卡死
而UDP是基于数据报的,即使你输入的是空,那也不是空消息,UDP协议会帮你添加消息头
TCP是面向连接,面向流,使用了Nagle优化算法,将多次间隔较小且数据量小的数据,合成一个大的数据块,然后进行封包。这样接收端就很难分辨,必须提供科学的拆包机制,即面向流的通信是无消息保护边界的
连续发送了3个短字符串,接收端会只接收到一个合并的字符串,粘包
UDP不会粘包,但会丢数据
TCP会粘包,但不会丢数据
怎样避免粘包
方法1:
需要将需要接收的数据长度发给接收端,
服务端先发送len(cmd_res),等待对方发送ready回信
收到ready再次发送实际的数据
接收数据一侧则使用while循环读,一直读到接收完
方法2:
将字节数直接封包为4byte的字节流,直接发送,而不是像方法1中转为字符串发送后再确认
import struct
struct.pack('i',12)#将12封装成4个字节的字节流,'i'代表int ,4个字节
结果为:b'\x0C\X00\X00\X00
方法3:
iteration
day31 上节课复习
MTU 1500byte
如果是ftp发送文件
fileinfo={
'finename':'a.txt',
'filesize':13333
}
将字典转换为字节流
import pickle
pickle.dunp(fileinfo)
将字节流还原为字典:pickle.loads()
发送文件
1.发送文件报文头长度
2.发报文头内容
3.发真实的数据
day31
socket_servet并发
import scoketserver
class Myserver(socketserver.BaseRequestHandler):
def handle(self):#必须重定义一个handle函数
print('conn is ',self.request)
print('addr is ',self.client_address)
while True:
data=self.request.recv(1024)
self.request.sendall(data.upper())
if _name_ =='_main_':
s=socketserver.ThreadingTCPServer(('127.0.0.1',8080),Myserver)
s.serve_forever()
scoketserver类中
server类:处理链接
request类:处理通信
ForKingUDPServer进程并发(在Unix下可运行,window下不可用)
ThreadingUDPServer线程并发
进程并发比线程并发系统开销大
只看到第5个