在 python 中发送和接收包裹
Sending and receiving package in python
这是 TCP 套接字服务器上的控制台聊天应用程序。客户端将 request/message 发送到服务器,服务器将消息分发给目标用户或提供请求 information.I am currently 运行 into a problem regarding recv package on the server side .我收到包裹并能够打印出来。但是系统出于某种原因仍然给我一个语法错误。
谢谢。
这是我的客户:
import socket
import select
import errno
import sys, struct
import pickle
HEADER_LENGTH = 1024
IP = "127.0.0.1"
PORT = 9669
def send_login_request(username):
package = [1]
length = len(username)
if length > 1019:
print ("Error: Username too long")
sys.exit()
package += struct.pack("I", length)
package += username
return package
def send_message(recv_id, message):
package = [2]
length = len(message)
if length > 1015:
print('message too long')
sys.exit()
package += recv_id
package += struct.pack('I', length)
package += message
return package
def send_con_request(conv_id):
package = [3]
length = len(id)
if length > 1015:
print('id too long')
sys.exit()
package += struct.pack("I", length)
package += conv_id
return package
# Create a socket
client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# Connect to a given ip and port
client_socket.connect((IP, PORT))
client_socket.setblocking(False)
my_username = input("Username: ")
request = send_login_request(my_username)
user_request = str(request)
client_socket.send(user_request.encode())
username_conf = client_socket.recv(HEADER_LENGTH).decode()
if username_conf == "Welcome to the server":
con_id = input("Please enter conversation's id, if don't have one, please enter no ")
if con_id == 'no':
con_request = send_con_request(con_id)
con_request = str(con_request)
client_socket.send(con_request.encode())
else:
con_request = send_con_request(con_id)
con_request = str(con_request)
client_socket.send(con_request.encode())
conversation = client_socket.recv(HEADER_LENGTH).decode()
recv_id = input("Please enter receiver's id")
while True:
# Wait for user to input a message
message = input(f'{my_username} > ').encode()
# If message is not empty - send it
if message:
send_message = send_message(recv_id,message)
client_socket.send(bytes(send_message))
try:
while True:
message_receiver = client_socket.recv(HEADER_LENGTH).decode()
x = message_receiver.split('|')
print(x)
username = x[0]
message = x[1]
# Print message
print(f'{username} > {message}')
except IOError as e:
if e.errno != errno.EAGAIN and e.errno != errno.EWOULDBLOCK:
print('Reading error: {}'.format(str(e)))
sys.exit()
# We just did not receive anything
continue
except Exception as e:
# Any other exception - something happened, exit
print('Reading error: {}'.format(str(e)))
sys.exit()
这是我的服务器:
import socket
import select
import struct
import sys
import pickle
HEADER_LENGTH = 1024
conversation ={}
users = [
{
'username': 'user1',
'user_id': 1
},
{
'username': 'user2',
'user_id': 2
},
{
'username': 'user3',
'user_id': 3
},
{
'username': 'user4',
'user_id': 4
},
{
'username': 'user5',
'user_id': 5
}
]
def login(username):
for user in users:
if user['username'] == username:
return user
else:
return False
IP = "127.0.0.1"
PORT = 9669
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
server_socket.bind((IP, PORT))
server_socket.listen()
# List of sockets for select.select()
sockets_list = [server_socket]
# List of connected clients - socket as a key, user header and name as data
clients_socket = {}
sessions = {
(1,2) : '1.txt',
(3,4) : '2.txt'
}
def getRecvSocket(user_id):
try:
return sessions[user_id]
except:
return None
def sendErrorMes(socketid, mes):
package = [9]
length = len(mes)
if length > 1019:
length = 1019
package += struct.pack("I", length)
package += mes
print(f'Listening for connections on {IP}:{PORT}...')
# Handles message receiving
def receive_message(client_socket):
try:
receive_message = client_socket.recv(HEADER_LENGTH)
return receive_message
except:
return False
while True:
read_sockets, _, exception_sockets = select.select(sockets_list, [], sockets_list)
# Iterate over notified sockets
for notified_socket in read_sockets:
# If notified socket is a server socket - new connection, accept it
if notified_socket == server_socket:
client_socket, client_address = server_socket.accept()
sockets_list.append(client_socket)
else:
# Receive message
package = receive_message(notified_socket)
print(package)
package_recv = eval(package.decode())
print(package_recv)
print(type(package_recv))
package_type = package_recv[0]
if package_type == 1:
size = struct.unpack("I", package[1:5])
if size[0] > 1019:
continue
username = package[5:5+size[0]]
username = username.decode()
# username = package_recv[1]
user = login(username)
if user == False:
notified_socket.send("no user found".encode())
else:
sessions[user["user_id"]] = notified_socket
notified_socket.send(("Welcome to the server").encode())
elif package_type == 2:
recv_id = struct.unpack("I", package[1:5])
size = struct.unpack("I", package[5:9])
if size[0] > 1015:
continue
# recv_id = package_recv[1]
if getRecvSocket(recv_id) == None:
sendErrorMes(notified_socket, "User is offline")
else:
message = package[9:9+size[0]]
# message = package_recv[2]
for socket in sessions.values():
if socket == notified_socket:
user = sessions[notified_socket]
# print(f'Received message from {user}, {message}')
# fIterate over connected clients and broadcast message
for client_socket in clients_socket:
# if clients[client_socket] == receive_user and client_socket != notified_socket:
# But don't sent it to sender
if client_socket != notified_socket and clients_socket[client_socket] == recv_id:
# Send user and message (both with their headers)
# We are reusing here message header sent by sender, and saved username header send by user when he connected
a = sessions[notified_socket]
b = recv_id
with open(f"{conversation[a,b]}.txt", "w"):
f.write(user + message)
client_socket.send((user + "|" + message).encode())
if message is False:
# print('Closed connection from: {}'.format(user))
# Remove from list for socket.socket()
sockets_list.remove(notified_socket)
# Remove from our list of users
del clients_socket[notified_socket]
continue
elif package_type == 3:
size = struct.unpack("I", package[1:5])
if size[0] > 1019:
continue
convo_id = package[5:5+size[0]]
convo_id = convo_id.decode()
# convo_id = package_recv[2]
if convo_id in conversation:
with open(conversation[convo_id], 'rb') as file_to_send:
for data in file_to_send:
notified_socket.sendall(data)
print('send successful')
else:
f = open(f"{len(conversation)+1}.txt", "w+")
这是服务器端的错误,我正在定位和解决问题:
Listening for connections on 127.0.0.1:9669...
b"[1, 5, 0, 0, 0, 'u', 's', 'e', 'r', '1']"
[1, 5, 0, 0, 0, 'u', 's', 'e', 'r', '1']
<class 'list'>
b''
Traceback (most recent call last):
File "c:/Users/Duong Dang/Desktop/bai 2.3/server.py", line 134, in <module>
package_recv = eval(package.decode())
File "<string>", line 0
^
SyntaxError: unexpected EOF while parsing
发送代码没有多大意义。您正在创建一个 python 列表,这是实现协议的最奇怪的方式。然后,您将获取该列表的 python 字符串表示形式并将其发送到服务器。您没有在服务器端做任何事情来确保您收到完整的消息。然后您使用 eval
来解释您在客户端创建的字符串。这是一种非常危险的做法,因为您的同行基本上可以指示您的 python 口译员做任何事情。
此外,您的 send_con_request
正在调用 len(id)
这根本不起作用,因为 id
是 python built-in 不提供__len__
方法。我认为应该是 len(conv_id)
?
无论如何,你应该修改你的协议。使用 struct
工具创建所需的正确二进制字符串。有很多可能的方法来构建它,但这里有一个。在客户端,创建一个 fixed-length header 来标识您发送的请求类型以及剩余“有效负载”字节的长度。您将首先使用 str.encode
.
将您的字符串(用户名或其他)转换为字节
import struct
# ProtoHeader encodes a 16 bit request identifer, plus a 32 bit payload
# length. A protocol data unit consists of this 6-byte header followed by
# payload bytes (which will vary according to the request)
ProtoHeader = struct.Struct("!HI")
LoginRequest = 1
SomeOtherRequest = 2
...
def format_login_request(username):
""" Create a protocol block containing a user login request.
Return the byte string containing the encoded request """
username_bytes = username.encode()
proto_block = ProtoHeader.pack(LoginRequest, len(username_bytes)) + username_bytes
return proto_block
...
conn.sendall(format_login_request(username))
在服务器端,您将首先收到 fixed-length header(它告诉您请求类型是什么以及存在多少其他有效负载字节)。然后接收那些剩余的字节,以确保您得到那么多。 socket.recv
不保证您将从对等方接收到任何特定 send
中发送的字节数。它确实 保证您会以正确的顺序收到它们,因此您必须继续收到,直到收到您期望的号码。这就是为什么将固定长度的字节字符串作为 header 并对可变长度有效负载中预期的字节数进行编码很重要。
服务器看起来像这样:
import struct
ProtoHeader = struct.Struct("!HI")
LoginRequest = 1
def receive_bytes(conn, count):
""" General purpose receiver:
Receive exactly @count bytes from @conn """
buf = b''
remaining = count
while remaining > 0:
# Receive part or all of data
tbuf = conn.recv(remaining)
tbuf_len = len(tbuf)
if tbuf_len == 0:
# Really you probably want to return 0 here if buf is empty and
# allow the higher-level routine to determine if the EOF is at
# a proper message boundary in which case, you silently close the
# connection. You would normally only raise an exception if you
# EOF in the *middle* of a message.
raise RuntimeError("end of file")
buf += tbuf
remaining -= tbuf_len
return buf
def receive_proto_block(conn):
""" Receive the next protocol block from @conn. Return a tuple of
request_type (integer) and payload (byte string) """
proto_header = receive_bytes(conn, ProtoHeader.size)
request_type, payload_length = ProtoHeader.unpack(proto_header)
payload = receive_bytes(conn, payload_length)
return request_type, payload
...
request_type, payload = receive_proto_block(conn)
if request_type == LoginRequest:
username = payload.decode()
这是 TCP 套接字服务器上的控制台聊天应用程序。客户端将 request/message 发送到服务器,服务器将消息分发给目标用户或提供请求 information.I am currently 运行 into a problem regarding recv package on the server side .我收到包裹并能够打印出来。但是系统出于某种原因仍然给我一个语法错误。
谢谢。
这是我的客户:
import socket
import select
import errno
import sys, struct
import pickle
HEADER_LENGTH = 1024
IP = "127.0.0.1"
PORT = 9669
def send_login_request(username):
package = [1]
length = len(username)
if length > 1019:
print ("Error: Username too long")
sys.exit()
package += struct.pack("I", length)
package += username
return package
def send_message(recv_id, message):
package = [2]
length = len(message)
if length > 1015:
print('message too long')
sys.exit()
package += recv_id
package += struct.pack('I', length)
package += message
return package
def send_con_request(conv_id):
package = [3]
length = len(id)
if length > 1015:
print('id too long')
sys.exit()
package += struct.pack("I", length)
package += conv_id
return package
# Create a socket
client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# Connect to a given ip and port
client_socket.connect((IP, PORT))
client_socket.setblocking(False)
my_username = input("Username: ")
request = send_login_request(my_username)
user_request = str(request)
client_socket.send(user_request.encode())
username_conf = client_socket.recv(HEADER_LENGTH).decode()
if username_conf == "Welcome to the server":
con_id = input("Please enter conversation's id, if don't have one, please enter no ")
if con_id == 'no':
con_request = send_con_request(con_id)
con_request = str(con_request)
client_socket.send(con_request.encode())
else:
con_request = send_con_request(con_id)
con_request = str(con_request)
client_socket.send(con_request.encode())
conversation = client_socket.recv(HEADER_LENGTH).decode()
recv_id = input("Please enter receiver's id")
while True:
# Wait for user to input a message
message = input(f'{my_username} > ').encode()
# If message is not empty - send it
if message:
send_message = send_message(recv_id,message)
client_socket.send(bytes(send_message))
try:
while True:
message_receiver = client_socket.recv(HEADER_LENGTH).decode()
x = message_receiver.split('|')
print(x)
username = x[0]
message = x[1]
# Print message
print(f'{username} > {message}')
except IOError as e:
if e.errno != errno.EAGAIN and e.errno != errno.EWOULDBLOCK:
print('Reading error: {}'.format(str(e)))
sys.exit()
# We just did not receive anything
continue
except Exception as e:
# Any other exception - something happened, exit
print('Reading error: {}'.format(str(e)))
sys.exit()
这是我的服务器:
import socket
import select
import struct
import sys
import pickle
HEADER_LENGTH = 1024
conversation ={}
users = [
{
'username': 'user1',
'user_id': 1
},
{
'username': 'user2',
'user_id': 2
},
{
'username': 'user3',
'user_id': 3
},
{
'username': 'user4',
'user_id': 4
},
{
'username': 'user5',
'user_id': 5
}
]
def login(username):
for user in users:
if user['username'] == username:
return user
else:
return False
IP = "127.0.0.1"
PORT = 9669
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
server_socket.bind((IP, PORT))
server_socket.listen()
# List of sockets for select.select()
sockets_list = [server_socket]
# List of connected clients - socket as a key, user header and name as data
clients_socket = {}
sessions = {
(1,2) : '1.txt',
(3,4) : '2.txt'
}
def getRecvSocket(user_id):
try:
return sessions[user_id]
except:
return None
def sendErrorMes(socketid, mes):
package = [9]
length = len(mes)
if length > 1019:
length = 1019
package += struct.pack("I", length)
package += mes
print(f'Listening for connections on {IP}:{PORT}...')
# Handles message receiving
def receive_message(client_socket):
try:
receive_message = client_socket.recv(HEADER_LENGTH)
return receive_message
except:
return False
while True:
read_sockets, _, exception_sockets = select.select(sockets_list, [], sockets_list)
# Iterate over notified sockets
for notified_socket in read_sockets:
# If notified socket is a server socket - new connection, accept it
if notified_socket == server_socket:
client_socket, client_address = server_socket.accept()
sockets_list.append(client_socket)
else:
# Receive message
package = receive_message(notified_socket)
print(package)
package_recv = eval(package.decode())
print(package_recv)
print(type(package_recv))
package_type = package_recv[0]
if package_type == 1:
size = struct.unpack("I", package[1:5])
if size[0] > 1019:
continue
username = package[5:5+size[0]]
username = username.decode()
# username = package_recv[1]
user = login(username)
if user == False:
notified_socket.send("no user found".encode())
else:
sessions[user["user_id"]] = notified_socket
notified_socket.send(("Welcome to the server").encode())
elif package_type == 2:
recv_id = struct.unpack("I", package[1:5])
size = struct.unpack("I", package[5:9])
if size[0] > 1015:
continue
# recv_id = package_recv[1]
if getRecvSocket(recv_id) == None:
sendErrorMes(notified_socket, "User is offline")
else:
message = package[9:9+size[0]]
# message = package_recv[2]
for socket in sessions.values():
if socket == notified_socket:
user = sessions[notified_socket]
# print(f'Received message from {user}, {message}')
# fIterate over connected clients and broadcast message
for client_socket in clients_socket:
# if clients[client_socket] == receive_user and client_socket != notified_socket:
# But don't sent it to sender
if client_socket != notified_socket and clients_socket[client_socket] == recv_id:
# Send user and message (both with their headers)
# We are reusing here message header sent by sender, and saved username header send by user when he connected
a = sessions[notified_socket]
b = recv_id
with open(f"{conversation[a,b]}.txt", "w"):
f.write(user + message)
client_socket.send((user + "|" + message).encode())
if message is False:
# print('Closed connection from: {}'.format(user))
# Remove from list for socket.socket()
sockets_list.remove(notified_socket)
# Remove from our list of users
del clients_socket[notified_socket]
continue
elif package_type == 3:
size = struct.unpack("I", package[1:5])
if size[0] > 1019:
continue
convo_id = package[5:5+size[0]]
convo_id = convo_id.decode()
# convo_id = package_recv[2]
if convo_id in conversation:
with open(conversation[convo_id], 'rb') as file_to_send:
for data in file_to_send:
notified_socket.sendall(data)
print('send successful')
else:
f = open(f"{len(conversation)+1}.txt", "w+")
这是服务器端的错误,我正在定位和解决问题:
Listening for connections on 127.0.0.1:9669...
b"[1, 5, 0, 0, 0, 'u', 's', 'e', 'r', '1']"
[1, 5, 0, 0, 0, 'u', 's', 'e', 'r', '1']
<class 'list'>
b''
Traceback (most recent call last):
File "c:/Users/Duong Dang/Desktop/bai 2.3/server.py", line 134, in <module>
package_recv = eval(package.decode())
File "<string>", line 0
^
SyntaxError: unexpected EOF while parsing
发送代码没有多大意义。您正在创建一个 python 列表,这是实现协议的最奇怪的方式。然后,您将获取该列表的 python 字符串表示形式并将其发送到服务器。您没有在服务器端做任何事情来确保您收到完整的消息。然后您使用 eval
来解释您在客户端创建的字符串。这是一种非常危险的做法,因为您的同行基本上可以指示您的 python 口译员做任何事情。
此外,您的 send_con_request
正在调用 len(id)
这根本不起作用,因为 id
是 python built-in 不提供__len__
方法。我认为应该是 len(conv_id)
?
无论如何,你应该修改你的协议。使用 struct
工具创建所需的正确二进制字符串。有很多可能的方法来构建它,但这里有一个。在客户端,创建一个 fixed-length header 来标识您发送的请求类型以及剩余“有效负载”字节的长度。您将首先使用 str.encode
.
import struct
# ProtoHeader encodes a 16 bit request identifer, plus a 32 bit payload
# length. A protocol data unit consists of this 6-byte header followed by
# payload bytes (which will vary according to the request)
ProtoHeader = struct.Struct("!HI")
LoginRequest = 1
SomeOtherRequest = 2
...
def format_login_request(username):
""" Create a protocol block containing a user login request.
Return the byte string containing the encoded request """
username_bytes = username.encode()
proto_block = ProtoHeader.pack(LoginRequest, len(username_bytes)) + username_bytes
return proto_block
...
conn.sendall(format_login_request(username))
在服务器端,您将首先收到 fixed-length header(它告诉您请求类型是什么以及存在多少其他有效负载字节)。然后接收那些剩余的字节,以确保您得到那么多。 socket.recv
不保证您将从对等方接收到任何特定 send
中发送的字节数。它确实 保证您会以正确的顺序收到它们,因此您必须继续收到,直到收到您期望的号码。这就是为什么将固定长度的字节字符串作为 header 并对可变长度有效负载中预期的字节数进行编码很重要。
服务器看起来像这样:
import struct
ProtoHeader = struct.Struct("!HI")
LoginRequest = 1
def receive_bytes(conn, count):
""" General purpose receiver:
Receive exactly @count bytes from @conn """
buf = b''
remaining = count
while remaining > 0:
# Receive part or all of data
tbuf = conn.recv(remaining)
tbuf_len = len(tbuf)
if tbuf_len == 0:
# Really you probably want to return 0 here if buf is empty and
# allow the higher-level routine to determine if the EOF is at
# a proper message boundary in which case, you silently close the
# connection. You would normally only raise an exception if you
# EOF in the *middle* of a message.
raise RuntimeError("end of file")
buf += tbuf
remaining -= tbuf_len
return buf
def receive_proto_block(conn):
""" Receive the next protocol block from @conn. Return a tuple of
request_type (integer) and payload (byte string) """
proto_header = receive_bytes(conn, ProtoHeader.size)
request_type, payload_length = ProtoHeader.unpack(proto_header)
payload = receive_bytes(conn, payload_length)
return request_type, payload
...
request_type, payload = receive_proto_block(conn)
if request_type == LoginRequest:
username = payload.decode()