├── .gitignore ├── README.md └── jsonsocket.py /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | 5 | # C extensions 6 | *.so 7 | 8 | # Distribution / packaging 9 | .Python 10 | env/ 11 | bin/ 12 | build/ 13 | develop-eggs/ 14 | dist/ 15 | eggs/ 16 | lib/ 17 | lib64/ 18 | parts/ 19 | sdist/ 20 | var/ 21 | *.egg-info/ 22 | .installed.cfg 23 | *.egg 24 | 25 | # Installer logs 26 | pip-log.txt 27 | pip-delete-this-directory.txt 28 | 29 | # Unit test / coverage reports 30 | htmlcov/ 31 | .tox/ 32 | .coverage 33 | .cache 34 | nosetests.xml 35 | coverage.xml 36 | 37 | # Translations 38 | *.mo 39 | 40 | # Mr Developer 41 | .mr.developer.cfg 42 | .project 43 | .pydevproject 44 | 45 | # Rope 46 | .ropeproject 47 | 48 | # Django stuff: 49 | *.log 50 | *.pot 51 | 52 | # Sphinx documentation 53 | docs/_build/ 54 | 55 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | jsonsocket 2 | ========== 3 | 4 | This is a small Python library for sending data over sockets. 5 | 6 | It allows sending lists, dictionaries, strings, etc. It can handle very large data (I've tested it with 10GB of data). Any JSON-serializable data is accepted. 7 | 8 | Examples: 9 | 10 | ```python 11 | from jsonsocket import Client, Server 12 | 13 | host = 'localhost' 14 | port = '8000' 15 | 16 | # Client code: 17 | client = Client() 18 | client.connect(host, port).send({'some_list': [123, 456]}) 19 | response = client.recv() 20 | # response now is {'data': {'some_list': [123, 456]}} 21 | client.close() 22 | 23 | 24 | # Server code: 25 | server = Server(host, port) 26 | server.accept() 27 | data = server.recv() 28 | # data now is: {'some_list': [123, 456]} 29 | server.send({'data': data}).close() 30 | 31 | ``` 32 | -------------------------------------------------------------------------------- /jsonsocket.py: -------------------------------------------------------------------------------- 1 | import json, socket 2 | 3 | class Server(object): 4 | """ 5 | A JSON socket server used to communicate with a JSON socket client. All the 6 | data is serialized in JSON. How to use it: 7 | 8 | server = Server(host, port) 9 | while True: 10 | server.accept() 11 | data = server.recv() 12 | # shortcut: data = server.accept().recv() 13 | server.send({'status': 'ok'}) 14 | """ 15 | 16 | backlog = 5 17 | client = None 18 | 19 | def __init__(self, host, port): 20 | self.socket = socket.socket() 21 | self.socket.bind((host, port)) 22 | self.socket.listen(self.backlog) 23 | 24 | def __del__(self): 25 | self.close() 26 | 27 | def accept(self): 28 | # if a client is already connected, disconnect it 29 | if self.client: 30 | self.client.close() 31 | self.client, self.client_addr = self.socket.accept() 32 | return self 33 | 34 | def send(self, data): 35 | if not self.client: 36 | raise Exception('Cannot send data, no client is connected') 37 | _send(self.client, data) 38 | return self 39 | 40 | def recv(self): 41 | if not self.client: 42 | raise Exception('Cannot receive data, no client is connected') 43 | return _recv(self.client) 44 | 45 | def close(self): 46 | if self.client: 47 | self.client.close() 48 | self.client = None 49 | if self.socket: 50 | self.socket.close() 51 | self.socket = None 52 | 53 | 54 | class Client(object): 55 | """ 56 | A JSON socket client used to communicate with a JSON socket server. All the 57 | data is serialized in JSON. How to use it: 58 | 59 | data = { 60 | 'name': 'Patrick Jane', 61 | 'age': 45, 62 | 'children': ['Susie', 'Mike', 'Philip'] 63 | } 64 | client = Client() 65 | client.connect(host, port) 66 | client.send(data) 67 | response = client.recv() 68 | # or in one line: 69 | response = Client().connect(host, port).send(data).recv() 70 | """ 71 | 72 | socket = None 73 | 74 | def __del__(self): 75 | self.close() 76 | 77 | def connect(self, host, port): 78 | self.socket = socket.socket() 79 | self.socket.connect((host, port)) 80 | return self 81 | 82 | def send(self, data): 83 | if not self.socket: 84 | raise Exception('You have to connect first before sending data') 85 | _send(self.socket, data) 86 | return self 87 | 88 | def recv(self): 89 | if not self.socket: 90 | raise Exception('You have to connect first before receiving data') 91 | return _recv(self.socket) 92 | 93 | def recv_and_close(self): 94 | data = self.recv() 95 | self.close() 96 | return data 97 | 98 | def close(self): 99 | if self.socket: 100 | self.socket.close() 101 | self.socket = None 102 | 103 | 104 | ## helper functions ## 105 | 106 | def _send(socket, data): 107 | try: 108 | serialized = json.dumps(data) 109 | except (TypeError, ValueError), e: 110 | raise Exception('You can only send JSON-serializable data') 111 | # send the length of the serialized data first 112 | socket.send('%d\n' % len(serialized)) 113 | # send the serialized data 114 | socket.sendall(serialized) 115 | 116 | def _recv(socket): 117 | # read the length of the data, letter by letter until we reach EOL 118 | length_str = '' 119 | char = socket.recv(1) 120 | while char != '\n': 121 | length_str += char 122 | char = socket.recv(1) 123 | total = int(length_str) 124 | # use a memoryview to receive the data chunk by chunk efficiently 125 | view = memoryview(bytearray(total)) 126 | next_offset = 0 127 | while total - next_offset > 0: 128 | recv_size = socket.recv_into(view[next_offset:], total - next_offset) 129 | next_offset += recv_size 130 | try: 131 | deserialized = json.loads(view.tobytes()) 132 | except (TypeError, ValueError), e: 133 | raise Exception('Data received was not in JSON format') 134 | return deserialized 135 | --------------------------------------------------------------------------------