socket网络编程
- socket基于tcp、ip协议的工具
- 所有的网络通信都基于socket
- wanghzh/p/5560787.html
- wupeiqi/articles/5040823.html
- 客户端去访问服务器:
- TCP:三次握手
- UDP:单向访问
- python支持unix系统进程与进程之间的通信
自己实现socket客户端和服务端
socket服务端
import socket
sk = socket.socket() # 服务器启动,创建socket
ip_port = ('127.0.0.1', 9999)
sk.bind(ip_port) # socket绑定ip和端口作为服务端
sk.listen(5) # socket服务器支持连接等待的数量
while True:
sock, addr = sk.accept() # 阻塞等待客户端访问
sock.sendall(bytes('已链接到服务器', encoding='utf-8'))
print(type(sock))
while True:
try:
byte_data = sock.recv(1024) # 客户端如果断开了,这里会报异常
data = str(byte_data, encoding='utf-8')
ret = '服务器:%s' % data
sock.sendall(bytes(ret, encoding='utf-8'))
if data == 'q':
break
except Exception as e:
break
else:
pass
finally:
pass
socket客户端
import socket
client = socket.socket() # 创建socket
client.connect(('127.0.0.1', 9999,)) # socket去连接服务端
byte_data = client.recv(1024)
print(str(byte_data, encoding='utf-8'))
while True:
inp = input()
client.sendall(bytes(inp, encoding='utf-8'))
byte_data = client.recv(1024)
data = str(byte_data, encoding='utf-8')
if data == '服务器:q':
print('断开连接')
break
else:
print(data)
client.close()
- socket.sendall()方法在python2.7中接收字符串,在python3.x中接收ude是字节
socket更多方法
- wupeiqi/articles/5040823.html
sk = socket.socket(family=socket.AF_INET,type=socket.SOCK_DGRAM,proto=0)
# family:socket.AF_INET = IPv4(默认) IPv6
# type:TCP协议
# proto:默认为0
sk.bind((ip,port,)
# 服务端绑定IP和port
sk.listen(count)
# 开始监听,参数表示最多等待的数量,当count == 2
# 一个客户端连上了服务器,如果再有2个客户端访问,后面的2个会处于等待状态,当再有客户端访问,超过count == 2的客户端会报错
sk.setblocking(bool)
# 是否阻塞
# True:阻塞(默认)
# False:不阻塞,当socket调用recv和accept时会报错,以后会用到
sk.connect((ip,port,)
# 客户端连接服务端
sk.connect_ex((ip,port,)
# 同上,只是如果连接上,则返回0,如果连不上,返回错误码
sk.close()
# 断开连接
sk.recv(1024)
# 接收信息,参数表示一次最所接收多少个字节
sk.recvfrom(buffersize)
# 从哪里接收UPD用到
sk.send()
# 发送信息,2.7中接收字符串,3.x中接收字节
# send方法有个弊端,实际发送的数据会出现少于实质传入的数据
sk.sendall()
# 底层通过循环和send方法发送数据,不会出现数据丢失
sk.sendto()
# 针对UDP
sk.settimeout()
# 设置超时
sk.getsocketname()
# 返回socket自己的地址
sk.getpeername()
# 返回socket连接远程的地址
sk.fileno()
# socket的文件描述(后面会用到)
socket实现文件上传
- 注意防止粘包问题
server
import socket
server = socket.socket()
server.bind(('127.0.0.1', 9999))
server.listen(5)
print('服务器启动:\n>>>>>')
while True:
conn,address = server.accept()
print('获取到客户端访问:ip:%s port:%s'%(address[0], address[1]))
retStr = '文件上传服务器,请发送文件名以及文件大小'
conn.sendall(bytes(retStr, encoding='utf-8'))
fileInfo = conn.recv(1024)
fileInfo = str(fileInfo, encoding='utf-8')
fileName, fileLength = fileInfo.split('&')
print('获取到文件名:%s 文件大小:%s'%(fileName,fileLength))
conn.sendall(bytes('接收成功',encoding='utf-8'))
totalLength = int(fileLength)
hasLoadSize = 0
file = open(fileName, mode='wb')
while True:
if hasLoadSize >= totalLength:
break
data = conn.recv(1024)
file.write(data)
client
import socket
import os
client = socket.socket()
client.connect(('127.0.0.1', 9999))
connectInfo = client.recv(1024)
connectInfo = str(connectInfo, encoding='utf-8')
print(connectInfo)
fileName = input('请输入文件名\n>>> ')
fileLength = os.stat('dest_top_img.jpg').st_size
ret_msg = '%s&%s'%(fileName,fileLength)
client.sendall(bytes(ret_msg, encoding='utf-8'))
rec_msg = client.recv(1024)
print(str(rec_msg, encoding='utf-8'))
print(str(rec_msg, encoding='utf-8'))
with open('dest_top_img.jpg', mode='rb') as file:
for line in file:
client.sendall(line)
client.close()
- 粘包原因:
- 报错:ImageNotLoaded(下载完后打不开文件)
- 客户端发送文件到服务端,需要依赖系统的缓冲区
- 服务端接收客户端发送的文件,需要到缓冲区取
- 但是客户端缓冲区什么时候发送是系统决定的,不可控,可能存在这种情况,当发送文件名和文件大小给服务器的时候,数据存放在缓冲区还没发送出直接发送文件内容,当服务端获取文件和文件大小的字段带有一部分文件的内容,这就导致需要发送的文件到服务器实际接收的时候不完整
- 解决:发送文件名、大小和发送文件之间与服务器做一个响应
socketserver模块实现多并发
- socket模块不支持多并发,服务端同一时刻只支持响应一个客户端请求
- 处理并发有两种方式:
IO多路复用
-
socketserver模块:由python提供的另一个模块,用于解决多并发访问的问题
- 内部实现:IO多路复用&多线程或多进程并发操作
-
实现步骤:
- 创建类继承:socketserver.BaseRequestHandler
- 创建server,传入ip端口和刚创建的类
- 开启服务
```
import socketserver
class MyServer(socketserver.BaseRequestHandler):
def handle(self):
print(self.request) # 客户端的连接
print(self.client_address) # 客户端地址
print(self.server) # 当前服务器:server
if __name__ == '__main__':
server = socketserver.ThreadingTCPServer(('127.0.0.1',9999),MyServer)
server.serve_forever()
# <socket.socket fd=520, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 9999), raddr=('127.0.0.1', 4971)>
# ('127.0.0.1', 4971)
# <socketocketserver.ThreadingTCPServer object at 0x00FAB310>
```
```
import socket
client = socket.socket()
client.connect(('127.0.0.1',9999))
```
梳理
- 对象中封装对象
- 面向对象的两种特性
- 装饰器 + 方法
- @property + 方法
- python2.7继承流程
- 继承object类(新式类):顶部最后
- 未继承object类(经典类):一条道走到黑,深度优先
- python3.x继承流程
- 顶部最后
- 抽象类和抽象方法(python中目前用的不多)
- 约束
- 接口(python没有接口)
- 抽象类+抽象方法