socket网络服务
socket就是IP地址加端口定位到唯一一台机器的唯一软件
套节字分两大类型
一个是文件类型套节字家族
AF_UNIX
一个是网络套节字家族
AF_INET
tcp协议:
如何定义socket服务端(模拟通信):
import socket
phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
phone.bind((‘127.0.0.1‘,8080))
phone.listen(5)
while True:#连接循环
conn,addr=phone.accept()
print(‘电话线路是‘,conn)
print(‘客户端的手机号是‘,addr)
while True:#通信循环
try:
data=conn.recv(1024)
if not data:break#在linx中因为系统无法对客户端的断开抛出异常所以会一直接收空的状态所以需要针对linx进行额外判断
print(‘客户端发来的消息是‘,data)
conn.send(data.upper())
except Exception as e:
break
print(‘客户端已经断开‘)
conn.close()
phpne.close()
定义socket客户端:
import socket
phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
phone.connect((‘127.0.0.1‘,8080))
while True:
try:
myinput=input(‘>>‘)
if not myinput:continue
phone.send(myinput.encode(‘utf8‘))
data=phone.recv(1024)
print(data)
except Exception as e:
print(e)
break
phone.close()
当重启服务端的时候有可能会遇上time_wait状态,这个是由于你的服务端仍然存在四次挥手的time_wait状态在占用地址(如果不懂,请深入研究1.tcp三次握手,四次挥手 2.syn洪水攻击 3.服务器高并发情况下会有大量的time_wait状态的优化方法)
解决方法:
phone=socket(AF_INET,SOCK_STREAM)
phone.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) #就是它,在bind前加
phone.bind((‘127.0.0.1‘,8080))
如何解决粘包的问题:
如何不让多个数据粘一块发出去呢?
在发包的时候先发送数据长度,知道长度之后就知道一次要收到多少数据,就可以避免粘包了。
封装报头:
固定长度
发送数据的时候要包含对将要发送数据的详细信息
如何将数据长度打包?
引入模块
import struct
res=struct.pack(‘i‘,数据)
struct.unpack(‘i‘,res)
(数据,)#元祖形式
struct.unpack(‘i‘,res)[0]
客户端定制包头:
import socket,struct,json
import subprocess
phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
如何让服务器重用ip端口号避免冲突?
phone.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) #就是它,在bind前加
phone.bind((‘127.0.0.1‘,8080))#服务器绑定ip加端口号
phone.listen(5)#开启监听
while True:#链接循环
conn,addr=phone.accept()
while True:#通讯循环
cmd=conn.recv(1024)
if not cmd:break
print(‘cmd: %s‘ %cmd)
res=subprocess.Popen(cmd.decode(‘utf-8‘),
shell=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
err=res.stderr.read()
print(err)
if err:
back_msg=err
else:
back_msg=res.stdout.read()
conn.send(struct.pack(‘i‘,len(back_msg))) #先发back_msg的长度
conn.sendall(back_msg) #在发真实的内容
conn.close()
phone.close()
客户端接收报头:
import socket,struct,json
import subprocess
phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
phone.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) #就是它,在bind前加
phone.bind((‘127.0.0.1‘,8080))
phone.listen(5)
while True:
conn,addr=phone.accept()
while True:
cmd=conn.recv(1024)
if not cmd:break
print(‘cmd: %s‘ %cmd)
res=subprocess.Popen(cmd.decode(‘utf-8‘),
shell=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
err=res.stderr.read()
print(err)
if err:
back_msg=err
else:
back_msg=res.stdout.read()
conn.send(struct.pack(‘i‘,len(back_msg))) #先发back_msg的长度
conn.sendall(back_msg) #在发真实的内容
conn.close()
服务端的特点是:
一直运行提供服务(链接循环),(基于一个链接的通信循环)
高并发:
之前学过的服务端客户端都是一对一的,这种方式一次服务器只能访问一个人,其余的挂起状态
而有一个模块可以实现并发的形式
socketserver模块
#高并发服务端,可以接入多个客户端
import socketserver
class FTPserver(socketserver.BaseRequestHandler):#通讯
def handle(self):
conn=self.request
while 1:
data=conn.recv(1024)
conn.send(....)
if __name__==‘__main__‘:
obj=socketserver.ThreadingTCPServer((‘127.0.0.1‘,8080),FTPserver)
obj.sever_forever()#链接循环
#以上代码中每当进来一个客户端链接都会实例化一个通讯对象,通讯对象类中是一个通讯循环
#udp的使用也是如此区别不大
udp协议:
服务端:
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# 绑定端口:
s.bind((‘127.0.0.1‘, 9999))
#因为udp是无链接,所以不用监听接口,因为没有监听所以也没有阻塞
print(‘Bind UDP on 9999...‘)
while True:
# 接收数据:
data, addr = s.recvfrom(1024)
print(‘Received from %s:%s.‘ % addr)
s.sendto(b‘Hello, %s!‘ % data, addr)
客户端:
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
for data in [b‘Michael‘, b‘Tracy‘, b‘Sarah‘]:
# 发送数据:
s.sendto(data, (‘127.0.0.1‘, 9999))
# 接收数据:
print(s.recv(1024).decode(‘utf-8‘))
s.close()