python在同一连接上套接字多条消息
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/42222425/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
python sockets multiple messages on same connection
提问by Dima Gimburg
My question is more general rather than specific. I want to implement a simple client server application just to deliver messagesfrom the client to the server and get acknowledgments back from the server.
我的问题更笼统而不是具体。我想实现一个简单的客户端服务器应用程序,只是为了将消息从客户端传递到服务器并从服务器获得确认。
I was wondering what do I have to take into consideration when doing so with sockets, do I have to implement my own communication interface and manage the messaging on the same connection or create a new connection on for each message?
我想知道在使用套接字时我必须考虑什么,我是否必须实现自己的通信接口并管理同一连接上的消息传递或为每条消息创建一个新连接?
(please make assumption that the message for right now is less than BUFFER_SIZE)
(请假设现在的消息小于 BUFFER_SIZE)
The code is something like this:
代码是这样的:
server.py
服务器.py
server_info = (HOST, PORT)
sock = socket.socket(family=AF_INET, type=SOCK_STREAM)
sock.bind(server_info)
sock.listen(NUMBER_OF_SOCKETS)
try:
while True:
connection, client_address = sock.accept()
try:
while True:
data = connection.recv(BUFFER_SIZE)
print('message received: {data}'.format(data=data))
connection.send("ok")
finally:
connection.close()
client.py
客户端.py
server_info = (HOST, PORT)
sock = socket.socket(family=AF_INET, type=SOCK_STREAM)
sock.connect(server_info)
try:
print("connection established")
while True:
print("Please enter a message you want to pass to the server")
msg = raw_input()
print('sending "{message}"'.format(message=msg))
sock.send(msg)
while True:
data = sock.recv(constants.BUFFER_SIZE)
print('received "{data}"'.format(data=data))
break
finally:
print('closing socket')
sock.close()
this code gives me the ability to receive multiple message on the server side and send multiple messages from the client side. Is it the right way to do it? I had to make 2 infinite loops on the client side in order to do so, what about closing the connection? when I send a 0 byte message both server and client get stuck.
这段代码使我能够在服务器端接收多条消息并从客户端发送多条消息。这是正确的方法吗?为此,我必须在客户端进行 2 个无限循环,关闭连接怎么样?当我发送 0 字节消息时,服务器和客户端都卡住了。
thank you very much!
非常感谢!
回答by Ohad the Lad
Adding a two types of server-client one is over multi process and the other is asynchronous, they do almost the same thing, the asynchronous one is more robust, read why here: Threads vs. Async.
添加两种类型的服务器-客户端,一种是多进程的,另一种是异步的,它们几乎做同样的事情,异步的更健壮,请在此处阅读原因: 线程与异步。
My examples: Using multi process:
我的例子:使用多进程:
import multiprocessing
import socket
import time
HOST = "0.0.0.0"
PORT = 9000
def handle(connection, address):
try:
while True:
data = connection.recv(1024)
connection.sendall(data + ' server time {}'.format(time.time()))
except:
pass
finally:
connection.close()
class Server(object):
def __init__(self, hostname, port):
self.hostname = hostname
self.port = port
def start(self):
self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.socket.bind((self.hostname, self.port))
self.socket.listen(1)
while True:
conn, address = self.socket.accept()
process = multiprocessing.Process(
target=handle, args=(conn, address))
process.daemon = True
process.start()
if __name__ == "__main__":
server = Server(HOST, PORT)
try:
print 'start'
server.start()
except:
print 'something wrong happened, a keyboard break ?'
finally:
for process in multiprocessing.active_children():
process.terminate()
process.join()
print 'Goodbye'
And the client for it :
和它的客户:
import sys
import threading
import time
import socket
SOCKET_AMOUNT = 100
HOST = "localhost"
PORT = 9000
def myclient(ip, port, message):
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect((ip, port))
sock.sendall(message)
result = sock.recv(1024)
print result + ' final clnt time {}'.format(time.time())
sock.close()
if __name__ == "__main__":
thread_list = []
for i in range(SOCKET_AMOUNT):
msg = "Thread #{}, clnt time {}".format(i, time.time())
client_thread = threading.Thread(
target=myclient, args=(HOST, PORT, msg))
thread_list.append(client_thread)
client_thread.start()
waiting = time.time()
[x.join() for x in thread_list]
done = time.time()
print 'DONE {}. Waiting for {} seconds'.format(done, done-waiting)
The next server is a lot more robust !!! data is not getting lost !!! the server:
下一个服务器更强大!!!数据没有丢失!!!服务器:
import asyncore
import socket
import time
import logging
import json
class Server(asyncore.dispatcher):
def __init__(self, host, port):
self.logger = logging.getLogger('SERVER')
asyncore.dispatcher.__init__(self)
self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
self.set_reuse_addr()
self.bind(('', port))
self.listen(confjson.get('SERVER_QUEUE_SIZE', None))
self.logger.debug('binding to {}'.format(self.socket.getsockname()))
def handle_accept(self):
socket, address = self.accept()
self.logger.debug('new connection accepted')
EchoHandler(socket)
class EchoHandler(asyncore.dispatcher_with_send):
def handle_read(self):
msg = self.recv(confjson.get('RATE', None))
self.out_buffer = msg
self.out_buffer += ' server recieve: {}'.format(time.time())
if not self.out_buffer:
self.close()
if __name__ == "__main__":
logging.basicConfig(level=logging.DEBUG,
format='%(name)s: %(message)s',
)
with open('config.json', 'r') as jfile:
confjson = json.load(jfile)
try:
logging.debug('Server start')
server = Server(confjson.get('HOST', None),
confjson.get('PORT', None))
asyncore.loop()
except:
logging.error('Something happened,\n'
'if it was not a keyboard break...\n'
'check if address taken, '
'or another instance is running. Exit')
finally:
logging.debug('Goodbye')
And the async client:
和异步客户端:
import asyncore
import socket
import time
import logging
import json
class Client(asyncore.dispatcher_with_send):
def __init__(self, host, port, message, pk):
self.logger = logging.getLogger('CLIENT')
asyncore.dispatcher.__init__(self)
self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
self.host = host
self.port = port
self.connect((host, port))
self.out_buffer = message
self.clientID = pk
self.logger.debug('Connected #{}'.format(self.clientID))
def handle_close(self):
self.close()
def handle_read(self):
rec_msg = self.recv(confjson.get('RATE', None))
self.logger.debug('#{}, {} back at client {}'.format(self.clientID,
rec_msg,
time.time()
)
)
self.close()
if __name__ == "__main__":
logging.basicConfig(level=logging.DEBUG,
format='%(name)s: %(message)s',
)
with open('config.json', 'r') as jfile:
confjson = json.load(jfile)
clients = []
for idx in range(confjson.get('SOCKET_AMOUNT', None)):
msg = "Start: {}".format(time.time())
clients.append(Client(confjson.get('HOST', None),
confjson.get('PORT', None),
msg,
idx)
)
start = time.time()
logging.debug(
'Starting async loop for all connections, unix time {}'.format(start))
asyncore.loop()
logging.debug('{}'.format(time.time() - start))
and a small config file:
和一个小的配置文件:
{
"HOST": "127.0.0.1",
"PORT": 5007,
"RATE": 8096,
"SERVER_QUEUE_SIZE": 16,
"SOCKET_AMOUNT": 100
}
回答by Shivkumar kondi
In bi-directional communication, by default, a client can know when it is done sending, but it cannot know if it is done receiving. And, also the server cannot know whether the client is done sending.
在双向通信中,默认情况下,客户端可以知道何时完成发送,但不知道是否完成接收。而且,服务器也无法知道客户端是否已完成发送。
Code :
代码 :
def recv_end(the_socket):
End='SERVER WRONG MARKER'
total_data=[];data='';got_end=False
while True:
data=the_socket.recv(8192)
if not data: break
if End in data:
total_data.append(data[:data.find(End)])
got_end=True
break
total_data.append(data)
if len(total_data)>1:
#check if end_of_data was split
last_pair=total_data[-2]+total_data[-1]
if End in last_pair:
total_data[-2]=last_pair[:last_pair.find(End)]
total_data.pop()
got_end=True
break
return (got_end,''.join(total_data))
def basic_server(sock):
got=[]
got_end,data = recv_end(sock)
if not got_end:
sock.send('ERROR:no end!') #<--- not possible w/close()
else: sock.sendall(data*2)
sock.shutdown(1)
sock.close()
import socket
Port=4444
def start_server():
sock=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
sock.bind(('',Port))
sock.listen(5)
print 'started on',Port
while True:
newsock,address=sock.accept()
basic_server(newsock)
def send_data(data):
sock=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
sock.connect(('localhost',Port))
print 'connected'
sock.sendall(data+'CLIENT WRONG MARKER')
print 'sent',data
sock.shutdown(1)
print 'shutdown'
result=[]
while True:
got=sock.recv(2)
if not got: break
result.append(got)
sock.close()
return ''.join(result)
if __name__=='__main__':
start_server()
You could do something like put a byte countin front of the data, or have an end marker so the server can know if it got all of the bytes.
你可以做一些事情,比如在数据前面放一个字节数,或者有一个结束标记,这样服务器就可以知道它是否得到了所有的字节。
However, that introduces a problem. What if the byte count is wrong or the end marker never arrives? With a socket.close()
the server cannot tell the client, "Strange. You are done sending data to me, but I didn't get all the data", since the client connection is not left open after the client is done sending.
然而,这引入了一个问题。如果字节数错误或结束标记永远不会到达怎么办?随着socket.close()
服务器不能告诉客户,“奇怪的。你完成了将数据发送给我,但我没有得到所有数据”,因为客户端完成发送后,客户端连接不悬空。
With a socket.shutdown(1
) the client can still be told by the server that something was wrong and take appropriate measures.
使用 a socket.shutdown(1
) 服务器仍然可以告诉客户端出现问题并采取适当的措施。
The shutdown command has three options: 0 = done receiving, 1 = done sending, 2 = both
关机命令有三个选项:0 = done receiving, 1 = done sending, 2 = both
In the code above focuses on 1, to get rid of the implict send in a close operation. Notice how in send_data the close operation is (relatively) far away from the shutdown. This allows the server to tell the client any parting comment.
上面代码中的重点是1,在关闭操作中去掉隐式发送。注意在 send_data 中关闭操作(相对)远离关闭操作。这允许服务器告诉客户端任何离别评论。
Just run the code to start the server. The server is set to recv only 2 bytes at a time for demonstration purposes (it should be something like 8192). To send data to it import it (call it shut_srv or whatever) and call send_data for the client side.
只需运行代码即可启动服务器。出于演示目的,服务器被设置为一次仅接收2 个字节(它应该类似于 8192)。要将数据发送给它,请导入它(称其为 shutdown_srv 或其他)并为客户端调用 send_data。
data=('a1234','b1234','c1234','d1234','e1234') for d in data: print shut_srv.send_data(d)
You will get a response like: connected sent a1234 shutdown ERROR:no end! connected sent b1234 shutdown ERROR:no end! connected sent c1234 shutdown ERROR:no end! connected sent d1234 shutdown ERROR:no end! connected sent e1234 shutdown ERROR:no end!
你会得到这样的回应: connected sent a1234 shutdown ERROR:no end! connected sent b1234 shutdown ERROR:no end! connected sent c1234 shutdown ERROR:no end! connected sent d1234 shutdown ERROR:no end! connected sent e1234 shutdown ERROR:no end!
If you make the markers the same. The response should be: connected sent a123456789 shutdown a1234a1234 connected sent b1234 shutdown b1234b1234 connected sent c1234 shutdown c1234c1234 connected sent d1234 shutdown d1234d1234 connected sent e1234 shutdown e1234e1234
如果你使标记相同。响应应该是:connected sent a123456789 shutdown a1234a1234 connected sent b1234 shutdown b1234b1234 connected sent c1234 shutdown c1234c1234 connected sent d1234 shutdown d1234d1234 connected sent e1234 shutdown e1234e1234
回答by Idan Rotbart
I had the same problem, try this server:
我有同样的问题,试试这个服务器:
import socket
import select
open_client_sockets = []
server_socket = socket.socket()
server_socket.bind(('0.0.0.0', 8001))
server_socket.listen(1)
(new_socket1, address1) = server_socket.accept()
open_client_sockets.append(new_socket1)
while True:
rlist, wlist, xlist = select.select(open_client_sockets, open_client_sockets, [])
for current_socket in rlist:
data = current_socket.recv(4096)
if data != '':
print "given data: ", str(data)