文章目录
socket
socket
客户端/服务器架构
C/S(Client/Server)架构
B/S(Browser/Server)架构
OSI(Open System Interconnect)七层模型
Socket
●socket (套接字)起源于Unix , socket即是一 种特殊的文件,一些socket函数就是对其
进行的操作(读/写IO、打开、关闭)
●它是TCP/IP网络环境下应用程序与底层通信驱动程序之间运行的开发接口,它可以将应
用程序与具体的TCP/IP隔离开来,使得应用程序不需要了解TCP/IP的具体细节, 就能够
实现数据传输。
●网络应用程序中, Socket通信是基于客户端/服务器结构。
服务端
"""
服务器监听的方式
1.监听IP+端口
2.监听套接字 -> socket
监听文件,Server端-> socket 发送接收数据,client端 -> socket发送接收数据
Server -> socket <- Client
本地服务
服务端:
1.创建socket
2.绑定ip+port <- 客户端来连 接的时候
3.监听客户端的连按
4.接收客户端的请求-> 阻塞状态
客户端与服务端进行交互=>接收数据/发送数据
5.关闭socket
客户端:
1.创建socket
2.诖接服务端=> ip+port
客户端与服务端进行交互=>接收数据/发送数据
3.关闭socket
"""
import socket
#创建一个套接字
server = socket.socket()
#绑定监听ip和端口
#0.0.0.0表示监听本机所有ip地址
server.bind(("0.0.0.0",8888))
#监听,设置可以有多个客户端连接进来
server.listen(2)
#接收客户端,如果进行accept时,程序进入阻塞状态
#返回两个数据,一个是客户端连接,一个是ip和端口
print("start.....")
conn,addr = server.accept()
print("有一个客户端连接进来了",conn,addr)
#接收客户端数据
#python3在网络上进行传输的格式都是bytes格式的
accept_data = conn.recv(1024)
print("接收的数据为:",str(accept_data,encoding="utf8"))
#给客户端发送一个数据
conn.sendall(bytes("thankyou!",encoding="utf8"))
conn.close()
print("end......")
"""
如果服务端建立连接之后,第一件事情是recv
那么客户端第一件事情一定要send
所以在编写cs服务时,注意数据发送和接收一定要一一对应
"""
客户端
import socket
client = socket.socket()
#参数是一个元组,连接器
client.connect(("127.0.0.1",8888))
send_data = input(">>>")
client.sendall(bytes(send_data,encoding="utf8"))
accept_data = client.recv(1024)
print(str(accept_data,encoding="utf8"))
client.close()
优化-客户端和服务端进行多次数据交互
#服务端
import socket
#创建一个套接字
server = socket.socket()
#绑定监听ip和端口
#0.0.0.0表示监听本机所有ip地址
server.bind(("0.0.0.0",8888))
#监听,设置可以有多个客户端连接进来
server.listen(2)
#接收客户端,如果进行accept时,程序进入阻塞状态
#返回两个数据,一个是客户端连接,一个是ip和端口
print("start.....")
conn,addr = server.accept()
print("有一个客户端连接进来了",conn,addr)
while True:
#接收客户端数据
#python3在网络上进行传输的格式都是bytes格式的
accept_data = conn.recv(1024)
print("接收的数据为:",str(accept_data,encoding="utf8"))
if not accept_data or accept_data == b"bye":
print("客户端要求断开")
break
#给客户端发送一个数据
conn.sendall(bytes("thankyou!",encoding="utf8"))
conn.close()
server.close()
print("end......")
#客户端
import socket
client = socket.socket()
#参数是一个元组,连接器
client.connect(("127.0.0.1",8888))
while True:
send_data = input(">>>")
if not send_data.strip():
continue
if send_data == "bye":
break
client.sendall(bytes(send_data,encoding="utf8"))
accept_data = client.recv(1024)
print(str(accept_data,encoding="utf8"))
client.close()
优化-连接多个客户端
#服务端
import socket
#创建一个套接字
server = socket.socket()
#绑定监听ip和端口
#0.0.0.0表示监听本机所有ip地址
server.bind(("0.0.0.0",8888))
#监听,设置可以有多个客户端连接进来
server.listen(2)
#接收客户端,如果进行accept时,程序进入阻塞状态
#返回两个数据,一个是客户端连接,一个是ip和端口
print("start.....")
while True:
conn,addr = server.accept()
print("有一个客户端连接进来了",conn,addr)
while True:
#接收客户端数据
#python3在网络上进行传输的格式都是bytes格式的
accept_data = conn.recv(1024)
print("接收的数据为:",str(accept_data,encoding="utf8"))
if not accept_data or accept_data == b"bye":
print("客户端要求断开")
break
#给客户端发送一个数据
conn.sendall(bytes("thankyou!",encoding="utf8"))
conn.close()
server.close()
print("end......")
#客户端
import socket
client = socket.socket()
#参数是一个元组,连接器
client.connect(("127.0.0.1",8888))
while True:
send_data = input(">>>")
if not send_data.strip():
continue
if send_data == "bye":
break
client.sendall(bytes(send_data,encoding="utf8"))
accept_data = client.recv(1024)
print(str(accept_data,encoding="utf8"))
client.close()
socket-tcp编程
●socket ( )
s = socket.socket(socket_ family,socket type,protocal=0)
socket family:
socket.AF INET IPv4 (默认)
eP)
sockct.AF_ INET6 IPv6
socket.AF_ UNIX 只能够用于单一-的Unix系统进程间通信
socket_ type:
socket.SOCK_ STREAM 流式socket , tor TCP (默认)
socket.SOCK_ DGRAM 数据报式socket,forUDP
socket.SOCK_ RAW原始套接字,普通的套接字无法处理ICMP、IGMP等网络报文,而SOCK_ RAW可以;其次,SOCK_ RAW也可以处理特殊的IPv4报文;此外,利用原始套接字,可以通过IP_ _HDRINCL套 接字选项由用户构造IP头。
config.py
addr = ("0.0.0.0",8888)
server.py
import socket
import config
with socket.socket(socket.AF_INET,socket.SOCK_DGRAM) as server:
server.bind(config.addr)
print("start")
while True:
data = server.recv(1024)
print(data)
client.py
import socket
import config
with socket.socket(socket.AF_INET,socket.SOCK_DGRAM) as client:
while True:
data =bytes(input(">>>"),encoding="utf8")
if data == "exit":
break
client.sendto(data,config.addr)
TCP与UDP的区别
TCP =>面向连按,有状态的。确保数据完螯性
UDP =>面向无洼按。有可能会丢包
TCP服务=>需要先启动服务端,再劣客户端连接
ConnectionRefusedError: [Errno 111] Connection refused
ConnectionRefusedError: [WinError 10061]由于日标计算札积极拒绝,无法连接。
UDP => 可以直接启动客户端。不报错,但是肯定也是无法发送数据给服务端的。
可能的原囚:
1.网络连通性=> 192.168.0.110 => ping
2.端口连通性=> telnet
|通」, 马上会断开连接=>服务内部白名单
3.端不通?
a. selinux => Disable
b. firewalld/iptables
c.服务是否启动
d.监听地端几是不是正确
粘包现象
●只有基于tcp的socket
●粘包:发送方发送两个字符串”hello" +”world" ,接收方却一次性接收到
了”helloworld"
●为什么会产生粘包?
应用程序所看到的数据是一个流( stream) , 一条消息有多少字节对应用程序是不可见
的,因此TCP协议是面向流的协议,这也是容易出现粘包问题的原因。
根本原因:两端互相不知道对方发送数据的长度
server.py
import socket
import config
with socket .socket() as server:
server.bind(config . addr)
server.listen(2)
print("start......")
conn, addr = server. accept( )
print("有一个客户端连接进米了",conn, addr)
accept_data = conn. recv(1024)
print("接收到的数据为:",str(accept_data, encoding="utf-8"))
accept_data = conn . recv(1024)
print("接收到的数据为:", str(accept_data, encoding="utf-8"))
print("end......")
client.py
import socket
import config
with socket.socket() as client:
client.connect(config. addr)
client.sendall(bytes("hello",encoding="utf-8"))
client.sendall(bytes("world",encoding="utf-8"))
解决
"""
TCP粘包的产生
TCP是数据是Stream,一条消息有多少个字节,程序不可知的
发送的时候,两条消息间隔很近
接收的时候,不知道消息的长度,于是就两条消息当作一条消息按收了。
sendall + input + sendall=>不粘包
sendall + print + sendll =〉粘包
如何解决?
1.sendall + time.sleep + sendLL =>不粘包
2.send + recv + send =>
"""
#方式一
import socket
import config
with socket.socket() as client:
client.connect(config. addr)
a = input(">>>")
client.sendall(bytes(a,encoding="utf-8"))
b = input(">>>")
client.sendall(bytes(b,encoding="utf-8"))
#方式二
#client.py
import socket
import config
with socket.socket() as client:
client.connect(config. addr)
client.sendall(bytes("hello",encoding="utf-8"))
client.recv(1024)
client.sendall(bytes("world",encoding="utf-8"))
#server.py
import socket
import config
with socket .socket() as server:
server.bind(config.addr)
server.listen(2)
print("start......")
conn, addr = server.accept()
print("有一个客户端连接进米了",conn, addr)
accept_data = conn.recv(1024)
print("接收到的数据为:",str(accept_data, encoding="utf-8"))
conn.sendall(b"ok")
accept_data = conn.recv(1024)
print("接收到的数据为:", str(accept_data, encoding="utf-8"))
print("end......")
ftp服务器
"""
1.启动ftp服务器
2.连接ftp服务器
3.client => filename_path /tmp/a.jpg
服务端读本地文件内容,发送给客户端
客户端保存服务器发送过来的内容
4.在本地文件能打开
能为一个客户端提供多次服务即可
"""
"""
服务端
"""
import os
import socket
import config
with socket.socket() as server:
server.bind(config.ftp_addr)
server.listen(2)
while True:
conn , addr = server.accept()
print("客户端连接过来了~",addr,conn)
#为客户端提供服务
while True:
file_path = conn.recv(1024)
if not file_path:break
if not os.path.exists(file_path):
conn.sendall("文件目录不存在".encode("utf8"))
continue
print("开始传输",file_path)
with open(file_path,"rb") as f:
file_data = f.read()
conn.sendall(str(len(file_data)).encode("utf8"))
conn.recv(1024)
conn.sendall(file_data)
"""
ftp客户端
"""
import os
import socket
import config
with socket.socket() as client:
client.connect(config.ftp_addr)
#用户可以多次下载文件
while True:
data = input("请输入您要下载的文件路径---exit退出")
if not data.strip():continue
if data == "exit": break
filename = os.path.split(data)[-1]
#将要下载的文件路径发送到服务器
client.send(bytes(data,encoding="utf-8"))
#接收服务器发送过来的数据(思考:如果接收的文件数据特别大怎么办?服务端发送数据时,告诉我们文件大小是多少)
file_length = client.recv(1024)
client.sendall(b"ok")
file_data = b""
while True:
file_data += client.recv(1024)
if len(file_data) >= int(file_length):break
with open(filename,"wb") as f:
f.write(file_data)
ftp服务器-多客户端
"""
服务端
"""
import os
import socket
import threading
import config
def worker(conn,addr):
print("客户端连接过来了~",addr,conn)
#为客户端提供服务
while True:
file_path = conn.recv(1024)
if not file_path:break
if not os.path.exists(file_path):
conn.sendall("文件目录不存在".encode("utf8"))
continue
print("开始传输",file_path)
with open(file_path,"rb") as f:
file_data = f.read()
conn.sendall(str(len(file_data)).encode("utf8"))
conn.recv(1024)
conn.sendall(file_data)
with socket.socket() as server:
server.bind(config.ftp_addr)
server.listen(2)
while True:
conn , addr = server.accept()
t = threading.Thread(target=worker,args=(conn,addr))
t.start()
"""
ftp客户端
"""
import os
import socket
import config
with socket.socket() as client:
client.connect(config.ftp_addr)
#用户可以多次下载文件
while True:
data = input("请输入您要下载的文件路径---exit退出")
if not data.strip():continue
if data == "exit": break
filename = os.path.split(data)[-1]
#将要下载的文件路径发送到服务器
client.send(bytes(data,encoding="utf-8"))
#接收服务器发送过来的数据(思考:如果接收的文件数据特别大怎么办?服务端发送数据时,告诉我们文件大小是多少)
file_length = client.recv(1024)
client.sendall(b"ok")
file_data = b""
while True:
file_data += client.recv(1024)
if len(file_data) >= int(file_length):break
with open(filename,"wb") as f:
f.write(file_data)
socket-udp编程
select
linux io模型
阻塞io
买一件事情的时候,等待回复/资源,如果没有得到回复资源,只会等待,其他任何事情都不做
买票=>老王去火车站买票=>排队->一直等->轮到他
烧水=>烧一壶谁=>等水开(在水开之前,什么都不做)
非阻塞io
做一件事的时候,等待回复,先去干一件其他的事情,等一下再来问情况
买票 => 老王去火车站买票->排队看一下->隔N个小时再来看一下
烧水=>烧一壶谁=>等5分钟去看一眼是不是开了
i0多路复用
nginx的并发,如果一个请求i/o,开启一个进程/线程处理这个请求
如果一下就进来2万个请求,是否直接开启2w个线程/进程处理请求
i/o多路复用模型=> 一个线程通过记录i/o状态来同时管理多个i/o,提升服务器的吞吐能力
一个线程管理1024个连接i/o,2万个20个线程
select,poll,epoll
信号驱动io
买一件事情的时候,等待回复/资源,如果没有得到回复资源,先去干别的,第有资源,会通知
买票->留个信息给管理员->有票->打电话通知->去火车站交钱买票
异步io
买一件事情的时候,等待回复/资源,如果没有得到回复资源,先去干别的,第有资源,自动调用并完成接下来的任务
买票->留个信息给管理员->有票->打电话通知->把票寄给你
io多路复用
Select和poll的区别(select有数量的限制1024,poll没有数量的限制)
select-> 1024个fd ->对所有的描述符进行遍历->找出就绪转态的fd
换尿布=>select=>1024=>定期去检查每个baby是否尿片是不是湿了=>找到所有尿布湿了的小孩
找人=>select=>问每一个人
epoll->等通知型
换尿布=>select=>1024=>高级尿布,感应器=>如果湿了,响…=>去给他换
select/poll:
1、当有io就绪,select会遍历所有的fd,来找到就绪fd
2、每次调用select(), 都会把所有的fd_ set从用户态拷贝到内核态
3、select所监控的fd_ set有限制,一般来说最多 只能监听1024个。
4、poll跟select实现类似,只是poll 没有fd监控数量限制
epoll:
1、没有并发连接的限制
2、效率提升不是采用轮询方式,而是回调(有结果通种)
3、fd在整个过程中只拷贝-次
select模块
Python中的select模块专注于I/O多路复用,提供了select、poll、
epoll三个方法
poll epollI在linux中可用 , windows仅支持select。
进程指定内核监听哪些文件描述符(最多监听1024个fd)的哪些事件,当没有文件描述符事件发生
时,进程被阻塞;
当一个或者多个文件描述符事件发生时1进程被唤醒。
文件描述符:
对于Linux而言,所有对设备或文件的操作都是通过文件描述符进行的。
select方法用来监视文件描述符(当文件描述符条件不满足时, select会阻塞) ,当某个文件描述符状态改变
后,会返回三个列表。
fd_ r_ list, fd_ w_ list, fd_ e_ list = select.select(rlist, wlist, xlist, [timeout])
参数:可接受四个参数(前三个必须)
rlist: wait until ready for reading (所有的输入的data,就是指外部发过来的数据)
当序列中的fd满足“可读”条件时,则获取发生变化的fd并添加到fd r_ list中
wlist: wait until ready for writing ( 监控和接收所有要发出去的data )
●当参数2序列中含有fd时,则将该序列中所有的fd添加到fd_ w_ list中
xlist: wait for an “exceptional condition” ( 监控错误信息)
●当参数3序列中的fd发生错误时,则将该发生错误的fd添加到fd_ e list中
三创教育
timeout:超时时间。当超时时间为空 ,则select会-直阻塞,直到监听的句柄发生变化
小案例
"""
select-server
"""
import socket
import select
import config
with socket.socket() as server:
server.bind(config.addr)
server.listen(5)
rlist = [server]
while True:
#当我们执行到这来,程序就会阻塞,等待rlist就绪
rl , _ , _ = select.select(rlist,[],[])
#遍历所有操作fd
for fd in rl:
#如果fd是server=>有新的连接过来
if fd == server:
conn,addr = server.accept()
print("有客户端连接过来了")
#将客户端加入到监听列表
rlist.append(conn)
#接收客户端发送的数据,并直接返回
msg = conn.recv(1024)
conn.sendall(msg)
else:
#已经建立连接的客户端
msg = fd.recv(1024)
if not msg:
rlist.remove(fd)
fd.close()
else:
fd.sendall(msg)
"""
select-client
"""
import socket
import config
with socket.socket() as client:
client.connect(config.addr)
while True:
data = input("请输入要发送的数据(exit):")
if not data.strip():continue
elif data.strip() == "exit":break
client.sendall(bytes(data,encoding="utf8"))
print("recv:",client.recv(1024))
解决bug
"""
select-server
"""
import socket
import select
import config
import threading
def newclient(conn):
# 接收客户端发送的数据,并直接返回
msg = conn.recv(1024)
conn.sendall(msg)
def oldclient(fd):
# 已经建立连接的客户端
msg = fd.recv(1024)
if not msg:
rlist.remove(fd)
fd.close()
else:
fd.sendall(msg)
with socket.socket() as server:
server.bind(config.addr)
server.listen(5)
rlist = [server]
while True:
#当我们执行到这来,程序就会阻塞,等待rlist就绪
rl , _ , _ = select.select(rlist,[],[])
#遍历所有操作fd
for fd in rl:
#如果fd是server=>有新的连接过来
if fd == server:
conn,addr = server.accept()
print("有客户端连接过来了")
#将客户端加入到监听列表
rlist.append(conn)
t1 = threading.Thread(target=newclient,args=(conn,))
t1.start()
else:
t2 = threading.Thread(target=oldclient,args=(fd,))
t2.start()
select与poll
1、select采用轮询的方式遍历所有fd ,监控对应事件是否发生
2、select需要在用户空间与内核空间频繁拷贝fd
3、 select最大仅仅支持1024个文件描述符
4、poll与select相差不大, poll无文件描述符监听限制。
epoll
1、注册新事件时,就将fd拷贝进内核,每个fd在整个过程中只会拷贝一次。
2、为每个fd指定一个回调函数 ,当设备就绪,调用这个回调函数。
3、epoll对文件描述符没有额外限制
epoll
select.epoll()创建epoll对象
epoll.close()关闭epoll对象的文件描述符
epoll.closed检测epol对象是否关闭
epoll.fileno()返回epoll对象的文件描述符
epoll.register(fd[, eventmask])向epoll对象中注册fd和对应的事件
epoll.unregister(fd)取消注册
事件:
EPOLLIN Available for read可读状态符为1
EPOLLOUT Available for write可写状态符为4
EPOLLERR Error condition happened on the assoc. fd发生错误状态符为8
"""epoll-server"""
import socket
import select
import config
with socket.socket() as server:
server.bind(config.addr)
server.listen(5)
#创建一个epoll对象
epoll_obj = select.epoll()
# 将server注册带epoll_obj,监听读事件
epoll_obj.register(server,select.EPOLLIN)
# 存放所有的客户端信息
connections = {}
while True:
# events 就绪的对象
events = epoll_obj.poll()
for fd,event in events:
print(fd,event)
#区分新连接和已经建立的连接
#新连接
if fd == server.fileno():
conn,addr = server.accept()
connections[conn.fileno()] = conn
epoll_obj.register(conn,select.EPOLLIN)
msg = conn.recv(1024)
conn.sendall(msg)
#已经建立的连接
else:
conn = connections[fd]
msg = conn.recv(1024)
# 客户端断开连接,从epoll中取消注册,并断开连接
if not msg:
epoll_obj.unregister(fd)
conn.close()
del connections[fd]
else:
conn.sendall(msg)
"""
epoll-client
"""
import socket
import config
with socket.socket() as client:
client.connect(config.addr)
while True:
data = input("请输入要发送的数据(exit):")
if not data.strip():continue
elif data.strip() == "exit":break
client.sendall(bytes(data,encoding="utf8"))
print("recv:",client.recv(1024))
socketserver
SocketServer内部使用I0多路复用以及“多线程” 和“多进程”, 从而实现并发处理多个客户端请求的Socket服务端。
即:每个客户端请求连接到服务器时,Socket服务端都会在服务器是创建一个“线程”或者"进程”专门负责处理当前客户端的所有请求。
socketserver将socket模块和select模块进行了封装,简化网络服务器版的开发。
socketserver最主要的作用:就是实现一个并发处理。
SocketServer类
四个比较主要的类,其中常用的是TCPServer和UDPServer
1、 TCPServer
2、UDPServer
3、UnixStreamServer,类似于TCPServer提供面向数据流的套接字连接,但是旨在UNIX平台上可用;
4、UnixDatagramServer,类似于UDPServer提供面向数据报的套接字连接,但是旨在UNIX平台上可用;
两种支持异步处理的类:
1、ForkingMixIn ,为每一个客户端请求派生一个新的进程去专门处理;
2、ThreadingMixIn ,为每一个客户端请求派生一 个新的线程去专门处理;
继承自这两个类型的服务端在处理新的客户端连接时不会阻塞,而是创建新的进/线程专门处理安户端的请求.
创建SocketServer
1、创建一一个请求处理类)继承BaseRequestHandlerclass类并且重写父类的handle()方
法,该方法将处理传入的请求。
2、必须实例化-个上面类型中的一个类(如TCPServer )传递服务器的地址和你上面创建
的请求处理类给这个TCPServer。
3、调用handle_ request()或者serve_ forever()方法来处理一个或多个请求
4、调用server close()关闭socket
# socketserver-server
import socketserver
import config
class MyRequestHandler(socketserver.BaseRequestHandler):
"""'这个类的功能-> 每一个请求过来, 应该如何处理"""
def handle(self):
"""核心部分:处理接收的数据"""
# self.request =>类定义的方法=>表示客户端的请求
while True:
data = self.request.recv(1024)
print(data)
if not data: break
# 发送数据回去
self.request.sendall(data)
if __name__ == "__main__":
# 一次只能处理一个客户端的请求! !因为这里并没有使用多进程或多线程
# server = socketserver . TCPServer(config. addr, MyRequestHandLer)
# 一次处理qqr个客户端的请求! !因为这里并没有使用多进程或多线程
# server = socketserver.TCPServer(config.addr,MyRewuestHandler)
server = socketserver.ThreadingTCPServer(config.addr,MyRequestHandler)
print("start...")
server.serve_forever()
server.server_close()
# socketserver-client
import socket
import config
with socket.socket() as client:
client.connect(config.addr)
while True:
data = input(">>>")
if not data:continue
if data == "exit":break
client.sendall(bytes(data,encoding="utf8"))
recv = client.recv(1024)
print(str(recv,encoding="utf8"))
client.close()
案例-聊天
"""
服务器=>启动=>
1.启动服务并监听
2.服务端接收到用户连接
3.接收用户数据
=> @setnome =》返回欢迎您, XXX
=》clientf 与client2通信=>保存起来=> {username: username-socket} =>实例属性/类局烟
=》@username =>发送消息给username => 难点?
=》username => username-socket =》socket.send(消息内容)
=> 如果username找不到对应的socket, 给自己回一个消息-> (当前用户不存在)
=如果断开连接,那么从字典中删除
"""
import socketserver
import config
class MyRequestHandler(socketserver.BaseRequestHandler):
"""这个类的功能 -> 每一个请求过来,应该如何处理"""
socket_dict = dict()
def handle(self):
"""核心部分:处理接收的数据"""
print("一个新客户端连接来啦!")
while True:
print("当前所有的用户有", self.socket_dict)
# self.request => 类定义的方法 => 表示客户端的请求
data = str(self.request.recv(1024), encoding="utf-8")
if not data: break
print("接收到的数据是:", data)
if data.startswith("@setname"):
print("设置用户名") # "@setname lxl abc"
username = ''.join(data.split()[1:])
self.request.sendall(bytes(f"欢迎你!{username}!", encoding="utf-8"))
# 设置当前用户名 => 动态为请求添加了一个socket
self.username = username
self.add_client(username, self.request)
elif data.startswith("@"):
# "@lihu xxxx xxx2"
to = data.split()[0][1:]
message = " ".join(data.split()[1:])
if to in self.socket_dict:
# {"ryc": socket, "lxl": socket}
print(f"给{to}发消息")
self.socket_dict[to].sendall(bytes(message, encoding="utf-8"))
self.request.sendall(bytes("", encoding="utf-8"))
else:
self.request.sendall(bytes(f"您聊天的对象({to})不存在", encoding="utf-8"))
else:
self.request.sendall(bytes(f"数据有误!{data}", encoding="utf-8"))
@classmethod
def add_client(cls, username, client):
cls.socket_dict[username] = client
print("当前所有的用户有",cls.socket_dict)
@classmethod
def remove_client(cls, username):
cls.socket_dict.pop(username)
def finish(self):
"""一个客户端断开"""
if hasattr(self, "username"):
self.remove_client(self.username)
print(f"{self.username}退出了~")
if __name__ == "__main__":
# 一次只能处理一个客户端的请求!!因为这里并没有使用多进程或多线程
# server = socketserver.TCPServer(config.addr, MyRequestHandler)
# 同时处理多个客户端的请求!
server = socketserver.ThreadingTCPServer(config.addr, MyRequestHandler)
print("start....")
server.serve_forever()
server.server_close()
"""
客户端=>启动
1.连接服务器
2.输入你的名字发送给服务端 => @setname x0x
3. 与谁聊天(呢称-》Lihu) => @Lihu infomation
4.如果输入的数据是=> @exit =>表示要断开连接
"""
import socket
import config
with socket.socket() as client:
client.connect(config.addr)
while True:
data = input(">>>")
if not data: continue
if data == "@exit": break
client.sendall(bytes(data, encoding="utf-8"))
recv = client.recv(1024)