struct
udp_struct客户端
udp_socket是面向无连接的不可靠的通信,所以建立通信比较简单
- 建立连接 socket
- 绑定连接 bind
- 发送信息 sendto(str,(ip,port))
- 关闭连接 close
# 1创建一个套接字
udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# 2绑定一个本地信息
localaddr = ("", 7788)
udp_socket.bind(localaddr)
while True:
# 3发送数据
str=input("please input:")
udp_socket.sendto(str.encode("utf-8"),("10.11.162.208",8080))
if str=='exit':
break
# 4关闭一个套接字
udp_socket.close()
udp_socket服务端
- 建立一个套接字 socket
- 绑定本地消息 bind
- 接受数据 recvfrom(1024)
- 关闭连接 close
# 1创建一个套接字
udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# 2绑定一个本地信息
localaddr = ("", 7788)
udp_socket.bind(localaddr)
while True:
# 3接收数据
rece_data = udp_socket.recvfrom(1024)
# rece_data这个变量中存储的是一个元组(接收到的数据,(发送方的ip,port))
recv_msg = rece_data[0] # 储存接收到的数据
send_addr = rece_data[1] # 储存发送方的地址信息
# 4打印数据
# print(rece_data)
print(("%s:%s" % (str(send_addr), recv_msg.decode("gbk"))))
# 5关闭一个套接字
udp_socket.close()
tcp_socket是面向连接的可靠的一对一的通信,所以建立相对步骤多一些
tcp_socket客户端
- 创建一个套接字 socket
- 绑定一个服务器 bind
- 连接一个服务器 connect()
- 发送数据 send
- 关闭连接 close
# 1.创建一个套接字
tcp_stock=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
# 2.连接一个服务器
# 2.1绑定一个服务器
f_id=input("please input id:")
f_port=int(input("please input port:"))
f_addr=(f_id,f_port)
#3连接一个服务器
tcp_stock.connect(f_addr)
# 4.发送数据
msg=input("please input msg;")
tcp_stock.send(msg.encode("gbk"))
# 5.关闭套接字
tcp_stock.close()
tcp_socket服务端
tcp_server创建过程类似买了手机打电话的过程
1.创建一个套接字(买个手机)
2.bind绑定ip和port和信息(插入手机卡)
3.让默认的套接字右主动变为被动listen(让手机设置为正常的
响铃接收模式)
4.等待客户端的连接 accept(等待别人的来电)
5.接收客户端的消息 recv
6.关闭连接 close
# 1.创建一个套接字(买个手机)
tcp_server_socket=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
# 2.bind绑定ip和port和信息(插入手机卡)
local_addr=("",7788)
tcp_server_socket.bind(local_addr)
# 3.让默认的套接字右主动变为被动listen(让手机设置为正常的
tcp_server_socket.listen(128)
# 4.等待客户端的连接 accept(等待别人的来电)
while True:
print('---等待新客户端连接')
new_client_socket,client_addr=tcp_server_socket.accept()
print('---2----\n'
,client_addr,"客户端连接成功,等待客户端发送消息")
# 接收客户端发送来得请求
while True:
recv_data=new_client_socket.recv(1024)
print("接收到了客户端的消息:%s" %recv_data.decode("gbk"))
# 回送一些数据给客户端
new_client_socket.send("回送消息:问题正在解决---ok--".encode('gbk'))
# 关闭套接字
if recv_data!='exit':
new_client_socket.send("回送消息:问题正在解决---ok--".encode('gbk'))
else:
break
print("当前客户已结束服务")
new_client_socket.close()
tcp_server_socket.close()
socket多线程实现多用户连接通信
基本思路
tcp_socket客户端和服务端的创建和之前的一样,只不过在服务端接收recv时,使用了多线程来调用接收和回送方法,避免同一线程会覆盖new_client_socket
import socket
import threading
def handle_socket(client, addr):
while True:
data = client.recv(1024)
print("接收到客户端%s的消息:%s" % (addr[1], data.decode("gbk")))
to_client = input("please input:")
client.send(to_client.encode())
if data == b'exit':
break
client.close()
def main():
# 1.创建一个套接字(买个手机)
tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 2.bind绑定ip和port和信息(插入手机卡)
local_addr = ("", 7788)
tcp_server_socket.bind(local_addr)
# 3.让默认的套接字右主动变为被动listen(让手机设置为正常的
tcp_server_socket.listen(128)
# 4.等待客户端的连接 accept(等待别人的来电)
while True:
new_client_socket, client_addr = tcp_server_socket.accept()
print(client_addr, "客户端连接成功")
# 多线程实现多用户登录,不覆盖new_client_socket
client_thread = threading.Thread(target=handle_socket,args=(new_client_socket, client_addr))
client_thread.start()
if __name__ == '__main__':
main()
使用异步asyncore实现多客户端的聊天室
聊天室服务器各个类的说明
聊天室服务端
ChatServer:类似socket_server,负责接收客户端的连接请求和创建socket对象
建好的socket对象要传给ChatSession对象进行保存
ChatSession:保存socket对象,负责与客户端进行实际通信 ,继承async_chat这个父类,主要的方法:
collect_incoming_data用于数据的存储,found_terminator设置聊天的数据传输规则,以换行符标识
每一条消息,handle_close退出聊天
CommandHandler:用于自定义协议,自定义方法的预设格式,例如方法都是do_开头
然后在代码中通过反射的方法直接调用
Room:一:维护存在的所有的用户的session列表,以及广播发送消息处理
二:维护当前用户的聊天状态,比如:LoginRoom、login_Room.ChatRoom
import asyncore, asynchat
class EndSession(Exception):
pass
class ChatServer(asyncore.dispatcher):
def __init__(self, port):
asyncore.dispatcher.__init__(self)
self.create_socket()
self.set_reuse_addr()
self.bind(('localhost', port))
self.listen(5)
self.users = {} # 记录当前用户的一个字典
self.main_room = ChatRoom(self)
def handle_accepted(self, sock, addr):
print('当前连接', sock, addr)
ChatSession(self, sock)
class ChatSession(asynchat.async_chat):
'''
负责和客户端进行通信
'''
def __init__(self, server, sock):
asynchat.async_chat.__init__(self, sock)
self.server = server
self.set_terminator(b'\n') # 设置聊天数据分隔协议,以换行符作为数据结束的表示
self.data = []
self.name = None # 保存用户名字
self.enter(LoginRoom(self.server))
def enter(self, room):
# 从当前房间移除自身,然后添加到指定房间
try:
cur = self.room
except AttributeError:
pass
else:
cur.remove(self)
self.room = room
room.add(self)
def collect_incoming_data(self, data):
self.data.append(data.decode())
def found_terminator(self):
'''
当客户端的一条数据结束时调用
:return:
'''
line = ''.join(self.data) # 将消息记录拼接成一个字符串
self.data = []
try:
self.room.handle(self, line.encode())
except EndSession:
self.handle_close()
def handle_close(self):
asynchat.async_chat.handle_close(self)
self.enter(LogoutRoom(self.server))
class CommandHandler:
def handle(self, session, line):
line = line.decode()
if not line.strip():
return
parts = line.split(' ', 1)
cmd = parts[0] # do_login msg
try:
line = parts[1].strip()
except IndexError:
line = ''
method = getattr(self, 'do_' + cmd, None)
try:
method(session, line)
except TypeError:
pass
class Room(CommandHandler):
'''
包含多个用户环境, 负责基本的命令处理和广播消息
'''
def __init__(self, server):
self.server = server
self.sessions = [] # 记录在线人数
def add(self, session):
self.sessions.append(session)
def remove(self, session):
self.sessions.remove(session)
def broadcast(self, line):
for session in self.sessions:
session.push(line)
def do_logout(self, session, line):
raise EndSession
class ChatRoom(Room):
'''
代表正在聊天的客户
'''
def add(self, session):
session.push(b'login success') # 发送给客户端
self.broadcast((session.name + ' has entered the room. \n').encode())
self.server.users[session.name] = session
Room.add(self, session)
def remove(self, session):
Room.remove(self, session)
self.broadcast((session.name + ' has left the room.\n').encode())
def do_say(self, session, line):
self.broadcast((session.name + ':' + line + '\n').encode())
def do_look(self, session, line):
'''
查询在线用户
:param session:
:param line:
:return:
'''
session.push(b'Online Users:\n')
for user in self.sessions:
session.push((user.name + '\n').encode())
class LoginRoom(Room):
def add(self, session):
Room.add(self, session)
session.push(b'Connect Success')
def do_login(self, session, line):
name = line.strip()
if not name:
session.push(b'Username empty')
elif name in self.server.users:
session.push(b'Username exists')
else:
session.name = name
session.enter(self.server.main_room)
class LogoutRoom(Room):
'''
处理退出的用户
'''
def add(self, session):
try:
del self.server.users[session.name]
print('用户{}离线啦'.format(session.name))
except KeyError:
print('User does not exists')
if __name__ == '__main__':
PORT = 6666
ChatServer(PORT)
print('服务器已经在0.0.0.0:{}开始运行...'.format(PORT))
asyncore.loop()
聊天室GUI客户端
'''
聊天室客户端1
'''
import PySimpleGUI as sg
import telnetlib
def login(conn):
is_login = False
layout = [
[sg.Text('登录聊天室')],
[sg.Text('请输入您的用户名:'), sg.InputText(key='-username-'), sg.Button('进入聊天室')]
]
window = sg.Window('聊天室', layout)
while True:
event, values = window.read()
if event == '进入聊天室': # 如果用户点击了进入聊天室的按钮
username = values['-username-'].strip() ## 获取了InputText里面用户输入的用户名
if username:
try:
conn.open('127.0.0.1', 6666, timeout=10)
except:
sg.popup('连接失败,请检查服务器设置')
break
resp = conn.read_some() # 读取服务器的响应信息
if resp != b'Connect Success':
sg.popup('连接失败,请检查服务器设置')
break
conn.write(('login ' + username + '\n').encode()) # 向服务器发送登录请求
resp = conn.read_some()
if resp == b'Username exists':
sg.popup('用户名已存在')
else:
sg.popup('登录成功')
is_login = True
break
else:
sg.popup('请输入用户名')
if event in (None, 'Cancel'):
break
window.close()
del window
if is_login:
chat(conn, username)
def chat(conn, username):
layout = [
[sg.Text('聊天窗口(' + username + ')')],
[sg.Multiline(disabled=True, size=(80, 20), key='-chathistory-')],
[sg.InputText(size=(70, 5), key='-message-'), sg.Button('发送消息')],
[sg.Button('查询在线人数'), sg.Button('退出聊天室')]
]
window = sg.Window('Python聊天室', layout)
while True:
event, values = window.read(timeout=10) # timeout参数表示当前是异步周期性更新模式
if values:
history_msg = values['-chathistory-']
result = conn.read_very_eager().decode()
if result.strip():
if 'Online' in result:
sg.popup('当前在线用户', result)
else:
window['-chathistory-'].update(history_msg + result.strip())
if event in (None, '退出聊天室'):
break
elif event == '发送消息':
send(conn, 'say', values['-message-'])
window['-message-'].update('')
elif event == '查询在线人数':
send(conn, type='look')
print('窗口即将关闭')
conn.write(b'logout\n')
conn.close()
window.close()
def send(conn, type=None, msg=None):
if type == 'say':
conn.write(('say ' + msg + '\n').encode())
elif type == 'look':
conn.write(b'look\n')
if __name__ == '__main__':
conn = telnetlib.Telnet()
login(conn)
运行结果如图
最后感谢B站socket老师