I am learning python asyncio module and try to write a socks5 server with it. Python docs said:
Called when some data is received. data is a non-empty bytes object
containing the incoming data.
I wonder when client sends 2 bytes data, will data_received(self, data) just receive 1 byte not 2 bytes when it called and the rest 1 byte will call data_received(self, data) again?
#!/usr/bin/env python3
import asyncio
import logging
import socket
import struct
logging.basicConfig(level=logging.DEBUG,
format='{asctime} {levelname} {message}',
datefmt='%Y-%m-%d %H:%M:%S',
style='{')
class Remote(asyncio.Protocol):
def connection_made(self, transport):
self.transport = transport
self.server_transport = None
def data_received(self, data):
self.server_transport.write(data)
class Server(asyncio.Protocol):
INIT, REQUEST, REPLY = 0, 1, 2
def connection_made(self, transport):
client_info = transport.get_extra_info('peername')
logging.info('connect from {}'.format(client_info))
self.transport = transport
self.state = self.INIT
def data_received(self, data):
if self.state == self.INIT:
if data[0] == 5:
amount = data[1] # Authentication amount
if 0 in data[2:]:
self.transport.write(b'\x05\x00')
self.state = self.REQUEST
else:
self.eof_received()
else:
self.eof_received()
elif self.state == self.REQUEST:
ver, cmd, rsv, addr_type = data[:4]
logging.info('addr type: {}'.format(addr_type))
if addr_type == 1: # ipv4
addr = socket.inet_ntoa(data[4:8])
elif addr_type == 3:
addr_len = data[4]
addr = data[5:5+addr_len]
else:
data = b'\x05\x08\x00\x01'
data += socket.inet_aton('0.0.0.0') + struct.pack('>H', 0)
self.transport.write(data)
logging.error('not support addr type')
self.eof_received()
port = struct.unpack('>H', data[-2:])[0]
logging.info('target: {}:{}'.format(addr, port))
asyncio.ensure_future(self.remote(addr, port))
self.state = self.REPLY
elif self.state == self.REPLY:
logging.info('start relay')
self.remote_transport.write(data)
async def remote(self, addr, port):
loop = asyncio.get_event_loop()
transport, _remote = await loop.create_connection(Remote, addr, port)
_remote.server_transport = self.transport
self.remote_transport = transport
bind_addr, bind_port = transport.get_extra_info('sockname')
data = b'\x05\x00\x00\x01'
data += socket.inet_aton(bind_addr) + struct.pack('>H', bind_port)
self.transport.write(data)
if __name__ == '__main__':
loop = asyncio.get_event_loop()
server = loop.create_server(Server, '127.0.0.2', 1089)
loop.run_until_complete(server)
try:
loop.run_forever()
except KeyboardInterrupt:
server.close()
loop.run_until_complete(server.close())
loop.close()
no, data_received will receive as many bytes as are already received by the server. If you need to receive the first 3 bytes to handle the request, then you should implement some buffering in your Protocol to allow you to wait for the rest of the request to arrive before continuing.
It would typically look like this:
def __init__(self, …):
self._buffer = bytearray()
…
def data_received(self, data):
self._buffer += data
if self.state == self.INIT:
# here we need at least 3 bytes.
# if we don't have enough data yet, just wait for the next `data_received` call
if len(self._buffer) < 3:
return
header, self._buffer = self._buffer[:2], self._buffer[2:]
# parse authentication header, switch the state to REQUEST
elif self.state == self.REQUEST:
…
Related
I tried to make async socket core for my private server for game. This works, but don't accept more than 1 client. 2 clients trying to connect, but second client cannot do this, only when first will disconnect.
import asyncio, socket, time
class Server:
def __init__(self, port: int):
self.server = socket.socket()
self.port = port
async def start(self):
self.server.bind(("0.0.0.0", self.port))
self.server.listen()
print(f"Server started on {self.port}!")
while True:
session, ip = self.server.accept()
print(f"{ip[0]} connected!")
await Connection(session, ip[0]).messaging()
class Connection:
def __init__(self, session, ip):
self.session = session
self.ip = ip
def receive(self, session, lens):
data = b""
while len(data) < lens:
s = session.recv(lens)
if not s:
s += 0
break
data += s
return data
async def messaging(self):
last = int(time.time())
while True:
try:
header = self.session.recv(7)
if len(header) > 0:
last = int(time.time())
mtype = int.from_bytes(header[:2], "big")
mlen = int.from_bytes(header[2:5], "big")
mdata = self.receive(self.session, mlen)
print(f"[C] {mtype} from {self.ip}")
if int(time.time()) - last > 7:
break
except(ConnectionAbortedError, ConnectionResetError, ConnectionRefusedError, TimeoutError, OSError):
break
self.session.close()
print(f"{self.ip} disconnected!")
asyncio.run(Server(9339).start())
I tried to use module select but this has laggy disconnect.
How to put packet in socket, when I use socket.recv? I made my TCP server to client network. But in the code, I want to put packet in self.soc.recv(1024). How can I put the packet in socket?
Here's my code
#서버 코드
import threading, socket
class Room: #채팅방
def __init__(self):
self.clients = []#접속한 클라이언트를 담당하는 ChatClient 객체 저장
def addClient(self, c):#클라이언트 하나를 채팅방에 추가
self.clients.append(c)
def delClent(self, c):#클라이언트 하나를 채팅방에서 삭제
self.clients.remove(c)
def sendAllClients(self, msg):
for c in self.clients:
c.sendMsg(msg)
class ChatClient:#텔레 마케터: 클라이언트 1명이 전송한 메시지를 받고, 받은 메시지를 다시 되돌려줌
def __init__(self, id, soc, r):
self.id = id #클라이언트 id
self.soc = soc #담당 클라이언트와 1:1 통신할 소켓
self.room = r #채팅방 객체
def recvMsg(self):
while True:
data = self.soc.recv(1024)
msg = data.decode()
if msg == '/stop':
self.sendMsg(msg) # 클라이언트쪽의 리시브 쓰레드 종료하라고..
print(self.id,'님 퇴장')
break
msg = self.id+': ' + msg
self.room.sendAllClients(msg)
self.room.delClent(self)
self.room.sendAllClients(self.id+'님이 퇴장하셨습니다.')
def sendMsg(self, msg): #담당한 클라이언트 1명에게만 메시지 전송
self.soc.sendall(msg.encode(encoding='utf-8'))
def run(self):
t = threading.Thread(target=self.recvMsg, args=())
t.start()
class ServerMain:
ip = '192.168.55.232'
port = 2500
def __init__(self):
self.room = Room()
self.server_soc = None
def open(self):
self.server_soc = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.server_soc.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
self.server_soc.bind((ServerMain.ip, ServerMain.port))
self.server_soc.listen()
def run(self):
self.open()
print('채팅 서버 시작')
while True:
c_soc, addr = self.server_soc.accept()
print(addr)
msg = '사용할 id:'
c_soc.sendall(msg.encode(encoding='utf-8'))
msg = c_soc.recv(1024)
id = msg.decode()
cc = ChatClient(id, c_soc, self.room)
self.room.addClient(cc)
cc.run()
print('클라이언트', id, '채팅 시작')
def main():
server = ServerMain()
server.run()
main()
I got my packet by wireshark, and I want put packet in soket.recv(). Is any solution that I can get?
i was tried to use asyncio and threading libraries in different scripts. but threading will cause this error in high connections:
filedescriptor out of range in select()
my source that cause this error:
try:
reader, _, _ = select.select([socket_src, socket_dst], [], [], 1)
except select.error as err:
error("Select failed", err)
i tried to increase fd limit but it not fix this error.
and in asyncio library:
after run script everything is ok.
after 20 minutes of running script, users will connect to proxy after about 1 min of send request
and any user can not connect to proxy after 1 hour...
my source:
import socket
import asyncio
import aiosocks
from struct import pack, unpack
class Client(asyncio.Protocol):
def connection_made(self, transport):
self.transport = transport
self.server_transport = None
def data_received(self, data):
# print('recv:', repr(data))
self.server_transport.write(data)
def connection_lost(self, *args):
self.server_transport.close()
class Server(asyncio.Protocol):
INIT, HOST, DATA = 0, 1, 2
def connection_made(self, transport):
print('from:', transport.get_extra_info('peername'))
self.transport = transport
self.state = self.INIT
def connection_lost(self, exc):
self.transport.close()
def data_received(self, data):
# print('send:', repr(data))
if self.state == self.INIT:
assert data[0] == 0x05
self.transport.write(pack('!BB', 0x05, 0x00)) # no auth
self.state = self.HOST
elif self.state == self.HOST:
ver, cmd, rsv, atype = data[:4]
assert ver == 0x05 and cmd == 0x01
if atype == 3: # domain
length = data[4]
hostname, nxt = data[5:5+length], 5+length
elif atype == 1: # ipv4
hostname, nxt = socket.inet_ntop(socket.AF_INET, data[4:8]), 8
elif atype == 4: # ipv6
hostname, nxt = socket.inet_ntop(socket.AF_INET6, data[4:20]), 20
port = unpack('!H', data[nxt:nxt+2])[0]
print('to:', hostname, port)
asyncio.ensure_future(self.connect(hostname, port))
self.state = self.DATA
elif self.state == self.DATA:
self.client_transport.write(data)
async def connect(self, hostname, port):
loop = asyncio.get_event_loop()
socks5_addr = aiosocks.Socks5Addr('127.0.0.1', 1080)
dst = (hostname, port)
transport, client = await aiosocks.create_connection(Client, proxy=socks5_addr, proxy_auth=None, dst=dst)
client.server_transport = self.transport
self.client_transport = transport
hostip, port = transport.get_extra_info('sockname')
host = unpack("!I", socket.inet_aton(hostip))[0]
self.transport.write(
pack('!BBBBIH', 0x05, 0x00, 0x00, 0x01, host, port))
if __name__ == '__main__':
loop = asyncio.get_event_loop()
srv = loop.create_server(Server, 'localhost', 8000)
loop.run_until_complete(srv)
loop.run_forever()
what should i do to fix at least one of this problems?
if you know any other libraries, please say me.
thanks
I have client program written in python that talks to some server.
[Client]
import asyncore
import logging
import socket
import sys, threading, traceback
from cStringIO import StringIO
class Client(threading.Thread, asyncore.dispatcher):
def __init__(self, host, port):
self.logger = logging.getLogger()
threading.Thread.__init__(self)
self._thread_sockets = dict()
asyncore.dispatcher.__init__(self, map=self._thread_sockets)
# data members for the module
self.host = host
self.port = port
self.write_buffer = ""
self.is_connected = False
self.read_buffer = StringIO()
# Ok now to run the thread !!
self.start()
def run(self) :
self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
address = (self.host, self.port)
self.logger.debug('connecting to %s', address)
# wait until server is up
while not self.is_connected :
try :
self.connect(address)
except Exception as ex :
pass #do nothing, proceed forward !!
asyncore.loop(map=self._thread_sockets)
def handle_connect(self):
self.is_connected = True
self.logger.debug('handle_connect()')
def handle_close(self):
self.logger.debug('handle_close()')
self.close()
def handle_error(self):
traceback.print_exc(sys.stderr)
self.close()
def writable(self):
self.logger.debug('writable() : len is %d bytes', len(self.write_buffer))
is_writable = (len(self.write_buffer) > 0)
if is_writable:
self.logger.debug('writable() -> %s', is_writable)
return is_writable
def readable(self):
self.logger.debug('readable() -> True')
return True
def handle_write(self):
sent = self.send(self.write_buffer)
self.logger.debug('data len written to socket -> %s', sent)
self.logger.debug('handle_write() -> "%s"', self.write_buffer[:sent])
#self.write_buffer = self.write_buffer[sent:]
def handle_read(self):
data = self.recv(8192)
self.logger.debug('handle_read() -> %d bytes', len(data))
self.read_buffer.write(data)
self.logger.debug('data received from socket -> %s', self.read_buffer.getvalue())
self.read_buffer.truncate(0)
def send(self, data) :
self.write_buffer = data
if __name__ == '__main__':
logging.basicConfig(level=logging.DEBUG,
format='%(name)s: %(message)s',
)
try :
client = Client("127.0.0.1", 8182)
client.send('sending data from client')
except Exception as ex :
logging.exception(ex)
sys.exit(1)
I am able to receive data from server correctly but send call to the server always fails. As from the log the send always return 'None'.
Am i missing anything ?
You override the send method of asyncore.dispatcher with code that does not send any data and returns no value:
def send(self, data) :
self.write_buffer = data
At the least, you need to change your code to look similar to this:
def send_data(self, data):
self.write_buffer = data
and this:
client.send_data('sending data from client')
The asyncore.dispatcher class already has a send method which is a wrapper around the socket.send method. From asyncore.py:
def send(self, data):
try:
result = self.socket.send(data)
return result
except socket.error, why:
if why.args[0] == EWOULDBLOCK:
return 0
elif why.args[0] in _DISCONNECTED:
self.handle_close()
return 0
else:
raise
Because you override this method, your send method gets called in your handle_write method, and no data is sent to the server.
Please help me to improve this code:
import base64
import hashlib
import threading
import socket
class WebSocketServer:
def __init__(self, host, port, limit, **kwargs):
"""
Initialize websocket server.
:param host: Host name as IP address or text definition.
:param port: Port number, which server will listen.
:param limit: Limit of connections in queue.
:param kwargs: A dict of key/value pairs. It MAY contains:<br>
<b>onconnect</b> - function, called after client connected.
<b>handshake</b> - string, containing the handshake pattern.
<b>magic</b> - string, containing "magic" key, required for "handshake".
:type host: str
:type port: int
:type limit: int
:type kwargs: dict
"""
self.host = host
self.port = port
self.limit = limit
self.running = False
self.clients = []
self.args = kwargs
def start(self):
"""
Start websocket server.
"""
self.root = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.root.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
self.root.bind((self.host, self.port))
self.root.listen(self.limit)
self.running = True
while self.running:
client, address = self.root.accept()
if not self.running: break
self.handshake(client)
self.clients.append((client, address))
onconnect = self.args.get("onconnect")
if callable(onconnect): onconnect(self, client, address)
threading.Thread(target=self.loop, args=(client, address)).start()
self.root.close()
def stop(self):
"""
Stop websocket server.
"""
self.running = False
def handshake(self, client):
handshake = 'HTTP/1.1 101 Switching Protocols\r\nConnection: Upgrade\r\nUpgrade: websocket\r\nSec-WebSocket-Accept: %s\r\n\r\n'
handshake = self.args.get('handshake', handshake)
magic = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
magic = self.args.get('magic', magic)
header = str(client.recv(1000))
try:
res = header.index("Sec-WebSocket-Key")
except ValueError:
return False
key = header[res + 19: res + 19 + 24]
key += magic
key = hashlib.sha1(key.encode())
key = base64.b64encode(key.digest())
client.send(bytes((handshake % str(key,'utf-8')), 'utf-8'))
return True
def loop(self, client, address):
"""
:type client: socket
"""
while True:
message = ''
m = client.recv(1)
while m != '':
message += m
m = client.recv(1)
fin, text = self.decodeFrame(message)
if not fin:
onmessage = self.args.get('onmessage')
if callable(onmessage): onmessage(self, client, text)
else:
self.clients.remove((client, address))
ondisconnect = self.args.get('ondisconnect')
if callable(ondisconnect): ondisconnect(self, client, address)
client.close()
break
def decodeFrame(self, data):
if (len(data) == 0) or (data is None):
return True, None
fin = not(data[0] & 1)
if fin:
return fin, None
masked = not(data[1] & 1)
plen = data[1] - (128 if masked else 0)
mask_start = 2
if plen == 126:
mask_start = 4
plen = int.from_bytes(data[2:4], byteorder='sys.byteorder')
elif plen == 127:
mask_start = 10
plen = int.from_bytes(data[2:10], byteorder='sys.byteorder')
mask = data[mask_start:mask_start+4]
data = data[mask_start+4:mask_start+4+plen]
decoded = []
i = 0
while i < len(data):
decoded.append(data[i] ^ mask[i%4])
i+=1
text = str(bytearray(decoded), "utf-8")
return fin, text
def sendto(self, client, data, **kwargs):
"""
Send <b>data</b> to <b>client</b>. <b>data</b> can be of type <i>str</i>, <i>bytes</i>, <i>bytearray</i>, <i>int</i>.
:param client: Client socket for data exchange.
:param data: Data, which will be sent to the client via <i>socket</i>.
:type client: socket
:type data: str|bytes|bytearray|int|float
"""
if type(data) == bytes or type(data) == bytearray:
frame = data
elif type(data) == str:
frame = bytes(data, kwargs.get('encoding', 'utf-8'))
elif type(data) == int or type(data) == float:
frame = bytes(str(data), kwargs.get('encoding', 'utf-8'))
else:
return None
framelen = len(frame)
head = bytes([0x81])
if framelen < 126:
head += bytes(int.to_bytes(framelen, 1, 'big'))
elif 126 <= framelen < 0x10000:
head += bytes(126)
head += bytes(int.to_bytes(framelen, 2, 'big'))
else:
head += bytes(127)
head += bytes(int.to_bytes(framelen, 8, 'big'))
client.send(head + frame)
It works fine.
I want the server to use all the processor cores for improved performance. And this code is not effective in high quantities connections. How to implement a multi-threaded solution for this case?
sorry for my bad english.
In CPython, the global interpreter lock, or GIL, is a mutex that
prevents multiple native threads from executing Python bytecodes at
once.
So your code won't work. You can use processeses instead of threads (not on Windows*), twisted or asyncore if you want to support more than one client at the same time.
If your choice is multiprocessing, try this:
client.py:
import socket
def main():
s = socket.socket()
s.connect(("localhost", 5555))
while True:
data = raw_input("> ")
s.send(data)
if data == "quit":
break
s.close()
if __name__ == "__main__":
main()
server.py:
from multiprocessing import Process
from os import getpid
import socket
def receive(conn):
print "(%d) connected." % getpid()
while True:
data = conn.recv(1024)
if data:
if data == "quit":
break
else:
print "(%s) data" % getpid()
def main():
s = socket.socket()
s.bind(("localhost", 5555))
s.listen(1)
while True:
conn, address = s.accept()
print "%s:%d connected." % address
Process(target=receive, args=(conn,)).start()
s.close()
if __name__ == "__main__":
main()
*On Windows this code will throw an error when pickling the socket:
File "C:\Python27\lib\pickle.py", line 880, in load_eof
raise EOFError