I have async socket server file and client file.
When i send something like that "download filename.ex" to the client, this client's code hande my request:
try:
content = read(sp_data[-1]).decode('utf-8')
print(content)
msg = json.dumps({'file': sp_data[-1], 'command': data, 'content': content,
'msg': f'[+] File {sp_data[-1]} has been successfully downloaded.'}).encode('utf-8')
except FileNotFoundError:
msg = json.dumps({'msg': f'[-] File {sp_data[-1]} not found', 'command': data}).encode('utf-8')
s.send(msg)
When client send some data to the socketserver, this server's code handle received message:
def recv_message(client_socket):
global messages
data = json.loads(client_socket.recv(4096).decode('utf-8').strip()) ##Important here i got this error json.decoder.JSONDecodeError: Unterminated string starting at: line 1 column 67 (char 66)
raddr = get_raddr(str(client_socket))
raddr = f'{raddr[0]}:{raddr[1]}'
message = f'From: {raddr}\nCommand: {data["command"]}\nOutput: \n\n{data["msg"]}'
try:
d = messages[raddr]
d.append(message)
messages[raddr] = d
except KeyError:
messages[raddr] = [message]
except AttributeError:
print(message, messages)
if 'content' in data.keys(): ##Important
print(data['content'])
threading.Thread(target=create_file, args=(data['file'], data['content'],), daemon=False).start()
Error:
data = json.loads(client_socket.recv(4096).decode('utf-8').strip())
json.decoder.JSONDecodeError: Unterminated string starting at: line 1 column 67 (char 66)
But server's code above give me this error when it receive message from the first code(when i send something like that "download file.ex" to the client, client detect my command as its special command, execute the first code, send json file to the server. But if i send "dir" command to the client, it will detect my command like shell command, will run command through subprocess, will send result to the server back and i won't get any errors.)
Note: I also reduced socketserver's code. Therefore, something in my code can work worse. The main goal of this post - make download feature works. I also understand that my code is big. I left "##Important" comments in my files. U can watch only code that located by these comments.
Server:
import selectors
import socket
import threading
import json
import base64
import shlex
selector = selectors.DefaultSelector()
connections = {}
def accept_conn(server_socket):
sock, addr = server_socket.accept()
connections[len(connections) + 1] = [sock, f'{addr[0]}:{addr[-1]}']
selector.register(fileobj=sock, events=selectors.EVENT_READ, data=recv_message)
s = socket.socket()
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind(('localhost', 4444))
s.listen()
selector.register(fileobj=s, events=selectors.EVENT_READ, data=accept_conn)
messages = {}
##Important
def create_file(file, content): #content - base64 string
print(content)
with open(file, 'wb') as f:
f.write(base64.b64decode(content.encode('utf-8')))
def recv_message(client_socket):
global messages
data = json.loads(client_socket.recv(4096).decode('utf-8').strip()) ##Important here i got this error json.decoder.JSONDecodeError: Unterminated string starting at: line 1 column 67 (char 66)
raddr = get_raddr(str(client_socket))
raddr = f'{raddr[0]}:{raddr[1]}'
message = f'From: {raddr}\nCommand: {data["command"]}\nOutput: \n\n{data["msg"]}'
try:
d = messages[raddr]
d.append(message)
messages[raddr] = d
except KeyError:
messages[raddr] = [message]
except AttributeError:
print(message, messages)
if 'content' in data.keys(): ##Important
print(data['content'])
threading.Thread(target=create_file, args=(data['file'], data['content'],), daemon=False).start()
def get_raddr(string):
'''Get raddr parameter from client socket'''
raddr = string.replace('>', '')
return eval(raddr[raddr.find('raddr')::].replace('raddr=', ''))
def is_manage_string(sub, string):
tokens = shlex.split(string)
try:
if len(tokens) == 2 and tokens[0] == sub and str(int(tokens[-1])):
return True, int(tokens[-1])
except Exception as e:
print(e)
return False
manage_process = False
def manage():
global manage_process
while True:
manage_process = False
command = input('>>> ').strip()
if command == 'list':
try:
for i in range(1, len(connections) + 1):
print(f'{i}\t{connections[i][-1]}')
except KeyError:
pass
if len(connections) == 0:
print('[-] There are not any connections')
elif 'manage' in command:
index = is_manage_string('manage', command)
if index:
index = index[-1]
else:
print('[-] Invalid command\nUse manage "number_of_connection"\nEx: manage 1')
continue
if index >= 1 and index <= len(connections):
sock, addr = connections[index]
print(addr)
print(f'{addr} is used')
while True: ##Important here i launch loop which send data to socket
manage_process = True
command = input('>>> ').strip()
if command == 'messages':
try:
if messages[addr] == list():
print()
continue
except KeyError:
pass
try:
print('\n\n'.join(messages[addr]))
except KeyError:
print()
elif command == 'message':
try:
print(messages[addr][-1])
except:
print()
elif command == 'clear_messages':
try:
if messages[addr]:
messages[addr] = []
except KeyError:
print('[-] There are not any messages for cleaning up')
elif command == 'leave':
print(f'Leaving connection {addr}')
break
elif command: ##Important if command hasn't been detected as my special command(leave, messages), it will be executed like shell command
try:
sock.send(command.encode('utf-8'))
print(
'Your input has not been detected as special command and will execute like shell command or like client special command(ex: download; see client file)')
except ConnectionResetError:
print("Connection has been lost, therefore shell commands can't be used")
else:
continue
else:
print('[-] Invalid number of connection')
elif command:
print('[-] Invalid command\nType "help" to see avalible commands')
##Important
def event_loop():
while True:
data = selector.select()
for key, _ in data:
try:
key.data(key.fileobj)
except ConnectionResetError:
selector.unregister(key.fileobj)
##Important
threading.Thread(target=manage, daemon=True).start()
event_loop()
Client:
import socket
import subprocess
import shlex
import threading
import json
import base64
s = socket.socket()
s.connect(('localhost', 4444))
##Important
def read(file):
with open(file, 'rb') as f:
return base64.b64encode(f.read())
def runner(data):
sp_data = shlex.split(data)
try:
print(sp_data)
if len(sp_data) == 2 and sp_data[0] == 'download': ###Important here we create json object which will be send to socketserver
try:
content = read(sp_data[-1]).decode('utf-8')
print(content)
msg = json.dumps({'file': sp_data[-1], 'command': data, 'content': content,
'msg': f'[+] File {sp_data[-1]} has been successfully downloaded.'}).encode('utf-8')
except FileNotFoundError:
msg = json.dumps({'msg': f'[-] File {sp_data[-1]} not found', 'command': data}).encode('utf-8')
s.send(msg)
return ''
except Exception as e:
print(e)
command = subprocess.run(data, shell=True, encoding='cp866', text=True, capture_output=True)
command = command.stderr if command.stderr else command.stdout
command = json.dumps({'msg': command, 'command': data})
s.send(command.encode('utf-8'))
while True:##Important
data = s.recv(4096).decode('utf-8').strip()
threading.Thread(target=runner, args=(data,)).start()
import socket
import struct
class Socket(socket.socket):
def __init__(self):
self.sock = socket.socket()
super().__init__(socket.AF_INET, socket.SOCK_STREAM)
def send_msg(self, msg):
# Prefix each message with a 4-byte length (network byte order)
msg = struct.pack('>I', len(msg)) + msg
self.sock.sendall(msg)
def recv_msg(self):
# Read message length and unpack it into an integer
raw_msglen = self.recv_all(4)
if not raw_msglen:
return None
msglen = struct.unpack('>I', raw_msglen)[0]
# Read the message data
return self.recv_all(msglen)
def recv_all(self, n):
data = bytearray()
while len(data) < n:
packet = self.sock.recv(n - len(data))
if not packet:
return None
data.extend(packet)
return data
I reshaped ur code into Socket class.
Solution - usage these features:
def send_msg(sock, msg):
# Prefix each message with a 4-byte length (network byte order)
msg = struct.pack('>I', len(msg)) + msg
sock.sendall(msg)
def recv_msg(sock):
# Read message length and unpack it into an integer
raw_msglen = recvall(sock, 4)
if not raw_msglen:
return None
msglen = struct.unpack('>I', raw_msglen)[0]
# Read the message data
return recvall(sock, msglen)
def recvall(sock, n):
# Helper function to recv n bytes or return None if EOF is hit
data = bytearray()
while len(data) < n:
packet = sock.recv(n - len(data))
if not packet:
return None
data.extend(packet)
return data
Related
Why am I getting the error argument for 's' must be a bytes object when trying to run the lambda function? I'm following the usage example but I'm getting this error. Any explanation to this issue and how to resolve it?
{
"errorMessage": "Failed sending data.\nERROR: argument for 's' must be a bytes object",
"errorType": "Exception",
"stackTrace": [
" File \"/var/task/AlertMetricSender.py\", line 5, in lambda_handler\n sender.send()\n",
" File \"/var/task/modules/ZabbixSender.py\", line 91, in send\n self.__active_checks()\n",
" File \"/var/task/modules/ZabbixSender.py\", line 79, in __active_checks\n response = self.__request(request)\n",
" File \"/var/task/modules/ZabbixSender.py\", line 59, in __request\n raise Exception(\"Failed sending data.\\nERROR: %s\" % e)\n"
]
}
ZabbixSender.py:
#
# For sending metric value to zabbix server.
#
# You must create item as "zabbix trapper" on server.
# Because the server must be connected to agent:10050, if it is selected "zabbix agent".
#
# Usage:
# from modules.ZabbixSender import ZabbixSender
# ZABBIX_HOST = "zabbix.example.com"
# ZABBIX_PORT = 10051
# sender = ZabbixSender(ZABBIX_HOST, ZABBIX_PORT)
# sender.add("example-hostname-01", "healthcheck", 1)
# sender.add("example-hostname-01", "item.keyname", 0.123)
# sender.add("example-hostname-02", "item.keyname", 1234)
# sender.send()
#
import socket
import struct
import time
import json
class ZabbixSender:
log = True
def __init__(self, host='127.0.0.1', port=10051):
self.address = (host, port)
self.data = []
def __log(self, log):
if self.log: print(log)
def __connect(self):
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
self.sock.connect(self.address)
except:
raise Exception("Can't connect server.")
def __close(self):
self.sock.close()
def __pack(self, request):
string = json.dumps(request)
header = struct.pack('<4sBQ', 'ZBXD', 1, len(string))
return header + string
def __unpack(self, response):
header, version, length = struct.unpack('<4sBQ', response[:13])
(data, ) = struct.unpack('<%ds'%length, response[13:13+length])
return json.loads(data)
def __request(self, request):
self.__connect()
try:
self.sock.sendall(self.__pack(request))
except Exception as e:
raise Exception("Failed sending data.\nERROR: %s" % e)
response = ''
while True:
data = self.sock.recv(4096)
if not data:
break
response += data
self.__close()
return self.__unpack(response)
def __active_checks(self):
hosts = set()
for d in self.data:
hosts.add(d['host'])
for h in hosts:
request = {"request":"active checks", "host":h}
self.__log("[active check] %s" % h)
response = self.__request(request)
if not response['response'] == 'success': self.__log("[host not found] %s" % h)
def add(self, host, key, value, clock=None):
if clock is None: clock = int(time.time())
self.data.append({"host":host, "key":key, "value":value, "clock":clock})
def send(self):
if not self.data:
self.__log("Not found sender data, end without sending.")
return False
self.__active_checks()
request = {"request":"sender data", "data":self.data}
response = self.__request(request)
result = True if response['response'] == 'success' else False
if result:
for d in self.data:
self.__log("[send data] %s" % d)
self.__log("[send result] %s" % response['info'])
else:
raise Exception("Failed send data.")
return result
if __name__ == '__main__':
sender = ZabbixSender()
sender.add("gedowfather-example-01", "healthcheck", 1)
sender.add("gedowfather-example-01", "gedow.item", 1111)
sender.send()
AlertMetricSender.py:
from modules.ZabbixSender import ZabbixSender
def lambda_handler(event, context):
sender = ZabbixSender("10.10.10.10", 10051)
sender.add("Zabbix server", "lambda.test", 5)
sender.send()
The error is coming from struct.pack. You're not seeing that, because of your blanket try/except.
All socket activity is done in byte strings, not Unicode strings. You need this:
def __pack(self, request):
string = json.dumps(request).encode('utf-8')
header = b'ZBXD' + struct.pack('<BQ', 1, len(string))
return header + string
One subtle thing about this: You must convert to a bytes string BEFORE you do len(string). It's quite possible for the conversion to UTF-8 to increase the number of characters in the string.
AND I absolutely need to comment on this:
result = True if response['response'] == 'success' else False
What led you to write that? This is exactly the same as the much more natural reading:
result = response['response'] == 'success'
I'm trying to develop a very simple client/server program, the server part is working properly but I've a problem with the client part but I can't understand why.
The client's work is very simple, just retrive the counter value from a external device, then I'm trying to send the retrieved data to the server part.
At the beginning the socket is working well, but some time when I should send the data I've got the server exception and after that the Client is not working.
I can't understand if the s.close() function is working properly.
UPDATE: the exception that I got is "errno 110 connection timed out"
Client:
import time, socket, struct, array, json
import Client_Axis
import sys
import serial
import os
import datetime
import re
import packet
import Config_mio
usbport = '/dev/ttyAMA0'
h = "/r/n"
if __name__=="__main__":
"""Main function that starts the server"""
curr_value = "0000000000"
prev_value = ""
line = '111111111111111'
error_counter = 0
people_in = 0
people_out = 0
txtDate = ""
no_updates_counter = 0
while True:
ser = None
try:
# print('1')
ser = serial.Serial('/dev/ttyAMA0', 9600, timeout=1)
# ser.open()
# print('2')
for line in ser.read():
line = ser.readline()
print(line[6:10])
curr_value = line
except:
print('Serial error')
# print('3')
pass
finally:
if ser:
ser.close()
try:
error_counter += 1
# print('1')
response = Client_Axis.Read_Axis_Camera_Occupancy()
content = response.split(",")
people_in_refresh = int(re.search(r'\d+', content[3]).group())
people_out_refresh = int(re.search(r'\d+', content[4]).group())
# print('2')
error_flag = 0
if people_in != people_in_refresh or people_out != people_out_refresh:
people_in = people_in_refresh
people_out = people_out_refresh
try:
# Creates TCP socket in the specified IP address and port
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# Connect client to the server
s.connect((Config_mio.IP_Server_Add, Config_mio.ws_port))
# Create message and send it to server
timestamp = str(time.time())
# msg = packet("c", timestamp, Config_mio.RbPi_Id, content[3], content[4], None)
msg = "c"+","+str(timestamp)+","+str(Config_mio.RbPi_Id)+","+str(people_in)+","+str(people_out)+","+"None"
# json_message = json.dumps(msg)
# s.send(json_message)
s.send(msg)
print "messaggio INVIATO"
# Close connection when data is sent
#s.close()
except:
print('Server connection error 1')
pass
finally:
s.close()
#AXIS_occup_old = AXIS_occup
#AXIS_occup = response.read()
#my_dict = json.loads(AXIS_occup)
# print(AXIS_occup)
# print(my_dict)
#del my_dict["timestamp"]
#AXIS_occup = my_dict
#error_counter = 0
# print('3')
except:
error_msg = "Error retrieving occupancy from: " + Config_mio.IP_AXIS_Add
error_flag = 1
if (error_flag == 1):
no_updates_counter = 0
print "Error detected: %s \r\n" % error_msg
print error_counter
if (error_counter > 200):
os.system("sudo reboot")
elif (line[6:10] != '1111' and prev_value != curr_value):
try:
# Creates TCP socket in the specified IP address and port
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# Connect client to the server
s.connect((Config_mio.IP_Server_Add, Config_mio.ws_port))
# Create message and send it to server
timestamp = str(time.time())
msg = "r"+","+str(timestamp)+","+str(Config_mio.RbPi_Id)+","+"None"+","+"None"+","+str(line[6:10])
#msg = {"Id": raspberry_id,
# "Ranging": line[6:10],
# "timestamp": timestamp}
#json_message = json.dumps(msg)
#s.send(json_message)
s.send(msg)
print "Message : %s" % msg
# Close connection when data is sent
s.close()
except:
print('Server connection error 2')
pass
else:
no_updates_counter += 1
# Send message despite there are no changes in value
# This is a heartbeat message of 10 min
if (no_updates_counter > 200):
no_updates_counter = 0
AXIS_occup = ""
prev_value = curr_value
# Reboot device every day - routine
# We have 4 cases incase we miss few seconds
txtDate = str(datetime.datetime.fromtimestamp(time.time()))
if (txtDate[11:19] == "00:00:00"):
os.system("sudo reboot")
if (txtDate[11:19] == "00:00:01"):
os.system("sudo reboot")
if (txtDate[11:19] == "00:00:02"):
os.system("sudo reboot")
if (txtDate[11:19] == "00:00:03"):
os.system("sudo reboot")
# Kill time - 1 sec: Remove it for high speed localisation
time.sleep(1)
Server:
import socket
import json
import time
import Config_mio
import packet
import sqlite3 as lite
if __name__ == "__main__":
"""Main function that starts the server"""
# Creates TCP socket in the specified IP address and port
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind((Config_mio.IP_Server_Add, Config_mio.ws_port))
# Starts server, up to 10 clients are queued
s.listen(Config_mio.Max_Num_Clients)
while True:
conn, addr = s.accept()
#print "sono dopo ascolto"
msg = conn.recv(1024)
print "Data value:",msg
msg = msg.split(",")
if msg[0] == "c":
print "counter msg"
elif msg[0] == "r":
print "range msg",msg[1],msg[2],msg[5]
conn.close()
I started to code in python with sockets and I have a little problem for my chat script.
Server script
import pickle, socket, struct, sys, threading
SERVERADDRESS = ("localhost", 6030)
class helloChatServer(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
self.__server = socket.socket()
self.users = []
try:
self.__server.bind(SERVERADDRESS)
except socket.error:
print('Bind failed {}'.format(socket.error))
self.__server.listen(10)
def exit(self):
self.__server.close()
def run(self):
print( "Listening... {}".format(SERVERADDRESS))
while True:
client, addr = self.__server.accept()
try:
threading.Thread(target=self._handle, args=(client, addr)).start()
except OSError:
print('Error during processing the message')
def _handle(self, client, addr):
print('Client connected with {}:{}'.format(addr[0], str(addr[1])))
self.users.append(addr)
while True:
data = client.recv(1024)
print(data)
client.send(data)
client.close()
if __name__ == '__main__':
helloChatServer().run()
Client script
import pickle, socket, struct, sys, threading
SERVERADDRESS = (socket.gethostname(), 6050)
class helloChatClient():
def __init__(self, host='localhost', port=5000, pseudo="Visitor"):
self.__socket = socket.socket()
self.__socket.bind((host, port))
self.__pseudo = pseudo
print('Listening on {}:{}'.format(host, port))
def run(self):
handlers = {
'/exit': self._exit,
'/quit': self._quit,
'/join': self._join,
'/send': self._send
}
self.__running = True
self.__address = None
threading.Thread(target=self._receive).start()
while self.__running:
line = sys.stdin.readline().rstrip() + ' '
# Extract the command and the param
command = line[:line.index(' ')]
param = line[line.index(' ')+1:].rstrip()
# Call the command handler
if command in handlers:
try:
handlers[command]() if param == '' else handlers[command](param)
except:
print("Error during the execution of the message")
else:
print('Command inconnue:', command)
def _exit(self):
self.__running = False
self.__address = None
self.__socket.close()
def _quit(self):
self.__address = None
def _join(self, param):
if self.__pseudo == "Visitor":
self.__pseudo = input("Choose a username: ")
tokens = param.split(' ')
if len(tokens) == 2:
try:
self.__address = (tokens[0], int(tokens[1]))
self.__socket.connect(self.__address)
print('~~~~~~~~~~~~~~~~~~~~~~~~~~')
print('Connected at {}:{}'.format(*self.__address))
print('~~~~~~~~~~~~~~~~~~~~~~~~~~')
except OSError:
print("Error during the sending of the message")
self.__socket.send(self.__pseudo.encode())
def _send(self, param):
if self.__address is not None:
try:
message = param.encode()
totalsent = 0
while totalsent < len(message):
sent = self.__socket.send(message[totalsent:])
totalsent += sent
print(self.__socket.recv(1024).decode())
except OSError:
print('Error during the reception of the message')
def _receive(self):
while self.__running:
try:
data = self.__socket.recv(1024).decode()
print(data)
except socket.timeout:
pass
except OSError:
return
if __name__ == '__main__':
if len(sys.argv) == 4:
helloChatClient(sys.argv[1], int(sys.argv[2]), sys.argv[3]).run()
else:
helloChatClient().run()
Well when I run the script on the terminal, I see this.
Server
MacBook-Pro-de-Saikou:labo2 saikouah$ python3.4 helloChatServer.py
En écoute sur... ('MacBook-Pro-de-Saikou.local', 6030)
Client connected with 127.0.0.1:5004
Il y a actuellement 1 connecté
b'bluebeel'
b'hello'
Client
MacBook-Pro-de-Saikou:labo2 saikouah$ python3.4 helloChatClient.py localhost 5004 bluebeel
Écoute sur localhost:5004
/join MacBook-Pro-de-Saikou.local 6030
~~~~~~~~~~~~~~~~~~~~~~~~~~
Connecté à MacBook-Pro-de-Saikou.local:6030
~~~~~~~~~~~~~~~~~~~~~~~~~~
/send hello
bluebeel
On the client terminal he doesn't print me hello but bluebeel. I made several test and he took me every time the previous one message. Looks like he is late.
Someone can help me? :)
PROBLEM ANALYSIS
Your code fails in _receive function:
data = self.__socket.recv(1024).decode()
This line throws OSError because you try to call .recv before connecting to the server. Thus the exception handler is fired and the function exits. So what happens is that after calling
threading.Thread(target=self._receive).start()
function _receive exits before you call /join. So watch what happens
You call /join.
bluebeel is send to the server
Server receives it and sends it back to the client
But _receive function is no longer there. So the message is "stacked" on the socket (it will wait for next .recv() call)
You call /send hello
Server receives hello and sends it back
Client calls print(self.__socket.recv(1024).decode()) in _send method
But .recv retrieves the first message that is stacked on the socket. In that case it is not hello, it is bluebeel.
Now this schema continues to work. You send message, server pings it back but there's always 1 message in front of the received one. The "late" message.
SOLUTION
One way of solving this issue is to call
threading.Thread(target=self._receive).start()
in ._join method after .connect. Remember to remove print(self.__socket.recv(1024).decode()) from _send method, otherwise it will block stdin.
Of course you will have problems when issuing multiple /join commands. To properly address that you would have to keep track of _receive thread and kill it at the begining of ._join method. This however is beyond the scope of this question IMHO.
SIDE NOTE
Don't ever handle exceptions like you did. This is wrong:
try:
data = self.__socket.recv(1024).decode()
print(data)
except socket.timeout:
pass
except OSError:
return
At least do this:
import traceback
try:
data = self.__socket.recv(1024).decode()
print(data)
except socket.timeout:
traceback.print_exc()
except OSError:
traceback.print_exc()
return
This is the code that I have used.But I don't get actual result that I want.When I execute code ChatServer file works properly,but ChatClient gives only one line(Usage : python telnet.py hostname port).Please Help me.I am new in python.
The server code:
#!/usr/bin/env python
#!/usr/bin/env python
"""
A basic, multiclient 'chat server' using Python's select module
with interrupt handling.
Entering any line of input at the terminal will exit the server.
"""
import select
import socket
import sys
import signal
from communication import send, receive
BUFSIZ = 1024
class ChatServer(object):
""" Simple chat server using select """
def __init__(self, port=3490, backlog=5):
self.clients = 0
# Client map
self.clientmap = {}
# Output socket list
self.outputs = []
self.server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
self.server.bind(('',port))
print 'Listening to port',port,'...'
self.server.listen(backlog)
# Trap keyboard interrupts
signal.signal(signal.SIGINT, self.sighandler)
def sighandler(self, signum, frame):
# Close the server
print 'Shutting down server...'
# Close existing client sockets
for o in self.outputs:
o.close()
self.server.close()
def getname(self, client):
# Return the printable name of the
# client, given its socket...
info = self.clientmap[client]
host, name = info[0][0], info[1]
return '#'.join((name, host))
def serve(self):
inputs = [self.server,sys.stdin]
self.outputs = []
running = 1
while running:
try:
inputready,outputready,exceptready = select.select(inputs, self.outputs, [])
except select.error, e:
break
except socket.error, e:
break
for s in inputready:
if s == self.server:
# handle the server socket
client, address = self.server.accept()
print 'chatserver: got connection %d from %s' % (client.fileno(), address)
# Read the login name
cname = receive(client).split('NAME: ')[1]
# Compute client name and send back
self.clients += 1
send(client, 'CLIENT: ' + str(address[0]))
inputs.append(client)
self.clientmap[client] = (address, cname)
# Send joining information to other clients
msg = '\n(Connected: New client (%d) from %s)' % (self.clients, self.getname(client))
for o in self.outputs:
# o.send(msg)
send(o, msg)
self.outputs.append(client)
elif s == sys.stdin:
# handle standard input
junk = sys.stdin.readline()
running = 0
else:
# handle all other sockets
try:
# data = s.recv(BUFSIZ)
data = receive(s)
if data:
# Send as new client's message...
msg = '\n#[' + self.getname(s) + ']>> ' + data
# Send data to all except ourselves
for o in self.outputs:
if o != s:
# o.send(msg)
send(o, msg)
else:
print 'chatserver: %d hung up' % s.fileno()
self.clients -= 1
s.close()
inputs.remove(s)
self.outputs.remove(s)
# Send client leaving information to others
msg = '\n(Hung up: Client from %s)' % self.getname(s)
for o in self.outputs:
# o.send(msg)
send(o, msg)
except socket.error, e:
# Remove
inputs.remove(s)
self.outputs.remove(s)
self.server.close()
if __name__ == "__main__":
ChatServer().serve()
The chat client:
#! /usr/bin/env python
"""
Simple chat client for the chat server. Defines
a simple protocol to be used with chatserver.
"""
import socket
import sys
import select
from communication import send, receive
BUFSIZ = 1024
class ChatClient(object):
""" A simple command line chat client using select """
def __init__(self, name, host='127.0.0.1', port=3490):
self.name = name
# Quit flag
self.flag = False
self.port = int(port)
self.host = host
# Initial prompt
self.prompt='[' + '#'.join((name, socket.gethostname().split('.')[0])) + ']> '
# Connect to server at port
try:
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.sock.connect((host, self.port))
print 'Connected to chat server#%d' % self.port
# Send my name...
send(self.sock,'NAME: ' + self.name)
data = receive(self.sock)
# Contains client address, set it
addr = data.split('CLIENT: ')[1]
self.prompt = '[' + '#'.join((self.name, addr)) + ']> '
except socket.error, e:
print 'Could not connect to chat server #%d' % self.port
sys.exit(1)
def cmdloop(self):
while not self.flag:
try:
sys.stdout.write(self.prompt)
sys.stdout.flush()
# Wait for input from stdin & socket
inputready, outputready,exceptrdy = select.select([0, self.sock], [],[])
for i in inputready:
if i == 0:
data = sys.stdin.readline().strip()
if data: send(self.sock, data)
elif i == self.sock:
data = receive(self.sock)
if not data:
print 'Shutting down.'
self.flag = True
break
else:
sys.stdout.write(data + '\n')
sys.stdout.flush()
except KeyboardInterrupt:
print 'Interrupted.'
self.sock.close()
break
if __name__ == "__main__":
import sys
if len(sys.argv)<3:
sys.exit('Usage: %s chatid host portno' % sys.argv[0])
client = ChatClient(sys.argv[1],sys.argv[2], int(sys.argv[3]))
client.cmdloop()
###############################################################################
# The communication module (communication.py)
###############################################################################
import cPickle
import socket
import struct
marshall = cPickle.dumps
unmarshall = cPickle.loads
def send(channel, *args):
buf = marshall(args)
value = socket.htonl(len(buf))
size = struct.pack("L",value)
channel.send(size)
channel.send(buf)
def receive(channel):
size = struct.calcsize("L")
size = channel.recv(size)
try:
size = socket.ntohl(struct.unpack("L", size)[0])
except struct.error, e:
return ''
buf = ""
while len(buf) < size:
buf = channel.recv(size - len(buf))
return unmarshall(buf)[0]
I'm trying to write a client-server program where server receives the requests for a database records, or files, and sends it back. Everything was working just fine until I used the pickle function to send data from client to server,
Exception in thread Thread-1:
Traceback (most recent call last):
File "/usr/lib/python2.7/threading.py", line 551, in __bootstrap_inner
self.run()
File "server.py", line 71, in run
data = pickle.loads(data)
File "/usr/lib/python2.7/pickle.py", line 1381, in loads
file = StringIO(str)
TypeError: expected read buffer, NoneType found
When I send data from server to client, there is no problem. I worked like this for a few weeks but when there is about 50 exceptions, the server program closes.
client.py file:
import socket
import sys
import time
import pickle
import struct
def recvall(sock, n):
# Helper function to recv n bytes or return None if EOF is hit
data = ''
while len(data) < n:
packet = sock.recv(n - len(data))
if not packet:
return None
data += packet
return data
def recv_msg(sock):
# Read message length and unpack it into an integer
raw_msglen = recvall(sock, 4)
if not raw_msglen:
return None
msglen = struct.unpack('>I', raw_msglen)[0]
# Read the message data
return recvall(sock, msglen)
def sending(msg):
host = 'localhost'
port = 50000
size = 1024
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((host,port))
lenght = len(msg)
if lenght>0:
msg = pickle.dumps(msg)
msg = struct.pack('>I', len(msg)) + msg
s.sendall(msg)
print 'sended string lenght: '+str(lenght)
else:
s.send('nothing sended')
data = recv_msg(s)
lenght2 = len(data)
print data
print 'received string lenght: '+str(lenght2)
#sys.stdout.write(data)
s.close()
while 1:
msg = raw_input('Input:')
sending(msg)
server.py:
class Client(threading.Thread):
def __init__(self,(client,address)):
threading.Thread.__init__(self)
self.client = client
self.address = address
self.size = 1024
def run(self):
running = 1
while running:
sock = self.client
data = self.recv_msg(sock)
data = pickle.loads(data)
if data:
msg = struct.pack('>I', len(data)) + data
self.client.sendall(msg)
else:
self.client.close()
running = 0
def recv_msg(self, sock):
# Read message length and unpack it into an integer
raw_msglen = self.recvall(sock, 4)
if not raw_msglen:
return None
msglen = struct.unpack('>I', raw_msglen)[0]
# Read the message data
return self.recvall(sock, msglen)
def recvall(self, sock, n):
# Helper function to recv n bytes or return None if EOF is hit
data = ''
while len(data) < n:
packet = sock.recv(n - len(data))
if not packet:
return None
data += packet
return data
In previous version I puted the pickle function from server to client, and it worked fine, no problems, but now I started to write it from the beginning to find the problem, but I didn't.
The recv_msg method was found here:
Python Socket Receive Large Amount of Data
The recv_msg method returns None when EOF is reached, and you pass that None to pickle.loads, which is an error.
To fix the problem, place the call to pickle.loads() after the EOF-check:
data = self.recv_msg(sock)
if data is not None:
data = pickle.loads(data)
msg = struct.pack('>I', len(data)) + data
self.client.sendall(msg)
else:
self.client.close()
running = 0