基于 Socket 的群聊聊天室(带图形界面,包含注册、登录、数据入库功能)
代码下载
https://github.com/juno3550/GroupChatRoom
实现框架
Chat 包:
- server.py:服务器端执行代码(TCP 服务器,根据客户端消息调用 mode 包的注册、登录、聊天功能)
- client.py:客户端执行代码(连接服务器端,进行注册、登录、聊天)
- client_draw.py:客户端图形界面绘制
mode 包:
- chat_mode.py:封装服务器端的聊天逻辑
- login_mode.py:封装服务器端的登录逻辑
- register_mode.py:封装服务器端的注册逻辑
db 包:
- user_info_util.py:基于 mysql_util 查询或新增用户信息
- mysql_util.py:封装 mysql 基础操作
实现效果
示例:执行一个 server.py 与多个 client.py
代码实现
Chat 包
server.py
1 from twisted.internet.protocol import Factory
2 from twisted.protocols.basic import LineReceiver # 事件处理器
3 from twisted.internet import reactor
4 import json
5 from mode import chat_mode, login_mode, register_mode
6
7
8 # 每一个客户端连接都会对应一个不同的Chat对象
9 class Chat(LineReceiver):
10
11 def __init__(self, users):
12 # 存储所有连接用户信息的字典
13 self.users = users
14
15 # 断开连接时候自动触发,从users字典去掉连接对象
16 def connectionLost(self, reason):
17 if self in self.users.keys():
18 # print("%s断开连接" %self.users[self])
19 del self.users[self]
20
21 # 对客户端的请求内容做处理,只要收到客户端消息,自动触发此方法
22 def dataReceived(self, data):
23 # 将字节数据解码成字符串
24 data = data.decode('utf-8')
25 data_dict = json.loads(data)
26 # 根据type字段的值,进入对应的逻辑
27 # 登录逻辑
28 if data_dict["type"] == "login":
29 login_mode.login(self, data_dict)
30 # 注册逻辑
31 elif data_dict["type"] == "register":
32 register_mode.register(self, data_dict)
33 # 聊天逻辑
34 elif data_dict["type"] == "chat":
35 chat_mode.chat(self, data_dict)
36
37
38 # 处理业务的工厂类,只会实例化一次
39 class ChatFactory(Factory):
40
41 def __init__(self):
42 # 有多个连接的时候,会有多个chat对象
43 # self.users 在内存地址中,只有一份,所有连接对象都只使用同一个实例变量 self.users(等价于一个全局变量)
44 self.users = {}
45 # key: 连接对象本身;value:登录成功的用户昵称
46
47 # 一个客户端连接会实例化一个新的Chat对象
48 def buildProtocol(self, addr):
49 print(type(addr), addr)
50 # 返回一个处理具体业务请求的对象,参数传递了字典(存有所有连接对象)
51 return Chat(self.users)
52
53
54 if __name__ == '__main__':
55 # 设定监听端口和对象
56 # 使用Tcp协议,实例化ChatFactory
57 reactor.listenTCP(1200, ChatFactory())
58
59 print ("开始进入监听状态...")
60 reactor.run() # 开始监听
client.py
1 import tkinter
2 from tkinter import messagebox
3 import json
4 import time
5 import threading
6 import select
7 from socket import *
8 import traceback
9 from chat import client_draw
10
11
12 class Client:
13
14 # 配置连接
15 def connect(self):
16 # 创建socket
17 self.s = socket(AF_INET, SOCK_STREAM)
18 # 服务器端和客户端均在同个机器上运行
19 remote_host = gethostname()
20 # 设置端口号
21 port = 1200
22 # 发起连接
23 self.s.connect((remote_host, port))
24 print("从%s成功连接到%s" % (self.s.getsockname(), self.s.getpeername()))
25 return self.s
26
27 # 监听(接收)消息
28 def receive(self, s):
29 # 需要监控的对象列表
30 self.my = [s]
31 while 1:
32 print("监听中...")
33
34 # 实参:
35 # 第1个实参 self.my:可读的对象,监听服务器端的响应消息
36 # 第2个实参:可写的对象(本例不用)
37 # 第3个实参:出现异常的对象(本例不用)
38 # 这三个参数内容都是被操作系统监控的,即select.select()会执行系统内核代码
39 # 1)当有事件发生时,立马往下执行代码;否则阻塞监控10秒
40 # 2)若监控10秒了仍无事件发生,才往下执行
41 rl, wl, error = select.select(self.my, [], [], 10)
42 # 返回值:
43 # rl:监听某个文件描述符是否发生了读的事件(server给client发了数据)
44 # rl列表一开始为空,只有当s发生事件了(如客户端与服务器端建立了连接),才会将s加到rl中
45 # wl:监听某个文件描述符是否发生了写的事件(如client给server发了数据)
46 # error:监听某个文件描述符是否发生了异常事件
47 # 如果发生事件的对象是客户端连接对象,则代表收到服务器端数据
48 if s in rl:
49 try:
50 data = s.recv(1024).decode("utf-8")
51 data_dict = json.loads(data)
52 # 根据服务器端返回的type值,执行不同逻辑
53 type = data_dict["type"]
54 # 登录逻辑
55 if type == "login":
56 # 登录成功,跳转聊天页面
57 if "000" == data_dict["code"]:
58 nickname = data_dict["nickname"]
59 self.chat_interface(nickname)
60 # 登录失败,获取失败信息
61 else:
62 messagebox.showinfo(title="登录提示", message=data_dict["msg"])
63 # 注册逻辑
64 elif type == "register":
65 # 注册成功,跳转聊天页面
66 if "000" == data_dict["code"]:
67 nickname = data_dict["nickname"]
68 messagebox.showinfo(title="进入聊天室", message=data_dict["msg"])
69 self.chat_interface(nickname)
70 # 注册失败
71 else:
72 messagebox.showinfo(title="注册提示", message=data_dict["msg"])
73 # 聊天逻辑
74 elif type == "chat":
75 message = data_dict["message"]
76 nickname = data_dict["nickname"]
77 isMy = data_dict["isMy"]
78 chat_time = " " + nickname + "\t" + time.strftime("%Y/%m/%d %H:%M:%S", time.localtime()) + "\n"
79 # 聊天页面,显示发送人及发送时间
80 self.txtMsgList.insert(tkinter.END, chat_time, "DimGray")
81 # 如果是自己发的消息,字体使用'DarkTurquoise'
82 if "yes" == isMy:
83 self.txtMsgList.insert(tkinter.END, " " + message + "\n\n", 'DarkTurquoise')
84 # 如果是别人发的消息,字体使用'Black'
85 else:
86 self.txtMsgList.insert(tkinter.END, " " + message + "\n\n", 'Black')
87 # 插入消息时,自动滚动到底部
88 self.txtMsgList.see(tkinter.END)
89 except (ConnectionAbortedError, ConnectionResetError):
90 # 将连接对象从监听列表去掉
91 self.my.remove(s)
92 print("客户端发生连接异常,与服务器端断开连接")
93 traceback.print_exc()
94 s.close()
95 except Exception as e:
96 print("客户端发生了其它异常: ")
97 traceback.print_exc()
98 s.close()
99
100 # 进入注册页面
101 def register_interface(self):
102 client_draw.draw_register(self)
103
104 # 进入聊天页面
105 def chat_interface(self, nickname):
106 client_draw.draw_chat(self, nickname)
107
108 # 返回登录页面
109 def return_login_interface(self):
110 # 将不需要的控件先销毁
111 self.label_nickname.destroy()
112 self.input_nickname.destroy()
113 self.label_password.destroy()
114 self.input_password.destroy()
115 client_draw.draw_login(self)
116
117 # 获取输入框内容,进行注册验证
118 def verify_register(self):
119 username = self.input_account.get()
120 password = self.input_password.get()
121 nickname = self.input_nickname.get()
122 try:
123 register_data = {}
124 register_data["type"] = "register"
125 register_data["username"] = username
126 register_data["password"] = password
127 register_data["nickname"] = nickname
128 # 将dict类型转为json字符串,便于网络传输
129 data = json.dumps(register_data)
130 self.s.send(data.encode("utf-8"))
131 except:
132 traceback.print_exc()
133
134 # 获取输入框内容,进行登录校验
135 def verify_login(self):
136 account = self.input_account.get()
137 password = self.input_password.get()
138 try:
139 login_data = {}
140 login_data["type"] = "login"
141 login_data["username"] = account
142 login_data["password"] = password
143 data = json.dumps(login_data)
144 self.s.send(data.encode('utf-8'))
145 except:
146 traceback.print_exc()
147
148 # 获取输入框内容,发送消息
149 def send_msg(self):
150 message = self.txtMsg.get('0.0', tkinter.END).strip()
151 if not message:
152 messagebox.showinfo(title='发送提示', message="发送内容不能为空,请重新输入")
153 return
154 self.txtMsg.delete('0.0', tkinter.END)
155 try:
156 chat_data = {}
157 chat_data["type"] = "chat"
158 chat_data["message"] = message
159 data = json.dumps(chat_data)
160 self.s.send(data.encode('utf-8'))
161 except:
162 traceback.print_exc()
163
164 # 发送消息事件
165 def send_msg_event(self, event):
166 # 如果捕捉到键盘的回车按键,触发消息发送
167 if event.keysym == 'Return':
168 self.send_msg()
169
170 # 聊天页面,点击右上角退出时执行
171 def on_closing(self):
172 if messagebox.askokcancel("退出提示", "是否离开聊天室?"):
173 self.window.destroy()
174
175
176 def main():
177 chatRoom = Client()
178 client = chatRoom.connect()
179 t = threading.Thread(target=chatRoom.receive, args=(client,)) # 创建一个线程,监听消息
180 t.start()
181 # 创建主窗口,用于容纳其它组件
182 chatRoom.window = tkinter.Tk()
183 # 登录界面控件创建、布局
184 client_draw.draw_login(chatRoom)
185 # 进入事件(消息)循环
186 tkinter.mainloop()
187
188 if __name__ == "__main__":
189 main()
client_draw.py
1 import tkinter
2
3
4 # 登录页面
5 def draw_login(self):
6 # 设置主窗口标题
7 self.window.title("聊天室登录界面")
8 # 设置主窗口大小
9 self.window.geometry("450x300")
10 # 创建画布
11 self.canvas = tkinter.Canvas(self.window, height=200, width=500)
12 # 创建一个`Label`名为`账 号: `
13 self.label_account = tkinter.Label(self.window, text='账 号')
14 # 创建一个`Label`名为`密 码: `
15 self.label_password = tkinter.Label(self.window, text='密 码')
16 # 创建一个账号输入框,并设置尺寸
17 self.input_account = tkinter.Entry(self.window, width=30)
18 # 创建一个密码输入框,并设置尺寸
19 self.input_password = tkinter.Entry(self.window, show='*', width=30)
20 # 登录按钮
21 self.login_button = tkinter.Button(self.window, command=self.verify_login, text="登 录", width=10)
22 # 注册按钮
23 self.register_button = tkinter.Button(self.window, command=self.register_interface, text="注 册", width=10)
24
25 # 登录页面各个控件进行布局
26 self.label_account.place(x=90, y=70)
27 self.label_password.place(x=90, y=150)
28 self.input_account.place(x=135, y=70)
29 self.input_password.place(x=135, y=150)
30 self.login_button.place(x=120, y=235)
31 self.register_button.place(x=250, y=235)
32
33
34 # 注册界面
35 def draw_register(self):
36 # 登录按钮销毁
37 self.login_button.destroy()
38 # 注册按钮销毁
39 self.register_button.destroy()
40 self.window.title("聊天室注册界面")
41 self.window.geometry("450x300")
42 # 创建画布
43 self.canvas = tkinter.Canvas(self.window, height=200, width=500)
44 # 创建一个"Label",名为:"昵 称"
45 self.label_nickname = tkinter.Label(self.window, text='昵 称')
46 # 创建一个昵称输入框,并设置尺寸
47 self.input_nickname = tkinter.Entry(self.window, width=30)
48 # 创建注册按钮
49 self.register_submit_button = tkinter.Button(self.window, command=self.verify_register, text="提交注册", width=10)
50 # 创建注册按钮
51 self.return_login_button = tkinter.Button(self.window, command=self.return_login_interface, text="返回登录",width=10)
52
53 # 注册界面各个控件进行布局
54 self.label_account.place(x=90, y=70)
55 self.label_password.place(x=90, y=130)
56 self.input_account.place(x=135, y=70)
57 self.input_password.place(x=135, y=130)
58 self.label_nickname.place(x=90, y=190)
59 self.input_nickname.place(x=135, y=190)
60 self.register_submit_button.place(x=120, y=235)
61 self.return_login_button.place(x=250, y=235)
62
63
64 # 聊天室界面
65 def draw_chat(self, nickname):
66 self.window.title("【%s】的聊天室界面" % nickname)
67 self.window.geometry("520x560")
68 # 创建frame容器
69 # 放置聊天记录
70 self.frmLT = tkinter.Frame(width=500, height=320)
71 # 放置发送内容输入框
72 self.frmLC = tkinter.Frame(width=500, height=150)
73 # 放置发送按钮
74 self.frmLB = tkinter.Frame(width=500, height=30)
75
76 self.txtMsgList = tkinter.Text(self.frmLT)
77 # 设置消息时间字体样式
78 self.txtMsgList.tag_config('DimGray', foreground='#696969', font=("Times", "11"))
79 # 设置自己的消息字体样式
80 self.txtMsgList.tag_config('DarkTurquoise', foreground='#00CED1', font=("Message", "13"), spacing2=5)
81 # 设置其它人的消息字体样式
82 self.txtMsgList.tag_config('Black', foreground='#000000', font=("Message", "13"), spacing2=5)
83
84 self.txtMsg = tkinter.Text(self.frmLC)
85 # 触发键盘的回车按键事件,发送消息
86 self.txtMsg.bind("<KeyPress-Return>", self.send_msg_event)
87 self.btnSend = tkinter.Button(self.frmLB, text='发送', width=12, command=self.send_msg)
88 # 创建空的Label在左边占个位置,便于发送按钮靠右
89 self.labSend = tkinter.Label(self.frmLB, width=55)
90
91 # 窗口布局
92 self.frmLT.grid(row=0, column=0, columnspan=2, padx=10, pady=10)
93 self.frmLC.grid(row=1, column=0, columnspan=2, padx=10, pady=10)
94 self.frmLB.grid(row=2, column=0, columnspan=2, padx=10, pady=10)
95
96 # 固定大小
97 self.frmLT.grid_propagate(0)
98 self.frmLC.grid_propagate(0)
99 self.frmLB.grid_propagate(0)
100
101 self.labSend.grid(row=0, column=0)
102 # 发送按钮布局
103 self.btnSend.grid(row=0, column=1)
104 self.txtMsgList.grid()
105 self.txtMsg.grid()
106
107 # WM_DELETE_WINDOW 不能改变,这是捕获命令
108 self.window.protocol('WM_DELETE_WINDOW', self.on_closing)
mode 包
chat_mode.py
1 import json
2
3
4 # 聊天逻辑
5 def chat(self, data_dict):
6 """
7 :param self: 连接对象
8 :param data_dict: 客户端的请求消息
9 """
10 message = data_dict["message"].strip()
11 # 遍历所有的连接对象,群发消息
12 for user in self.users.keys():
13 data = {}
14 data["type"] = "chat"
15 # 获取当前发送消息客户端的昵称
16 nickname = self.users[self]
17 data["nickname"] = nickname
18 # "isMy"键默认为no
19 data["isMy"] = "no"
20 # 如果遍历的对象与发消息客户端是同一个,则将isMy字段设为yes, 便于前端用来判断展示不同的字体样式
21 if user == self:
22 data["isMy"] = "yes"
23 data["message"] = message
24 data = json.dumps(data)
25 user.sendLine(data.encode("utf-8"))
login_mode.py
1 from db.user_info_util import user_util
2 import json
3
4
5 # 登录逻辑
6 def login(self, data_dict):
7 """
8 :param self: 连接对象
9 :param data_dict: 客户端的请求消息
10 """
11 username = data_dict["username"].strip()
12 password = data_dict["password"].strip()
13 # 服务器端的响应消息
14 data = {}
15 # 账号密码不能为空
16 if username and password:
17 code, msg, nickname = login_check(username, password)
18 elif not username:
19 code = "003"
20 msg = "登录用户名不能为空"
21 elif not password:
22 code = "004"
23 msg = "登录密码不能为空"
24 # 登录成功,将连接对象以及昵称加到users中,便于后续遍历发送消息
25 if code == "000":
26 # 在全局变量users中新增用户信息
27 self.users[self] = nickname
28 data["nickname"] = nickname
29 data["type"] = "login"
30 data["code"] = code
31 data["msg"] = msg
32 data = json.dumps(data)
33 self.sendLine(data.encode("utf-8"))
34
35
36 # 登录校验逻辑
37 def login_check(username, password):
38 # 通过用户名到数据库获取用户信息
39 user_info = user_util.user_check(username)
40 # 未查到该用户信息,代表未注册
41 if len(user_info) == 0:
42 data = ("001", "账号【%s】未注册,请先进行注册!" % username, None)
43 # 密码错误
44 elif password != user_info[0][1]:
45 data = ("002", "密码有误,请重新输入!", None)
46 # 正常登录
47 else:
48 # 获取昵称
49 nickname = user_info[0][2]
50 data = ("000", "账号【%s】登录成功!" % username, nickname)
51 return data
register_mode.py
1 from db.user_info_util import user_util
2 import json
3
4
5 # 注册逻辑
6 def register(self, data_dict):
7 """
8 :param self: 连接对象
9 :param data_dict: 客户端的请求消息
10 """
11 username = data_dict["username"].strip()
12 password = data_dict["password"].strip()
13 nickname = data_dict["nickname"].strip()
14 # 服务器端的响应消息
15 data = {}
16 # 三者均不为空才能走注册校验
17 if username and password and nickname:
18 code, msg = register_check(username, password, nickname)
19 elif not username:
20 code = "002"
21 msg = "注册账号不能为空"
22 elif not password:
23 code = "003"
24 msg = "注册密码不能为空"
25 elif not nickname:
26 code = "004"
27 msg = "注册昵称不能为空"
28 if code == "000":
29 self.users[self] = nickname
30 data["nickname"] = nickname
31
32 data["type"] = "register"
33 data["code"] = code
34 data["msg"] = msg
35 data = json.dumps(data)
36 self.sendLine(data.encode("utf-8"))
37
38
39 # 注册校验
40 def register_check(username, password, nickname):
41 user_info = user_util.user_check(username)
42 if len(user_info) > 0:
43 data = ("001", "账号【%s】已被注册过" % user_info)
44 else:
45 user_util.user_insert(username, password, nickname)
46 data = ("000", "账号【%s】注册成功,点击'确定'进入聊天页面" % username)
47 return data
db 包
user_info_util.py
1 from db import mysql_util
2
3 class UserUtil:
4
5 def __init__(self, host, port, db, user, passwd, charset="utf8"):
6 self.mysql = mysql_util.MysqlTool(host, port, db, user, passwd, charset)
7
8 def user_check(self, username):
9 check_sql = "SELECT username,password,nickname FROM user WHERE username = '%s'" % username
10 self.mysql.connect()
11 user_info = self.mysql.get_all(check_sql)
12 self.mysql.close()
13 return user_info
14
15 def user_insert(self, username, passwd, nickname):
16 insert_sql = "INSERT INTO user(username,password,nickname) VALUES('%s','%s','%s')" % (username, passwd, nickname)
17 self.mysql.connect()
18 self.mysql.insert(insert_sql)
19 self.mysql.close()
20
21 user_util = UserUtil("localhost", 3306, "test", "root", "admin")
22
23 if __name__ == "__main__":
24 print(user_util.user_check("username_test"))
25 user_util.user_insert("username_test2", "pwd", "nickname")
26 print(user_util.user_check("username_test2"))
mysql_util.py
1 import pymysql
2
3
4 class MysqlTool:
5
6 def __init__(self, host, port, db, user, passwd, charset="utf8"):
7 self.host = host
8 self.port = port
9 self.db = db
10 self.user = user
11 self.passwd = passwd
12 self.charset = charset
13
14 # 创建数据库连接与执行对象
15 def connect(self):
16 try:
17 self.conn = pymysql.connect(host=self.host, port=self.port,
18 db=self.db, user=self.user, passwd=self.passwd, charset=self.charset)
19 self.cursor = self.conn.cursor()
20 except Exception as e:
21 print(e)
22
23 # 关闭数据库连接与执行对象
24 def close(self):
25 try:
26 self.cursor.close()
27 self.conn.close()
28 except Exception as e:
29 print(e)
30
31 # 获取一行数据
32 def get_one(self, sql):
33 try:
34 self.cursor.execute(sql)
35 result = self.cursor.fetchone()
36 except Exception as e:
37 print(e)
38 else:
39 return result
40
41 # 获取全部行的数据
42 def get_all(self, sql):
43 try:
44 self.cursor.execute(sql)
45 result = self.cursor.fetchall()
46 except Exception as e:
47 print(e)
48 else:
49 return result
50
51 # 增删改查的私有方法
52 def __edit(self, sql):
53 try:
54 execute_count = self.cursor.execute(sql)
55 self.conn.commit()
56 except Exception as e:
57 print(e)
58 else:
59 return execute_count
60
61 # 插入数据
62 def insert(self, sql):
63 return self.__edit(sql)
64
65 # 删除数据
66 def delete(self, sql):
67 return self.__edit(sql)
68
69
70 if __name__ == "__main__":
71 mysql = MysqlTool("localhost", 3306, "test", "root", "admin")
72 mysql.connect()
73 mysql.insert('insert into user(username, password, nickname) values("username_test1", "pwd_test1", "nick_test1");')
74 print(mysql.get_all("select * from user;"))
75 mysql.delete('delete from user where username="%s"' % "username_test1")
76 print(mysql.get_all("select * from user;"))
基于 Socket 的群聊聊天室(带图形界面,包含注册、登录、数据入库功能)的更多相关文章
- Java Socket聊天室编程(二)之利用socket实现单聊聊天室
这篇文章主要介绍了Java Socket聊天室编程(二)之利用socket实现单聊聊天室的相关资料,非常不错,具有参考借鉴价值,需要的朋友可以参考下 在上篇文章Java Socket聊天室编程(一)之 ...
- Java图形界面学习---------简易登录界面
/** * @author Administrator * Java图形界面学习---------简易登录界面 * date:2015/10/31 */ import java.awt.BorderL ...
- C#基于Socket的简单聊天室实践
序:实现一个基于Socket的简易的聊天室,实现的思路如下: 程序的结构:多个客户端+一个服务端,客户端都是向服务端发送消息,然后服务端转发给所有的客户端,这样形成一个简单的聊天室功能. 实现的细节: ...
- memcache的带图形界面监控工具memcachephp
memcache也有一款图形界面的监控工具(memcachephp),可以通过这个工具查看到局域网内所有部署memcache机器或者端口的memcache的运行情况,对我们监控memcache的缓存命 ...
- Java图形界面开发—简易登录注册小程序
登录注册小代码,将学过的一些小知识融合在一起进行了使用,加深印象.本例中如果有注释不详细的地方,详见其它博客. Java程序操作数据库SQLserver详解 功能介绍:简单的登录注册系统,使用了数据库 ...
- Linux强大屏幕截图方法,理论能截取任何图形界面,包括登录界面
众所周知,屏幕截图可以使用“Print Screen”按键,但是,此按键的响应是靠系统的后台服务实现的,Linux在某些场景下,是不响应此按键的. 这里介绍一种更强大的截图方法,它是靠转储X图形环境的 ...
- 【C++】基于socket的多线程聊天室(控制台版)
以前学习socket网络编程和多线程编程的时候写的一个练手程序 聊天室基本功能: 1.用户管理:登录,注册,登出,修改用户名,修改密码 2.聊天室功能:群聊,私聊,获取在线用户列表,获取所有用户列表 ...
- Android基于socket的群聊程序
在网上看了好多,但是感觉不是太简单就是只能单独聊,所以就自己写了个可以群聊的,直接上代码了 一.服务器端 这里用的MyEclipse作为服务器端 MyServerScoket.java package ...
- java带图形界面的五子棋
Main: package BlackWhite; import java.awt.BorderLayout; import java.awt.Color; import java.awt.FlowL ...
随机推荐
- JS中indexOf的用法
String.IndexOf(Char, [startIndex], [count]):返回指定字符在原字符串中的第一个匹配项的索引.可指定字符开始检索位置和指定长度的字符,若没有找到该字符,则返回 ...
- SpringCloud之服务配置
1.config 1.1定义 对于分布式微服务,有很多的配置,那么修改起来很麻烦.这就需要对这些配置文件进行集中式的管理,config的功能就是用来统一管理配置文件的.它为微服务提供集中化的外部配置支 ...
- HDOJ-1540(线段树+较复杂的单点修改和区间查询)
Tunnel Warfare HDOJ-1540 这题关于线段树的操作有一定的难度,需要较好的思维能力. 关于题目的详细解答已经在代码中体现了. #include<iostream> #i ...
- springboot源码解析-管中窥豹系列之EnableXXX(十)
一.前言 Springboot源码解析是一件大工程,逐行逐句的去研究代码,会很枯燥,也不容易坚持下去. 我们不追求大而全,而是试着每次去研究一个小知识点,最终聚沙成塔,这就是我们的springboot ...
- [个人总结]pip安装tensorboard太慢
在执行pip install语句的时候直接指定国内豆瓣的镜像源进行下载: pip install -i https://pypi.douban.com/simple 你想下载的包的名称 例如下载ten ...
- C#连接Excel读取与写入数据库SQL ( 下 )
接上期 dataset简而言之可以理解为 虚拟的 数据库或是Excel文件.而dataset里的datatable 可以理解为数据库中的table活着Excel里的sheet(Excel里面不是可以新 ...
- 2020年12月-第02阶段-前端基础-CSS基础选择器
CSS选择器(重点) 理解 能说出选择器的作用 id选择器和类选择器的区别 1. CSS选择器作用(重点) 如上图所以,要把里面的小黄人分为2组,最快的方法怎办? 很多, 比如 一只眼睛的一组,剩下的 ...
- JAVA中枚举Enum详解
1.关键字:enum.枚举可以定义成单独的文件,也可以定义在其他类内部. 枚举在类内部的示例: public class EnumInner { public static void main(Str ...
- Fastjson <=1.2.24-反序列化-任意命令执行
漏洞分析 https://www.secpulse.com/archives/72391.html 复现参考 https://www.cnblogs.com/hack404/p/11980791.ht ...
- Java基础:特性write once;run anywhere!
三高:高可用 高性能 高并发 特性: 简单性 面向对象:万物皆为对象 可移植性 高性能 分布式 动态性 多线程 安全性 健壮性 Java三大版本 javaSE:标准版(桌面程序,控制台) javaME ...