基于UDP & python的网络聊天室

实现功能

  1. 服务端:开启聊天室,向客户机发送消息并转发客户机的消息给聊天室的其他客户机
  2. 客户端:加入聊天室,输入有效用户名,开始聊天

python代码

client.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import socket
import sys, threading

# TODO:详细的错误处理

def receive_message():
while True:
try:
# 客户端用recv接收数据,最大1024字节
print(s.recv(1024).decode('utf-8'))
except:
break


def send_message():
while True:
try:
message = input()
# 发送给服务器
s.sendto((name + ':' + message).encode('utf-8'), server_socket)
if message == "exit":
break
except KeyboardInterrupt:
# 中断退出
s.sendto((name + ':' + "exit").encode('utf-8'), server_socket)
break
except socket.herror:

break


s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

server_ip = input("请输入服务器的ip:")
server_port = int(input("请输入服务器绑定的端口号:"))
server_socket = (server_ip, server_port)

print('''----- 欢迎来到聊天室,退出聊天室请输入 "exit" -----''')

name = input("输入你的昵称:")
# 向服务器发送新用户名
s.sendto(name.encode('utf-8'), server_socket)
receive = s.recv(1024).decode('utf-8')
# 昵称重复时,服务器回复 "NO"
while receive == "NO":
name = input("该昵称已被使用,请重新输入:")
s.sendto(name.encode('utf-8'), server_socket)
receive = s.recv(1024).decode('utf-8')

print('''--------------- %s ---------------''' % name)

# 使用多线程收发信息
thread_receive = threading.Thread(target=receive_message, name = "thread_receive")
thread_send = threading.Thread(target=send_message, name="thread_send")
thread_receive.start()
thread_send.start()
thread_send.join()

# 客户端关闭连接
s.close()
print("--------------- 连接关闭 ---------------")

server.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
# !/usr/bin/env python3
# -*- coding: utf-8 -*-

import socket

# 创建socket对象
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # 套接字类型为无连接的UDP

# 使用本机ip
server_ip = socket.gethostbyname(socket.gethostname())
server_port = int(input("请输入要绑定的端口号:"))

# 绑定端口
print("server" + server_ip + ':' + str(server_port))
s.bind((server_ip, server_port))

# 保存所有用户的用户名和socket
users = {}

while True:
# 服务器端调用recvfrom接收数据,最大1024字节
try:
data, address = s.recvfrom(1024)
except:
print("recvfrom异常,服务器关闭")
break
else:
print('Received from %s:%s.' % address)
# 将要广播的消息
broadcast = b""
# 检查所有用户数据,判断是否为新用户
if address not in users.values():
# 检查用户名是否已经存在(新用户只发送名字)
if data.decode('utf-8') in users.keys():
# 发送失败消息
s.sendto(b"NO", address)
continue
else:
s.sendto(b"YES", address)
# 通知所有人新用户上线
broadcast = ("欢迎 " + data.decode('utf-8') + " 进入聊天室...").encode('utf-8')
# 加入用户信息
users[data.decode('utf-8')] = address
else:
# 分割消息
name, message = data.decode('utf-8').split(':', 1)
# 用户退出消息
if message == "exit":
broadcast = (name + "退出聊天室...").encode('utf-8')
del users[name]
else:
# 普通消息
broadcast = (name + " : " + message).encode('utf-8')
# 向(除发送方外的)客户发送响应
for addr in users.values():
if addr != address:
s.sendto(broadcast, addr)

s.close()

运行效果