Python 最简单的我的第一个聊天室QQ软件【基于Socket服务】
文章目录
- 什么是Socket
- Python for socket
- 最简单程序通讯
- 基本函数介绍
- MyChat
- 原理解释
- 服务端
- 原理
- 代码
- 客户端
- 原理
- 代码
- 使用介绍
- 全局代码
什么是Socket
所谓套接字(Socket),就是对网络中不同主机上的应用进程之间进行双向通信的端点的抽象。一个套接字就是网络上进程通信的一端,提供了应用层进程利用网络协议交换数据的机制。从所处的地位来讲,套接字上联应用进程,下联网络协议栈,是应用程序通过网络协议进行通信的接口,是应用程序与网络协议栈进行交互的接口
Socket(套接字)可以看成是两个网络应用程序进行通信时,各自通信连接中的端点,这是一个逻辑上的概念。它是网络环境中进程间通信的API(应用程序编程接口),也是可以被命名和寻址的通信端点,使用中的每一个套接字都有其类型和一个与之相连进程。通信时其中一个网络应用程序将要传输的一段信息写入它所在主机的 Socket中,该 Socket通过与网络接口卡(NIC)相连的传输介质将这段信息送到另外一台主机的 Socket中,使对方能够接收到这段信息。 Socket是由IP地址和端口结合的,提供向应用层进程传送数据包的机制
一般Socket服务分为三个步骤
1.服务器监听
所谓服务器监听,是指服务器端套接字并不定位具体的客户端套接字,而是处于等待连接的状态,实时监控网络状态
2.客户端请求
所谓客户端请求,是指由客户端的套接字提出连接请求,要连接的目标是服务器端的套接字。为此,客户端的套接字必须首先描述它要连接的服务器的套接字,指出服务器端套接字的地址和端口号,然后就向服务器端接字提出连接请求 。
3.连接确认
所谓连接确认,是指当服务器端套接字监听到或者说接收到客户端套接字的连接请求,就会响应客户端套接字的请求,建立一个新的线程,并把服务器端套接字的描述发送给客户端。一旦客户端确认了此描述,连接就建立好了。而服务器端套接字继续处于监听状态,接收其他客户端套接字的连接请求。
Python for socket
import socket
最简单程序通讯
server
端
import socket
port = 1314
server = socket.socket()
server.bind(("0.0.0.0", port))
server.listen(10)
connect, address = self.__socket.accept()
print(f"连接方ip为:{address}")
connect.send(byte("Hello World", encoding="utf-8"))
connect.close()
server.close()
client
端
import socket
client = socket.socket()
client.connect(("127.0.0.1", 1314))
get = str(client.recv(1024), encoding="utf-8") # 缓存区为1kb
print(get)
client.close()
基本函数介绍
server
端函数
函数名 | 参数 | 返回值 |
---|---|---|
bind | (ip,port) | None |
accept | None | socket |
listen | int | None |
client
端函数
函数名 | 参数 | 返回值 |
---|---|---|
connect | (ip,port) | None |
common
函数
函数名 | 参数 | 返回值 |
---|---|---|
send | bytes | None |
recv | int | bytes |
close | None | None |
对于服务端,bind的时候如果ip为’0.0.0.0’,说明绑定ip为全绑定,无论是连接内外网ip,都可以连接。而连接connect的时候,如果服务端在公网,就连接公网ip,port需要选择一个未被占用的port
MyChat
原理解释
服务端
原理
服务端做消息转发,服务端架设到公网服务器上,内网客户端连接公网服务器后实现转发消息来通讯,这种模式对于少量的连接很方便,但是连接人数多了就会出现拥挤等错误
代码
server.py
class SocketServer:def __init__(self, logger: Log):self.__logger = loggerself.__socket = socket.socket()self.__isrun = Falseself.__connects = []self.__max = 0def loginfo(self, msg):print("|INFO|\t" + str(msg))self.__logger.logmsg(msg)def createmsg(self, sender, receiver, action, **data) -> str:msg = {"code": 200,"sender": sender,"receiver": receiver,"action": action,"time": time.strftime("%Y/%m/%d %H:%M:%S", time.localtime()),"data": data}return json.dumps(msg)def _logobj(self, data: dict):action = data["action"]if action == "msg":msg = "收到{}发往{}的信息:{}".format(data["sender"], data["receiver"], data["data"]["msg"])self.loginfo(msg)if action == "close":msg = "用户{}关闭了连接".format(data["sender"])self.loginfo(msg)if action == "live":msg = "用户{}查询了当前在线人数:{}".format(data["sender"], len(self.__connects))self.loginfo(msg)def _forward(self, data: dict):try:goal = data["receiver"]# 判断是否为服务器消息if goal == "host":return# 判断是否为群发消息elif goal == "all":print("|INFO|\t开始转发群消息")self.__logger.logmsg("开始转发群消息")for i in self.__connects:i[1].send(bytes(json.dumps(data), encoding=BufferEncoding))return# 监测目标是否上线else:for i in self.__connects:if goal in i:# 找到目标receiver: socket.socket = i[1]# 转发消息receiver.send(bytes(json.dumps(data), encoding=BufferEncoding))return# 未检测到self.loginfo("目标未上线")except Exception as e:self.loginfo(str(e))def _action(self, index, receiver: socket.socket, address, data: dict):action = data["action"]if action == "msg":self._forward(data)return 0if action == "close":self.__connects.remove((index, receiver, address))receiver.close()return -1if action == "live":live = []for i in self.__connects:live.append(i[0])msg = self.createmsg("host", data["sender"], "live", count=len(live), index=live)receiver.send(bytes(msg, encoding=BufferEncoding))return 0def _getmessage(self, index, receiver: socket.socket, address):try:while self.__isrun:data = json.loads(str(receiver.recv(BufferSize), encoding=BufferEncoding)) # 4KB缓存###LOG - Start 服务器Log记录self._logobj(data)###End###Deal - Start 消息处理result = self._action(index, receiver, address, data)if result == -1:returnif result == 0:continue###Endexcept Exception as e:self.loginfo(str(e))finally:self.loginfo(f"{index}的消息接收线程已退出")def _listen(self):self.loginfo("成功启动套接字监听")while self.__isrun:if len(self.__connects) == self.__max:break # 达到最大连接数量断开监听connect, address = self.__socket.accept() # 获取到连接# 生成标识符index = ""for i in range(5):index += str(random.randint(0, 9))self.loginfo("接受到:{}的连接,分配的ID:{}".format(address, index))self.__connects.append((index, connect, address)) # 挂起连接并加入库msg = self.createmsg("host", index, "msg", index=index, msg="连接成功")connect.send(bytes(msg, encoding="utf-8")) # 发生返回信息_thread.start_new_thread(self._getmessage, (index, connect, address,)) # 打开信息监听进程continueself.loginfo("监听线程已退出")def startlisten(self, port, ip="0.0.0.0", count=10):self.__socket.bind((ip, port))self.__socket.listen(count)self.__isrun = Trueself.__max = countself.loginfo("监听初始化完毕,正在启动")self.loginfo(f"开启端口:{port},支持最大连接数量:{count}")_thread.start_new_thread(self._listen, ())def send(self, address, msg):if not self.__isrun:return# 监测目标是否上线for i in self.__connects:if address in i:msg = self.createmsg("host", address, "msg", msg=msg)i[1].send(bytes(msg, encoding=BufferEncoding))self.loginfo("发生成功")returnself.loginfo("目标不在线")def sendall(self, msg):if not self.__isrun:returnfor i in self.__connects:msg = self.createmsg("host", i[0], "msg", msg=msg)i[1].send(bytes(msg, encoding=BufferEncoding))self.loginfo("发送成功")returndef close(self):self.__isrun = False# 释放log对象self.__logger.close()# 等待未完成的任务time.sleep(5)# 释放所有套接字self.__socket.close()for i in self.__connects:i[1].close()
log.py
FileHeader = '''
Socket Log
StartTime:{}
----------------------------------------------------------------
'''FileTail = '''
----------------------------------------------------------------
EndTime:{}
Count:{}
'''InfoTemplate = "|INFO|\t[{}]{}"class Log:def __init__(self, path):self.__path = "{}\\{}.log".format(path, time.strftime("%Y%m%d%H%M%S", time.localtime()))self.__lock = threading.Lock()self.__counter = 0def _writer(self, message):self.__lock.acquire()self.__counter += 1stream = open(self.__path, "a+")stream.write(message + "\n")stream.close()self.__lock.release()def open(self):self._writer(FileHeader.format(time.strftime("%Y/%m/%d/ %H:%M:%S", time.localtime())))def close(self):self._writer(FileTail.format(time.strftime("%Y/%m/%d/ %H:%M:%S", time.localtime()), self.__counter - 1))def logmsg(self, msg):data = InfoTemplate.format(time.strftime("%Y/%m/%d/ %H:%M:%S", time.localtime()), msg)self._writer(data)def logobj(self, obj):data = InfoTemplate.format(time.strftime("%Y/%m/%d/ %H:%M:%S", time.localtime()), json.loads(obj))self._writer(data)
main.py
import json
import _thread
import random
import socket
import threading
import timeBufferSize = 4096 # 最大缓存4KB
BufferEncoding = "utf-8" # 缓存编码reader = open("C:\\0_data\\config.json", "r")
data = json.loads(reader.read())
reader.close()
log = Log(data["path"])
log.open()
server = SocketServer(log)
server.startlisten(data["port"])
try:while True:get = input()if "Close" in get:server.close()print("|INFO|\t已关闭所有进程")breakif "Send" in get:data = get.split("/")server.send(data[1], data[2])else:print("|INFO|\t键盘输出:", get)except Exception as e:server.close()finally:print("|INFO|\t程序已退出")
config.json
{"path": "C:\\0_data","port": 1234
}
客户端
原理
与服务端相似,只不过仅仅只有一个获取数据的线程
代码
client.py
class SocketClient:def __init__(self):self.__socket = socket.socket()self.__isrun = Falseself.__index = -1def close(self):msg = {"code": 200,"sender": self.__index,"receiver": "host","action": "close","time": time.strftime("%Y/%m/%d %H:%M:%S", time.localtime()),"data": {}}self.__socket.send(bytes(json.dumps(msg), encoding="utf-8"))self.__isrun = Falsetime.sleep(5)self.__socket.close()def send(self, address, msg):if not self.__isrun: returnmsg = {"code": 200,"sender": self.__index,"receiver": address,"action": "msg","time": time.strftime("%Y/%m/%d %H:%M:%S", time.localtime()),"data": {"msg": msg}} # 构造返回字符串self.__socket.send(bytes(json.dumps(msg), encoding="utf-8"))print("发送成功")def checklive(self):if not self.__isrun: returnmsg = {"code": 200,"sender": self.__index,"receiver": "host","action": "live","time": time.strftime("%Y/%m/%d %H:%M:%S", time.localtime()),"data": {}} # 构造返回字符串self.__socket.send(bytes(json.dumps(msg), encoding="utf-8"))print("发送成功")def _recive(self):try:while self.__isrun:data = json.loads(str(self.__socket.recv(BufferSize), encoding="utf-8"))if data["action"] == "live":print("目前在线人数为", data["data"]["count"])for i in data["data"]["index"]:print(i)continuemsg = "[{}]{}:{}".format(data["time"], data["sender"], data["data"]["msg"])print(msg)# 退出消息print("已退出消息接收")except Exception as e:print("接收错误", str(e))def startconnect(self, ip, port):self.__isrun = Trueself.__socket.connect((ip, port))# 连接成功# 获取序列号data = json.loads(str(self.__socket.recv(BufferSize), encoding="utf-8"))self.__index = data["data"]["index"]print("连接成功,您的序列号为:", self.__index)# 开启消息监听线程_thread.start_new_thread(self._recive, ())
main.py
import _thread
import json
import socket
import timeBufferSize = 4096
client = SocketClient()
client.startconnect("127.0.0.1", 3414)while True:get = input()if get == "Close":client.close()breakif "Send" in get:data = get.split("/")client.send(data[1], data[2])if "Check" in get:client.checklive()print("程序已结束")
使用介绍
log
类会在服务端处理时记录下所有的信息,在客户端连接后,服务端会生成一个index作为标识符保存在服务器,并且返回一个json字符串到客户端告知index,而客户端想要发送消息的时候,只需要向服务器说明对方的index即可,比如
你的index:12345,对方的index:96354,在控制台里面输入
Send/96354/Hello World
然后程序会创建一个json字符串并发送到服务端
{"code": 200,"sender": "12345","receiver": "96354","action": "msg","time": "2022/4/8 11:09:23","data": {"msg": "Hello World"}
} # 构造返回字符串
然后服务端会先判断消息的action
标签,如果是msg
标签,那么服务器会调用_forward
函数,首先检测receiver的标识符是否存在,然后找到对应的socket,然后使用该socket把这条msg发送过去
如果你想要查询有哪儿些其他用户,可以在控制台输入
Check
然后程序会创建一个json字符串,不同于上面的,该字符串的action
为live
{"code": 200,"sender": "12345","receiver": "host","action": "live","time": "2022/4/8 11:09:23","data": {}
}
当服务器检测到action
不为msg
的json串,会调用_action
函数进行检测然后返回数据
而服务端和客户端关闭的时候也仅需要输入
Close
服务端输入会释放掉所有连接的socket,而客户端会先构造一个action
为close
的json串传到服务器,服务器会释放掉对于的socket,然后客户端才会释放socket
全局代码
server.py
import json
import _thread
import random
import socket
import threading
import timeBufferSize = 4096 # 最大缓存4KB
BufferEncoding = "utf-8" # 缓存编码
FileHeader = '''
Socket Log
StartTime:{}
----------------------------------------------------------------
'''FileTail = '''
----------------------------------------------------------------
EndTime:{}
Count:{}
'''InfoTemplate = "|INFO|\t[{}]{}"class Log:def __init__(self, path):self.__path = "{}\\{}.log".format(path, time.strftime("%Y%m%d%H%M%S", time.localtime()))self.__lock = threading.Lock()self.__counter = 0def _writer(self, message):self.__lock.acquire()self.__counter += 1stream = open(self.__path, "a+")stream.write(message + "\n")stream.close()self.__lock.release()def open(self):self._writer(FileHeader.format(time.strftime("%Y/%m/%d/ %H:%M:%S", time.localtime())))def close(self):self._writer(FileTail.format(time.strftime("%Y/%m/%d/ %H:%M:%S", time.localtime()), self.__counter - 1))def logmsg(self, msg):data = InfoTemplate.format(time.strftime("%Y/%m/%d/ %H:%M:%S", time.localtime()), msg)self._writer(data)def logobj(self, obj):data = InfoTemplate.format(time.strftime("%Y/%m/%d/ %H:%M:%S", time.localtime()), json.loads(obj))self._writer(data)class SocketServer:def __init__(self, logger: Log):self.__logger = loggerself.__socket = socket.socket()self.__isrun = Falseself.__connects = []self.__max = 0def loginfo(self, msg):print("|INFO|\t" + str(msg))self.__logger.logmsg(msg)def createmsg(self, sender, receiver, action, **data) -> str:msg = {"code": 200,"sender": sender,"receiver": receiver,"action": action,"time": time.strftime("%Y/%m/%d %H:%M:%S", time.localtime()),"data": data}return json.dumps(msg)def _logobj(self, data: dict):action = data["action"]if action == "msg":msg = "收到{}发往{}的信息:{}".format(data["sender"], data["receiver"], data["data"]["msg"])self.loginfo(msg)if action == "close":msg = "用户{}关闭了连接".format(data["sender"])self.loginfo(msg)if action == "live":msg = "用户{}查询了当前在线人数:{}".format(data["sender"], len(self.__connects))self.loginfo(msg)def _forward(self, data: dict):try:goal = data["receiver"]# 判断是否为服务器消息if goal == "host":return# 判断是否为群发消息elif goal == "all":print("|INFO|\t开始转发群消息")self.__logger.logmsg("开始转发群消息")for i in self.__connects:i[1].send(bytes(json.dumps(data), encoding=BufferEncoding))return# 监测目标是否上线else:for i in self.__connects:if goal in i:# 找到目标receiver: socket.socket = i[1]# 转发消息receiver.send(bytes(json.dumps(data), encoding=BufferEncoding))return# 未检测到self.loginfo("目标未上线")except Exception as e:self.loginfo(str(e))def _action(self, index, receiver: socket.socket, address, data: dict):action = data["action"]if action == "msg":self._forward(data)return 0if action == "close":self.__connects.remove((index, receiver, address))receiver.close()return -1if action == "live":live = []for i in self.__connects:live.append(i[0])msg = self.createmsg("host", data["sender"], "live", count=len(live), index=live)receiver.send(bytes(msg, encoding=BufferEncoding))return 0def _getmessage(self, index, receiver: socket.socket, address):try:while self.__isrun:data = json.loads(str(receiver.recv(BufferSize), encoding=BufferEncoding)) # 4KB缓存###LOG - Start 服务器Log记录self._logobj(data)###End###Deal - Start 消息处理result = self._action(index, receiver, address, data)if result == -1:returnif result == 0:continue###Endexcept Exception as e:self.loginfo(str(e))finally:self.loginfo(f"{index}的消息接收线程已退出")def _listen(self):self.loginfo("成功启动套接字监听")while self.__isrun:if len(self.__connects) == self.__max:break # 达到最大连接数量断开监听connect, address = self.__socket.accept() # 获取到连接# 生成标识符index = ""for i in range(5):index += str(random.randint(0, 9))self.loginfo("接受到:{}的连接,分配的ID:{}".format(address, index))self.__connects.append((index, connect, address)) # 挂起连接并加入库msg = self.createmsg("host", index, "msg", index=index, msg="连接成功")connect.send(bytes(msg, encoding="utf-8")) # 发生返回信息_thread.start_new_thread(self._getmessage, (index, connect, address,)) # 打开信息监听进程continueself.loginfo("监听线程已退出")def startlisten(self, port, ip="0.0.0.0", count=10):self.__socket.bind((ip, port))self.__socket.listen(count)self.__isrun = Trueself.__max = countself.loginfo("监听初始化完毕,正在启动")self.loginfo(f"开启端口:{port},支持最大连接数量:{count}")_thread.start_new_thread(self._listen, ())def send(self, address, msg):if not self.__isrun:return# 监测目标是否上线for i in self.__connects:if address in i:msg = self.createmsg("host", address, "msg", msg=msg)i[1].send(bytes(msg, encoding=BufferEncoding))self.loginfo("发生成功")returnself.loginfo("目标不在线")def sendall(self, msg):if not self.__isrun:returnfor i in self.__connects:msg = self.createmsg("host", i[0], "msg", msg=msg)i[1].send(bytes(msg, encoding=BufferEncoding))self.loginfo("发送成功")returndef close(self):self.__isrun = False# 释放log对象self.__logger.close()# 等待未完成的任务time.sleep(5)# 释放所有套接字self.__socket.close()for i in self.__connects:i[1].close()reader = open("C:\\0_data\\config.json", "r")
data = json.loads(reader.read())
reader.close()
log = Log(data["path"])
log.open()
server = SocketServer(log)
server.startlisten(data["port"])
try:while True:get = input()if "Close" in get:server.close()print("|INFO|\t已关闭所有进程")breakif "Send" in get:data = get.split("/")server.send(data[1], data[2])else:print("|INFO|\t键盘输出:", get)except Exception as e:server.close()finally:print("|INFO|\t程序已退出")
client.py
import _thread
import json
import socket
import timeBufferSize = 4096class SocketClient:def __init__(self):self.__socket = socket.socket()self.__isrun = Falseself.__index = -1def close(self):msg = {"code": 200,"sender": self.__index,"receiver": "host","action": "close","time": time.strftime("%Y/%m/%d %H:%M:%S", time.localtime()),"data": {}}self.__socket.send(bytes(json.dumps(msg), encoding="utf-8"))self.__isrun = Falsetime.sleep(5)self.__socket.close()def send(self, address, msg):if not self.__isrun: returnmsg = {"code": 200,"sender": self.__index,"receiver": address,"action": "msg","time": time.strftime("%Y/%m/%d %H:%M:%S", time.localtime()),"data": {"msg": msg}} # 构造返回字符串self.__socket.send(bytes(json.dumps(msg), encoding="utf-8"))print("发送成功")def checklive(self):if not self.__isrun: returnmsg = {"code": 200,"sender": self.__index,"receiver": "host","action": "live","time": time.strftime("%Y/%m/%d %H:%M:%S", time.localtime()),"data": {}} # 构造返回字符串self.__socket.send(bytes(json.dumps(msg), encoding="utf-8"))print("发送成功")def _recive(self):try:while self.__isrun:data = json.loads(str(self.__socket.recv(BufferSize), encoding="utf-8"))if data["action"] == "live":print("目前在线人数为", data["data"]["count"])for i in data["data"]["index"]:print(i)continuemsg = "[{}]{}:{}".format(data["time"], data["sender"], data["data"]["msg"])print(msg)# 退出消息print("已退出消息接收")except Exception as e:print("接收错误", str(e))def startconnect(self, ip, port):self.__isrun = Trueself.__socket.connect((ip, port))# 连接成功# 获取序列号data = json.loads(str(self.__socket.recv(BufferSize), encoding="utf-8"))self.__index = data["data"]["index"]print("连接成功,您的序列号为:", self.__index)# 开启消息监听线程_thread.start_new_thread(self._recive, ())client = SocketClient()
client.startconnect("127.0.0.1", 3414)while True:get = input()if get == "Close":client.close()breakif "Send" in get:data = get.split("/")client.send(data[1], data[2])if "Check" in get:client.checklive()print("程序已结束")
Python 最简单的我的第一个聊天室QQ软件【基于Socket服务】相关推荐
- 基于Python语言、RSA非对称加密的IRC聊天室客户端
源码地址: (55条消息) 基于Python语言.RSA非对称加密的IRC聊天室客户端源码与应用程序-Python文档类资源-CSDN文库 1 研究背景和现状 IRC是Internet Relay C ...
- android socket 简易聊天室 java服务器,Android Socket通信实现简单聊天室
socket通信是基于底层TCP/IP协议实现的.这种服务端不需要任何的配置文件和tomcat就可以完成服务端的发布,使用纯java代码实现通信.socket是对TCP/IP的封装调用,本身并不是一种 ...
- python实现一个简单的广域网内的GUI聊天室
这里写目录标题 1. 聊天室介绍 2. 开发环境 3. 服务端代码(chat_server.py)实现 4. 客户端代码(chat_client.py)实现 5. 测试 参考资料 1. 聊天室介绍 本 ...
- Python 技术篇 - 修改源码解决中文主机名导致的flask、socket服务起不来问题: ‘utf-8‘ codec can‘t decode byte 0xc0 in position...
由于主机名为中文导致的 flask 服务起不来,报错如下: File "D:\work\python3.9_64\lib\socket.py", line 791, in getf ...
- python打开qq并登录_Python脚本简单实现打开默认浏览器登录人人和打开QQ的方法...
本文实例讲述了Python脚本简单实现打开默认浏览器登录人人和打开QQ的方法.分享给大家供大家参考,具体如下: 每天打开电脑第一件事应该就是打开人人刷一下,然后登上QQ.每次都这样很麻烦,于是写了一个 ...
- python怎么打开qq_Python脚本简单实现打开默认浏览器登录人人和打开QQ的方法
本文实例讲述了Python脚本简单实现打开默认浏览器登录人人和打开QQ的方法.分享给大家供大家参考,具体如下: 每天打开电脑第一件事应该就是打开人人刷一下,然后登上QQ.每次都这样很麻烦,于是写了一个 ...
- python爬虫简单实例-Python 利用Python编写简单网络爬虫实例3
利用Python编写简单网络爬虫实例3 by:授客 QQ:1033553122 实验环境 python版本:3.3.5(2.7下报错 实验目的 获取目标网站"http://bbs.51tes ...
- python多人聊天室_python实现简单多人聊天室
本文实例为大家分享了python实现多人聊天室的具体代码,供大家参考,具体内容如下 刚开始学习python,写了一个聊天室练练手. Server.py import socket,select,thr ...
- python简单的多人聊天室
刚开始学习python,写了一个聊天室练练手. Server.py import socket,select,thread;host=socket.gethostname() port=5963 ad ...
最新文章
- 浅析JQuery中的html(),text(),val()区别
- Python Numba CPU下加速
- 软件需求说明书 概要设计说明书 项目开发计划 详细设计说明书 模版
- oracle(thin),kettle thin方式连接oracle,MySQL、SqlServer
- 运维人,你应该了解的三张武功心法图(转载)
- 一个 冒泡排序 和 选择排序 的简单c程序
- [解题报告]1005 - Number Sequence
- php 多态实现案例
- PLSQL连接Oracle报错 ORA-12154 标识符
- 万能险被保监会叫停 安全投资理财大旗它将接手
- C# Dictionary 使用;增加、移除的各种方法
- 移动硬盘不认盘还能数据恢复吗?
- 微信卡券开发具体的步骤,不会踩坑
- H3C网络故障排除方法
- layui use 定义js外部引用函数
- 雨水全自动浮动床过滤器
- 在移动端设置overflow:hidden禁止滚动的解决方法
- matlab位置跟踪仿真
- hadoop学习路线路线
- 1元钱买一瓶汽水,2个空瓶换一瓶汽水,3个瓶盖换一瓶汽水,问:3块钱能和多少瓶汽水?