Python+Socket实现多人聊天室,功能:好友聊天、群聊、图片、表情、文件等

一、项目简介

本项目主要基于python实现的多人聊天室,主要的功能如下:

  • 登录注册
  • 添加好友
  • 与好友进行私聊
  • 创建群聊
  • 邀请/申请加入群聊
  • 聊天发送图片
  • 聊天发送表情
  • 聊天发送文件
  • 聊天记录保存在本地中
  • 聊天过程中发送的文件保存本地

二、环境介绍

  • python3.8
  • mysql8.0
  • tkinter:作为程序的gui库
  • flask :主要用于登录/注册、表情下载、信息修改等http请求等
  • socket:主要用户聊天过程中消息发送、对方在线状态更新等
  • pygame:用于播放新消息提示音

三、运行展示

登录:
登录页面
注册:
image-20220427221842887
登录后主界面:
image-20220427221858349
点击右上方“修改资料”:
image-20220427221942224
添加好友或群:
image-20220427222002416
双击好友或群打开聊天窗口:
image-20220427222032094
点击表情按钮选择发送的表情:
image-20220427222102584
发送图片可以预览,点击文件名称直接打开:
image-20220427222217499

四、关键代码

配置文件:server.conf

配置服务器ip、http端口、socket端口、数据库的账号密码、是否启用新消息提示音

[server]
SERVER_IP = 127.0.0.1
HTTP_PORT = 8000
SOCKET_PORT = 8001
SQLALCHEMY_DATABASE_URI = mysql://root:root@127.0.0.1:3306/chatdb
ENABLE_MUSIC = 0

服务端主要代码:ChatServer.py

维持Socket通信、开启Flask进行http

# controller定义
@app.route('/login', methods=['POST'])
def login():try:params = request.valueslogin_name = params['loginName']pwd = params['pwd']md5 = hashlib.md5()md5.update(pwd.encode(encoding='utf-8'))password = md5.hexdigest()users = Users.query.filter(Users.loginName == login_name)\.filter(Users.pwd == password).all()if len(users) == 0:return Result.fail('账号不存在或密码错误')else:# 服务返回uid,客户端打开好友界面后,凭借此uid与服务器进行socket连接uid = users[0].id# 已存在uid:已登录,重新登录,原登录退出连接,退出程序if uid in online_users.keys():# logoutconnection = online_users[int(uid)]send_msg = {'type': UtilsAndConfig.SYSTEM_LOGOUT}connection.send(json.dumps(send_msg).encode())online_users[uid] = Nonereturn Result.success(uid)except Exception as e:return Result.fail('参数异常')# 监听socket
def socket_listen_thread():while True:connection, address = mySocket.accept()# 用户连接携带的uid,判断是否和服务器相同data_dic = json.loads(connection.recv(1024).decode())uid = Noneif data_dic['type'] == UtilsAndConfig.CONNECTION_REQUEST:uid = int(data_dic['uid'])else:connection.send(UtilsAndConfig.CONNECTION_NOT_ALLOWED.encode())if uid in online_users.keys():# 可建立连接online_users[uid] = connectionconnection.send(UtilsAndConfig.CONNECTION_ALLOWED.encode())# 通知好友们,我上线了friends = get_friends_by_uid(uid)for f in friends:if f.id in online_users.keys():friend_connection = online_users[f.id]send_msg = {'type': UtilsAndConfig.FRIENDS_ONLINE_CHANGED, 'uid': uid, 'online': 1}friend_connection.send(json.dumps(send_msg).encode())# 创建子线程,保持通信keep_link_thread = threading.Thread(target=socket_keep_link_thread, args=(connection, ))keep_link_thread.setDaemon(True)keep_link_thread.start()else:connection.send(UtilsAndConfig.CONNECTION_NOT_ALLOWED.encode())def socket_keep_link_thread(connection):while True:try:msg = connection.recv(1024).decode()if not msg:if connection in online_users.values():uid = list(online_users.keys())[list(online_users.values()).index(connection)]online_users.pop(uid)friends = get_friends_by_uid(uid)for f in friends:if f.id in online_users.keys():friend_connection = online_users[f.id]send_msg = {'type': UtilsAndConfig.FRIENDS_ONLINE_CHANGED, 'uid': uid, 'online': 0}friend_connection.send(json.dumps(send_msg).encode())connection.close()returnelse:msg_json = json.loads(str(msg))# 发消息if msg_json['type'] == UtilsAndConfig.CHAT_SEND_MSG:to_id = msg_json['toId']is_friend = msg_json['isFriend']from_uid = msg_json['fromId']send_time = msg_json['sendTime']msg_text = msg_json['msgText']data = {'from_uid': from_uid, 'to_id': to_id, 'send_time': send_time, 'msg_text': msg_text,'is_friend': is_friend, 'type': '', 'msg_type': 'train'}# 通知接收方,收到新消息if is_friend == 1:if to_id in online_users.keys():friend_connection = online_users[to_id]data['type'] = UtilsAndConfig.CHAT_HAS_NEW_MSGfriend_connection.send(json.dumps(data).encode())# 通知发送方,发送成功data['type'] = UtilsAndConfig.CHAT_SEND_MSG_SUCCESSconnection.send(json.dumps(data).encode())else:# 通知发送方,发送失败,对方不在线data['type'] = UtilsAndConfig.CHAT_SEND_MSG_ERRconnection.send(json.dumps(data).encode())else:# 群members = get_group_members(to_id)members_online = Falsefor m in members:if m.uId in online_users.keys() and m.uId != from_uid:members_online = Truemember_connection = online_users[m.uId]data['type'] = UtilsAndConfig.CHAT_HAS_NEW_MSGmember_connection.send(json.dumps(data).encode())if members_online:# 通知发送方,发送成功data['type'] = UtilsAndConfig.CHAT_SEND_MSG_SUCCESSconnection.send(json.dumps(data).encode())else:# 通知发送方,发送失败,对方不在线data['type'] = UtilsAndConfig.CHAT_SEND_MSG_ERRconnection.send(json.dumps(data).encode())if msg_json['type'] == UtilsAndConfig.CHAT_SEND_FILE:from_id = msg_json['from_id']to_id = msg_json['to_id']is_friend = msg_json['is_friend']send_date = msg_json['send_date']file_length = msg_json['file_length']file_suffix = msg_json['file_suffix']file_name = msg_json['file_name']file_save_name = str(uuid.uuid1()) + '.' + file_suffixreturn_file_path = '/static/tmp/' + file_save_namefile_path = os.path.abspath(os.path.dirname(__file__)) + return_file_pathif not os.path.exists(os.path.dirname(file_path)):os.makedirs(os.path.dirname(file_path))data = {'from_uid': from_id, 'to_id': to_id, 'send_time': send_date, 'file_name': file_name,'is_friend': is_friend, 'type': UtilsAndConfig.CHAT_SEND_FILE_SUCCESS,'file_path': return_file_path}if is_friend == 1:if to_id not in online_users.keys():# 通知发送方,发送失败,对方不在线data['type'] = UtilsAndConfig.CHAT_SEND_MSG_ERRconnection.send(json.dumps(data).encode())continueelse:members = get_group_members(to_id)flag = Truefor m in members:if m.uId in online_users.keys() and m.uId != from_id:flag = Falsebreakif flag:# 通知发送方,发送失败,对方不在线data['type'] = UtilsAndConfig.CHAT_SEND_MSG_ERRconnection.send(json.dumps(data).encode())continue# 接收文件total_data = b''file_data = connection.recv(1024)total_data += file_datanum = len(file_data)while num < file_length:file_data = connection.recv(1024)num += len(file_data)total_data += file_datawith open(file_path, "wb") as f:f.write(total_data)connection.send(json.dumps(data).encode())# 通知接收方,收到新文件消息if is_friend == 1:friend_connection = online_users[to_id]data['type'] = UtilsAndConfig.CHAT_HAS_NEW_FILEfriend_connection.send(json.dumps(data).encode())else:members = get_group_members(to_id)for m in members:if m.uId in online_users.keys() and m.uId != from_id:member_connection = online_users[m.uId]data['type'] = UtilsAndConfig.CHAT_HAS_NEW_FILEmember_connection.send(json.dumps(data).encode())except ConnectionAbortedError:if connection in online_users.values():uid = list(online_users.keys())[list(online_users.values()).index(connection)]online_users.pop(uid)friends = get_friends_by_uid(uid)for f in friends:if f.id in online_users.keys():friend_connection = online_users[f.id]send_msg = {'type': UtilsAndConfig.FRIENDS_ONLINE_CHANGED, 'uid': uid, 'online': 0}friend_connection.send(json.dumps(send_msg).encode())connection.close()returnexcept ConnectionResetError:if connection in online_users.values():uid = list(online_users.keys())[list(online_users.values()).index(connection)]online_users.pop(uid)friends = get_friends_by_uid(uid)for f in friends:if f.id in online_users.keys():friend_connection = online_users[f.id]send_msg = {'type': UtilsAndConfig.FRIENDS_ONLINE_CHANGED, 'uid': uid, 'online': 0}friend_connection.send(json.dumps(send_msg).encode())connection.close()return# 主线程
if __name__ == '__main__':# 启动socket线程socketThread = threading.Thread(target=socket_listen_thread)socketThread.setDaemon(True)socketThread.start()# 启动Flask服务器app.run(host=serverConfig.SERVER_IP, port=serverConfig.HTTP_PORT, debug=False)

客户端主界面:ChatHome.py

与服务器保持Socket通信、与服务端进行http交互

class ChatHome:def run(self):pygame.mixer.init()# Socket连接self.socket.connect((self.server_config.SERVER_IP, self.server_config.SOCKET_PORT))send_data = {'type': UtilsAndConfig.CONNECTION_REQUEST, 'uid': self.uid}self.socket.send(json.dumps(send_data).encode())socket_result = self.socket.recv(1024).decode()if socket_result != UtilsAndConfig.CONNECTION_ALLOWED:tkinter.messagebox.showwarning('提示', '参数出错,socket连接被拒绝!')sys.exit()# 创建子线程保持socket通信keep_link_thread = threading.Thread(target=self.socket_keep_link_thread)keep_link_thread.setDaemon(True)keep_link_thread.start()# 基本信息self.root = tk.Tk()self.root.title('ChatRoom')self.root.geometry('320x510+100+0')# 用户名self.frame_user_info = Frame(self.root, relief=RAISED, width=320, borderwidth=0, height=70, bg='#4F7DA4')self.frame_user_info.place(x=0, y=0)self.init_user_info()# 中间画布canvasself.frame_mid = Frame(self.root, width=320, height=340)self.frame_mid.place(x=0, y=70)# # 画布中的frameself.init_friends_and_group_view()# 下方按钮frame_bottom_button = Frame(self.root, relief=RAISED, borderwidth=0, width=320, height=50)frame_bottom_button.place(x=0, y=420)button_bottom_add_friends = Button(frame_bottom_button, width=11,text='加好友/加群', command=self.open_add_friends)button_bottom_add_friends.place(x=55, y=10)button_bottom_create_groups = Button(frame_bottom_button, width=11,text='创建群', command=self.open_create_groups)button_bottom_create_groups.place(x=165, y=10)# 新消息frame_message = Frame(self.root, relief=RAISED, borderwidth=0, width=320, height=50)frame_message.place(x=0, y=460)self.label_message_tip = Label(frame_message)self.label_message_tip.place(x=55, y=12)self.refresh_message_count()button_message_open = Button(frame_message, width=7,text='查看', command=self.open_message_window)button_message_open.place(x=193, y=10)self.root.mainloop()# 保持socket通信def socket_keep_link_thread(self):while True:try:back_msg = self.socket.recv(1024).decode()msg = json.loads(back_msg)# 好友状态改变if msg['type'] == UtilsAndConfig.FRIENDS_ONLINE_CHANGED:self.frames_friend_view[msg['uid']].online_type_change(msg['online'])# 有新验证消息if msg['type'] == UtilsAndConfig.MESSAGE_NEW_MSG:self.refresh_message_count()self.play_new_msg_music()# 好友/群数量改变if msg['type'] == UtilsAndConfig.FRIENDS_GROUPS_COUNT_CHANGED:self.init_friends_and_group_view()self.refresh_message_count()# 有新文本消息, 写入缓存,更新显示if msg['type'] == UtilsAndConfig.CHAT_HAS_NEW_MSG:from_uid = msg['from_uid']to_id = msg['to_id']is_friend = msg['is_friend']txt = {'type': 'get', 'from_uid': from_uid, 'datetime': msg['send_time'],'msg': msg['msg_text'], 'msg_type': 'train'}UtilsAndConfig.add_one_chat_record(self.uid, is_friend, from_uid, to_id,json.dumps(txt, cls=UtilsAndConfig.MyJSONEncoder,ensure_ascii=False), False)# 是否打开聊天界面,打开则更新,未打开则好友列表提示新消息if self.window_chat_context is not None and self.window_chat_context.to_id == from_uid\and self.window_chat_context.is_friend == 1 and is_friend == 1:self.window_chat_context.get_new_msg()passelif self.window_chat_context is not None and self.window_chat_context.to_id == to_id\and self.window_chat_context.is_friend == 0 and is_friend == 0:self.window_chat_context.get_new_msg()else:if is_friend == 1:self.frames_friend_view[from_uid].new_msg_comming()else:self.frames_group_view[to_id].new_msg_comming()self.play_new_msg_music()# 发送文本消息成功, 写入本地缓存,更新显示if msg['type'] == UtilsAndConfig.CHAT_SEND_MSG_SUCCESS:from_uid = msg['from_uid']to_id = msg['to_id']send_time = msg['send_time']msg_text = msg['msg_text']is_friend = msg['is_friend']txt = {'type': 'send', 'datetime': send_time, 'msg': msg_text, 'msg_type': 'train'}UtilsAndConfig.add_one_chat_record(self.uid, is_friend, from_uid, to_id,json.dumps(txt, cls=UtilsAndConfig.MyJSONEncoder,ensure_ascii=False), True)self.window_chat_context.get_new_msg()# 发送文件成功if msg['type'] == UtilsAndConfig.CHAT_SEND_FILE_SUCCESS:to_id = msg['to_id']send_time = msg['send_time']file_name = msg['file_name']is_friend = msg['is_friend']txt = {'type': 'send', 'datetime': send_time, 'msg': file_name, 'msg_type': 'file'}UtilsAndConfig.add_one_chat_record(self.uid, is_friend, self.uid, to_id,json.dumps(txt, cls=UtilsAndConfig.MyJSONEncoder,ensure_ascii=False), True)self.window_chat_context.get_new_msg()self.window_chat_context.sending_file(False)# 收到文件if msg['type'] == UtilsAndConfig.CHAT_HAS_NEW_FILE:to_id = msg['to_id']from_uid = msg['from_uid']send_time = msg['send_time']file_name = msg['file_name']is_friend = msg['is_friend']file_path = msg['file_path']files_dir = os.path.abspath(os.path.dirname(__file__)) + '/static/LocalCache/' \+ str(self.uid) + '/files/'if not os.path.exists(os.path.dirname(files_dir)):os.makedirs(os.path.dirname(files_dir))all_file_name = file_name.split('/')[-1]file_suffix = all_file_name.split('.')[-1]end_index = len(all_file_name) - len(file_suffix) - 1file_name = all_file_name[0:end_index]file_save_path = files_dir + file_name + '.' + file_suffixi = 1while os.path.exists(file_save_path):file_save_path = files_dir + file_name + '(' + str(i) + ')' + '.' + file_suffixi += 1# http下载文件,保存到本地try:url = self.server_config.HTTP_SERVER_ADDRESS + file_pathres = requests.get(url)file_content = res.contentfile = open(file_save_path, 'wb')file.write(file_content)file.close()except requests.exceptions.InvalidSchema:pass# 服务器中文件不存在txt = {'type': 'get', 'from_uid': from_uid, 'datetime': send_time,'msg': file_save_path, 'msg_type': 'file'}UtilsAndConfig.add_one_chat_record(self.uid, is_friend, from_uid, to_id,json.dumps(txt, cls=UtilsAndConfig.MyJSONEncoder,ensure_ascii=False), False)if self.window_chat_context is not None and self.window_chat_context.to_id == from_uid\and self.window_chat_context.is_friend == 1 and is_friend == 1:self.window_chat_context.get_new_msg()passelif self.window_chat_context is not None and self.window_chat_context.to_id == to_id\and self.window_chat_context.is_friend == 0 and is_friend == 0:self.window_chat_context.get_new_msg()else:if is_friend == 1:self.frames_friend_view[from_uid].new_msg_comming()else:self.frames_group_view[to_id].new_msg_comming()self.play_new_msg_music()# 告诉服务器 文件下载完成,可删除url = self.server_config.HTTP_SERVER_ADDRESS + '/downloadFileSuccess?path=' + file_pathrequests.get(url)# 发送聊天消息失败,不写入缓存,提示对方已下线if msg['type'] == UtilsAndConfig.CHAT_SEND_MSG_ERR:tkinter.messagebox.showwarning('提示', '对方已下线,不能发送消息')# 服务器强制下线if msg['type'] == UtilsAndConfig.SYSTEM_LOGOUT:self.socket.close()tkinter.messagebox.showwarning('提示', '此账号已在别处登录!')self.root.destroy()returnexcept ConnectionAbortedError:tkinter.messagebox.showwarning('提示', '与服务器断开连接!')self.root.destroy()returnexcept ConnectionResetError:tkinter.messagebox.showwarning('提示', '与服务器断开连接!')self.root.destroy()return

五、私聊或评论告诉我,获取源码

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/19697.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Linux-Socket实现模拟群聊(多人聊天室)

Linux-Socket实现模拟群聊&#xff08;多人聊天室&#xff09; 简单版本 服务端源码 #include<stdio.h> #include<stdlib.h> #include<pthread.h> #include<unistd.h> #include<string.h> #include<sys/socket.h> #include<arpa/ine…

不要拿 ChatGPT 干这 6 件事

虽然 ChatGPT 是一个强大的 AI 工具&#xff0c;能够生成连贯和相关性极强的回复&#xff0c;但它也有其局限性。它不是敏感信息的安全渠道&#xff0c;也不是法律或医疗咨询的可靠来源&#xff0c;不能替代人类的决策或专业的心理健康支持&#xff0c;也不是事实信息的权威来源…

宽客挑战赛: 量化投资基础知识12题

跨界知识聚会系列文章&#xff0c;“知识是用来分享和传承的”&#xff0c;各种会议、论坛、沙龙都是分享知识的绝佳场所。我也有幸作为演讲嘉宾参加了一些国内的大型会议&#xff0c;向大家展示我所做的一些成果。从听众到演讲感觉是不一样的&#xff0c;把知识分享出来&#…

为了做宽客选择读一个PhD值得吗

转 为了做宽客选择读一个PhD值得吗&#xff1f; 编者按语&#xff1a;接触不少想往quant方向走的童鞋,对于量化交易是很感兴趣也觉得未来大有可为&#xff0c;但是quant由于其行业性质其门槛具有一定的专业要求。现在很多投行、银行、公募、私募等招聘职位基本都要求硕士学历&…

“宽客鼻祖”的诞生——爱德华·索普

本文摘自《算法交易员&#xff1a;会赚钱的人工智能》 1961 年某个炎热的夜晚&#xff0c;美国拉斯维加斯赌场里有个男人正在玩“21点”&#xff0c;他的身后围着数十名看客。虽然这个男人已经淡定地玩了整整 6 小时&#xff0c;但从他戴着的大黑框眼镜和淋漓的汗水中还是可以…

克里夫·阿斯内斯:量化天才的宽客人生

克里夫阿斯内斯 &#xff08;Clifford Asness&#xff09;&#xff0c;1966年10月1日出生在美国纽约皇后区的一个普通中产阶级家庭。父亲是一名律师&#xff0c;母亲独立经营一家医疗教育公司。在宾夕法尼亚大学取得商学院和工程与应用科学院的两个学士学位之后&#xff0c;他进…

宽客人物事件图谱

看完了《宽客》这本书&#xff0c;描述的是20世纪美国华尔街对冲基金、股票市场、证券市场从早期的投资者靠直觉交易进化为数学家、物理学家靠数学模型进行交易的发展历程&#xff0c;这些进入金融界的数学家、物理学家依靠大数据分析、自动化交易找到市场中稍纵即逝的交易机会…

量化交易创干合送给每一位爱习宽客quat

原 量化交易原创干货合集&#xff0c;送给每一位爱学习的宽客quant 序号标题传送链接1双均线策略(期货) 量化策略源码https://www.myquant.cn/docs/python_strategyies/1532alpha对冲(股票期货) 量化策略源码https://www.myquant.cn/docs/python_strategyies/1013集合竞价选股…

量化交易创干货合送每位爱习宽客quan

原 量化交易原创干货合集,送给每一位爱学习的宽客quant 序号标题传送链接1双均线策略(期货) 量化策略源码https://www.myquant.cn/docs/python_strategyies/1532alpha对冲(股票+期货) 量化策略源码https://www.myquant.cn/docs/python_strategyies/1013集合竞价选股(股票)

惊恐 !ChatGPT通过谷歌L3入职面试,拿到18万美元offer,人类码农危?

上一篇&#xff1a;单男福利&#xff1f;程序猿用ChatGPT创造的虚拟老婆&#xff0c;被真女友强制「安乐死」 来源&#xff1a;新智元 【导读】ChatGPT已经通过谷歌面试&#xff0c;拿下offer了。看来&#xff0c;替代全部码农它还做不到&#xff0c;但替代一部分&#xff0c;已…

18.3 万美元offer到手!ChatGPT 通过谷歌 L3 面试:留给谷歌的时间不多了

微软投资、OpenAI 发布的 ChatGPT 去年底爆红后&#xff0c;“传统搜索引擎要亡”的言论甚嚣尘上&#xff0c;搜索巨头谷歌也拉响了“红色代码”警报&#xff0c;内部正在进行各种开发测试&#xff0c;应对这个看起来可能是几十年来谷歌价值 1490 亿美元搜索业务的第一个显著威…

ChatGPT助力校招----面试问题分享(十一)

1 ChatGPT每日一题&#xff1a;PCB布线&#xff0c;高速信号线走直角的后果 问题&#xff1a;PCB布线&#xff0c;高速信号线走直角的后果 ChatGPT&#xff1a;对于高速信号线来说&#xff0c;最好避免使用直角布线。直角布线会引入反射和信号损耗&#xff0c;从而导致信号完…

chatgpt赋能python:Python教程:如何打开和读取文件

Python教程&#xff1a;如何打开和读取文件 作为一名具有10年Python编程经验的工程师&#xff0c;我经常会发现自己需要处理和操作文件。因此&#xff0c;在本文中&#xff0c;我将向你展示如何使用Python在程序中打开和读取文件。 1. 文件路径 在打开文件之前&#xff0c;我…

ChatGPT+Midjourney融合

&#x1f31f;亲爱的小伙伴们&#xff01;今天我要向大家强烈推荐一种绝妙的工具&#xff0c;它将会给你的生活带来巨大的改变和便利。就是——ChatGPTMidjourney融合&#xff01;&#x1f525;&#x1f525;&#x1f525; ✨让我们来探索一下这个神奇的组合带来的惊喜吧&…

【ChatGPT】《ChatGPT “最强打工人” 7x24 随时待命》- 知识点目录

《ChatGPT “最强打工人” 7x24 随时待命》 编程 阅读篇 &#x1f4bb; 编程&#xff1a; &#x1f5a5;️ Bash 脚本生成器&#x1f468;‍&#x1f4bb;‍ Web Dev Q&A&#x1f468;‍&#x1f4bb; 代码逐行解释器 &#x1f4da; 阅读&#xff1a; &#x1f520; …

ChatGPT提问,BLIP-2回答:图像描述自动提问

ChatGPT Asks, BLIP-2 Answers: Automatic Questioning Towards Enriched Visual Descriptions (ChatGPT提问&#xff0c;BLIP-2回答&#xff1a;图像描述自动提问) https://arxiv.org/pdf/2303.06594.pdf https://github.com/Vision-CAIR/ChatCaptioner ChatCaptioner是一种…

微软发布基于ChatGPT打造的 Bing 搜索服务

微软推出了全新的Bing搜索体验&#xff0c;它是基于ChatGPT技术的。现在&#xff0c;所有人都可以使用桌面版的"Bing有限预览"。只需访问Bing.com&#xff0c;你就可能会看到全新的搜索界面&#xff0c;微软还提供了一些问题示例供参考。点击"试试吧"&…

前端已死? 2023 年前端十大 Web 趋势

点击“开发者技术前线”&#xff0c;选择“星标” 让一部分开发者看到未来 作者 | Robin Wiruch 来自|infoQ 译者 | 核子可乐 策划 | 丁晓昀 虽然就个人观点&#xff0c;我觉得 Web 开发的前景已经好几年没什么进展&#xff08;2016 年至 2021 年&#xff09;&#xff0c;但在…

两种开源聊天机器人的性能测试(一)——ChatterBot

因为最近在学习自然语言处理的相关知识&#xff0c;QQ小冰这个东西最近又很热&#xff0c;所以就试着玩了下两个开源聊天机器人&#xff0c;在这里分享一点小经验&#xff0c;希望对有共同兴趣的人能起到那么一点作用。 我主要测试了两个聊天机器人&#xff0c;一个是ChatterBo…

聊聊chatbot那些事

1. 生活中的chatbot ​ 现在社会&#xff0c;随着AI的迅猛发展&#xff0c;各种新技术层出不穷&#xff0c;大大改变了我们的生活&#xff0c;其中&#xff0c;有很多技术已经走入了我们的日常生活&#xff0c;比如CV领域的人脸识别 &#xff0c;NLP 领域的智能助手等。本次&a…