Python——socket&socketserver(网络编程)
socket通常也称作"套接字",用于描述IP地址和端口,是一个通信链的句柄,应用程序通常通过"套接字"向网络发出请求或者应答网络请求。
socket起源于Unix,而Unix/Linux基本哲学之一就是“一切皆文件”,对于文件用【打开】【读写】【关闭】模式来操作。socket就是该模式的一个实现,socket即是一种特殊的文件,一些socket函数就是对其进行的操作(读/写IO、打开、关闭)
socket和file的区别:
- file模块是针对某个指定文件进行【打开】【读写】【关闭】
- socket模块是针对 服务器端 和 客户端Socket 进行【打开】【读写】【关闭】
在Scoket里的表达方式:
Socket Families(地址簇) IP层
- socket.AF_UNIX unix本机进程间通信
- socket.AF_INET IPV4
- socket.AF_INET6 IPV6
Socket Types 传输层
- socket.SOCK_STREAM #for tcp
- socket.SOCK_DGRAM #for udp
socket.SOCK_RAW #原始套接字,普通的套接字无法处理ICMP、IGMP等网络报文,而SOCK_RAW可以;其次,SOCK_RAW也可以处理特殊的IPv4报文;此外,利用原始套接字,可以通过IP_HDRINCL套接字选项由用户构造IP头。
socket.SOCK_RDM #是一种可靠的UDP形式,即保证交付数据报但不保证顺序。SOCK_RAM用来提供对原始协议的低级访问,在需要执行某些特殊操作时使用,如发送ICMP报文。SOCK_RAM通常仅限于高级用户或管理员运行的程序使用。
特点:
1. 默认socket.socket()为:IPv4地址与TCP的传输
2. 2.X版本能发字符串,比特,字节。但在3.X中只能发bit类型。
3. 需要注意的是粘包问题(就是两个发送的数据全部在一起)
4. DUP传输时,需要注意防火墙是否允许通过。
粘包:
发生在发送端的粘包:
由于两个数据的发送时间间隔短+数据的长度小,再加上TCP的优化机制(将两个较小数据和发送时间间隔很短的合并在一起发送,这样会减少TCP对数据的确认包)
发送在接收端的粘包:
由于TCP协议中所传输的数据无边界,所以来不及接收的多条数据会在接收方的内核缓存并粘起来。
可以通过struct模块来解决。
import struct
value = 345223123
value_struct = struct.pack('i',value) #通过pack来将字节数控制到4个字节,那么接收方先进行4字节的接收,再接收其他的就不存在粘包问题了。
print(value_struct) #b'\xd3\xaf\x93\x14'
import struct client_data = {'client_info':'login',#用户信息 'client_status':False,#用户状态 'client_name':None,#用户名 'client_pwd':None,#密码 'server_con':None,#服务器回复信息 'data_name':None,#如有数据,将写数据的名字 'data_size':None,#如有数据,将写数据的大小 } data_len = len(client_data) print(data_len) data_len_struct = struct.pack('i',data_len) print(data_len_struct) print(struct.unpack('i',data_len_struct)[0])
标准配置:
1. 可以允许多个用户接入,但只有一个能进行数据传输,只有一个断了,下一个才能进行数据传输。
2. 服务器一直在监听状态。
3. 用户无法输入空的字符串
#Server
import socket
server = socket.socket()
server.bind(('127.0.0.1',8888))
server.listen(2) #需要监听用户的个数
while True:
conn,addr = server.accept() #接收一个新的连接。conn是每进来一个连接,都为其创建一个实例,addr为连接进来的IP地址。
while True:
data = conn.recv(8192) #监听应该为连接进来的实例,多大为10M左右,官方建议最多不超过8192K。
if not data:break #为了解决客户端一断开就进入死循环。因为客户端一断开就会发送空数据。
print('recv:',data.decode())
conn.send(data.upper())
server.close()
#Client
import socket
client = socket.socket()
client.connect(('127.0.0.1',8888))
while True:
choice = input('==>:')
if len(choice) == 0: continue
client.send(choice.encode()) #把目前的unicode字符编码转换为utf-8字符编码
data = client.recv(1024)
print(data.decode()) #将utf-8字符编码进行转换
client.close()
一个简单的SSH:
存在的问题:
1. 在检查字符长度时,可能存在中文和英文长度上的差异,就是server发过来的要比实际发送数据过来的要小。
__author__="XinB"
#Server
import socket,os
server = socket.socket() #将socket模块导入并赋值
server.bind(('localhost',9999)) #写服务器的Ip地址和接收的端口号
server.listen(2) #开始监听,并设置同时进入的客户端数量。
while True:
conn,addr = server.accept() #开始接收客户端(会卡到该过程,等待用户接入)
while True:
data = conn.recv(1024) #如果用户接入,接收发过来的信息。
if not data:break #如果用户发过来的信息是空,那么将结束用户并断开该用户。
cmd_recv = os.popen(data.decode()).read() #如果不为空,那么将用户传输过来的,应用到dos系统命令中。
if len(cmd_recv) == 0: #判断如果返回的值为空(用户输入错误将会返回为空),那么将打印。
cmd_recv = '无效输出'
conn.send(str(len(cmd_recv.encode('utf-8'))).encode('utf-8')) #将系统返回的值,计算值的大小,将大小发送给用户。
conn.send(cmd_recv.encode('utf-8')) #再将值发送给客户端。
server.close()
__author__="XinB"
#Client
import socket
client = socket.socket() #将socket模块导入并赋值
client.connect(('localhost',9999)) #客户端填写服务器端Ip地址和端口号
while True:
choice = input('>>>:').strip() #循环用用户输入字符串
if len(choice) == 0: continue #如果用户输入的为空将返回重新输入。
client.send(choice.encode()) #如果不为空,将发送给服务器端
cmd_res = client.recv(1024) #第一次接收的是将要发过来数据的长度。
resved_size = 0 #创建一个初始为0的变量,将判断接收的数据是否已经满足要求。
resved_data = '' #创建一个空字符,将显示所返回的结果。
while resved_size < int(cmd_res.decode()): #循环在resved_size 小于服务器返回值的时候。
data = client.recv(1024) #开始接收数据
resved_size+=len(data) #将接收数据的大小添加到空变量
resved_data += data #将接收到的数据添加到空字符中。
else:
print(resved_data.decode()) #如果全部接收完毕,将所有的数据打印出来。
client.close()
socket-server
特点:
- socket的再封装版本。使其更加简化
- 可以实现多用户接入,socket-server主要用于server端,client端使用socket写即可。
- 必须继承socket.BaseRequestHandler的父类。
- 所有的客户端请求,全部由handle方法处理。
- 每一个客户端请求,都会实例化一个server classes。
- 3.Xpython 由于客户端断开后会报连接错误,所以需要try一下。
- 请求过来之前可以使用setup,请求处理中可以用handle,请求处理之后可以使用finish。
socket-server分类:
1. class socketserver.TCPServer()
使用与TCP的连接使用
2. class socketserver.UDPServer()
使用与UDP的连接使用,所使用的语法和TCPserver是一样的。
3. class socketserver.UnixStreamServer()
在本机,用于两个不同进程之间的通信使用。
初始化案例:
import socketserver
class Myserver(socketserver.BaseRequestHandler): #必须要继承该类
def handle(self): #必须填写方法
'''
1. handle方法针对的是每一个客户端都会独立出来一个。而不是公用这一个handle方法。
2. handle必须的原因是父类有相同方法,如果要自定义就要自己写此方法。
3. self.client_address为客户端的Ip地址加端口。
4. self.server为服务器的IP地址。
5. self.request为客户端连接信息。(跟socket的conn一样)
:return:
'''
print(self.request)
self.request.send() #发送
self.request.recv() #接收
server = socketserver.ThreadingTCPServer(('127.0.0.1',9000),Myserver)
server.serve_forever()

浙公网安备 33010602011771号