I am trying to create a chat program that works by UDP hole punching and connecting directly to a node which is provided by a broker.
The script I have right now works just fine locally. The problems come when I try to use an external address to connect to. I can't figure this out on my own so I was hoping someone here could help me out!
import socket
import json
import landerdb
import threading
class PeerChat:
address = None
def __init__(self):
self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
self.db = landerdb.Connect("nodes")
self.brok_ip = ""
self.brok_port = 5000
self.nick = "Test"
self.friends = []
def listen(self):
self.command = {
"HERE":self.here,
"MSG":self.msg,
}
global address
self.sock.bind(address)
while True:
msg, addr = self.sock.recvfrom(1024)
try:
data = json.loads(msg)
except:
continue
if data[u'cmd'] in self.command:
threading.Thread(target=self.command[data[u'cmd']], args=(addr, data)).start()
def main(self):
while True:
msg = raw_input("> ")
msg = msg.split()
try:
msg = json.dumps({"cmd":msg[0], "data":' '.join(msg[2:]), "nick":self.nick, "to":msg[1]})
except:
continue
for x in self.db.find("nodes", "all"):
self.sock.sendto(msg, tuple(x['addr']))
def here(self, addr, data):
if not self.db.find("nodes", {"addr":addr}):
self.db.insert("nodes", {"addr":addr})
if data['nick'] in self.friends:
print data['nick'] + " has come online."
def msg(self, addr, data):
if data['to'] == self.nick:
print data['nick']+": "+data['data']
def GetNodes(self):
self.sock.sendto("", (self.brok_ip, self.brok_port))
with open("nodes", 'wb') as file:
while True:
msg, addr = self.sock.recvfrom(1024)
if msg == "\n":
break
file.write(msg)
msg, addr = self.sock.recvfrom(1024)
global address
address = ("0.0.0.0", int(msg))
for x in self.db.find("nodes", "all"):
addr = tuple(x['addr'])
self.sock.sendto(json.dumps({"cmd":"HERE", "nick":self.nick}),addr)
if __name__ == "__main__":
PeerChat().GetNodes()
threading.Thread(target=PeerChat().listen).start()
PeerChat().main()
I haven't tested this code externally yet, because I'm at uni, but I use slightly different lines to setup a UDP connection.
They might give you some ideas...
#!/usr/bin/python
import socket
import struct
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.bind(('239.255.60.60', 4876))
mreq = struct.pack("=4sl", socket.inet_aton("239.255.60.60"), socket.INADDR_ANY)
sock.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq)
packet_number = 0
while True:
packet_number += 1
print packet_number
raw_data = sock.recv(1024)
print raw_data
print
I don't fully understand all that, but it works for me.
It was adapted from here. You should read the entire page a couple of times to understand what they are saying. Do a search of the page for 'IP_MULTICAST_TTL' I gather that you need to set socket.IP_MULTICAST_TTL, 33 in sock.setsockopt, where the number is any number > 32. Remember that I am almost as unsure as you are at this stage...
Related
Is there something the client needs to do in order to flag that "This is the end?"
For example, I have the following server running:
import socket, sys
while True:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind(('', 5500))
s.listen(1)
conn, addr = s.accept()
print('Connected by', addr)
data = b''
while True:
data_chunk = conn.recv(10)
data += data_chunk
if not data: break
# How do I get here so I can echo the message *at the end* ?
# conn.send(b'Done. What you sent was: %s' % data)
And I have the following client code:
import socket
client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client_socket.connect(('sandbox2.myip.com', 5500))
client_socket.send(b'select * from table')
# Now, what to do so that I can get the final message from he server?
When I do the connection though, this is what it shows:
sampleDB$ python listener.py
Connected by ('99.51.149.29', 63352)
I can confirm that the data data_chunks are being received, but then it just seems to hang at this part right here:
data_chunk = conn.recv(10)
Is there something I need to do in the client (or the server?) to flag that "This is the end of the transmission, so conn.recv() doesn't just hang indefinitely?
Here's a complete example of a socket based client/server protocol that implements the strategy described in my comment:
import socket
import threading
from struct import pack, unpack
HOST = 'localhost'
PORT = 7070
FORMAT = ('!Q', 8)
MSG = '''The Owl and the Pussy-cat went to sea
In a beautiful pea-green boat,
They took some honey, and plenty of money,
Wrapped up in a five-pound note.
The Owl looked up to the stars above,
And sang to a small guitar,
"O lovely Pussy! O Pussy, my love,
What a beautiful Pussy you are,
You are,
You are!
What a beautiful Pussy you are!"'''
def sendbuffer(s, b):
buffer = pack(FORMAT[0], len(b)) + b
offset = 0
while offset < len(buffer):
offset += s.send(buffer[offset:])
def recvbuffer(s):
p = s.recv(FORMAT[1], socket.MSG_WAITALL)
n = unpack(FORMAT[0], p)[0]
return None if n == 0 else s.recv(n, socket.MSG_WAITALL)
def server():
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.bind((HOST, PORT))
s.listen()
conn, _ = s.accept()
with conn:
while (data := recvbuffer(conn)):
sendbuffer(conn, data)
def client(msg):
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.connect((HOST, PORT))
sendbuffer(s, msg.encode())
data = recvbuffer(s)
print(data.decode())
sendbuffer(s, b'') # kill switch
def main():
threading.Thread(target=server).start()
client(MSG)
if __name__ == '__main__':
main()
I have used Python socket in ESP as a server and Laptop as a client. I customized the socket codes from this site. When I send the loop as the client input, I enter a loop on the server. I don't know how the while loop is broken when I send a word other than loop, For example "Hello".
server.py:
import socket
host = ''
port = 5560
def setupServer():
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
print("Socket created.")
try:
s.bind((host, port))
except socket.error as msg:
print(msg)
print("Socket bind comlete.")
return s
def setupConnection():
s.listen(1)
conn, address = s.accept()
print("Connected to: " + address[0] + ":" + str(address[1]))
return conn
def Hello_():
print('Hello')
def Loop_():
while True:
print('yes')
def dataTransfer(conn):
while True:
data = conn.recv(1024)
data = data.decode('utf-8')
dataMessage = data.split(' ', 1)
command = dataMessage[0]
if command == 'loop':
Loop_()
if command == 'Hello':
Hello_()
else:
print("X")
conn.close()
s = setupServer()
while True:
try:
conn = setupConnection()
dataTransfer(conn)
except:
break
client.py
import socket
host = '192.168.56.1'
port = 5560
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((host, port))
while True:
command = input("Enter your command: ")
s.send(str.encode(command))
s.close()
I know your time is valuable and I appreciate your attention for spending time for help me.
If you want the Loop_() method to return when more data is received on the socket, you can modify the method so that it calls select() to poll the socket to see if more data has arrived, as shown below. (Note that I've added a conn argument to the Loop_() method so I can pass in the socket to check it)
import select
[...]
def Loop_(conn):
while True:
print('yes')
inReady, outReady, exReady = select.select([conn], [], [], 0.0)
if (conn in inReady):
print('more data has arrived at the TCP socket, returning from Loop_()')
break
def dataTransfer(conn):
while True:
data = conn.recv(1024)
data = data.decode('utf-8')
dataMessage = data.split(' ', 1)
command = dataMessage[0]
if command == 'loop':
Loop_(conn)
if command == 'Hello':
Hello_()
else:
print("X")
conn.close()
I made an app to send to python socket the message "Oleft" when i tilt the phone but the result on console is like:
connection from('192.168.0.101', 33313)
b'Oleft'
b'OleftOleftOleftOleftOleftOleftOleftOleftOleftOleftOleft'
b'Oleft'
b'OrightOrightOrightOright'
b'OleftOleftOleftOleftOleftOrightOrightOrightOrightOrightOrightOleftOleftOleftOleftOleftOleftOleftOleftOleftOleftOleftOleftOleftOleftOleftOleftOleftOleftOleftOleft'
b'OleftOleftOleftOleftOleftOleftOleftOleftOleftOleftOleft'
b'OleftOleftOleftOleftOleftOleftOleftOleftOleftOleftOleft'
b'OleftOleftOleftOleftOleftOleftOleftOleftOleftOleftOleft'
b'OleftOleft'
It has no way to receive only an b'Oleft' ?
wgremote.py
import socket
class WGRemote:
def __init__(self):
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.host = socket.gethostname()
self.port = 10000
self.received = None
def connect(self):
global c, addr
self.sock.bind((self.host, self.port))
self.sock.listen(5)
c, addr = self.sock.accept()
print('connection from' + str(addr))
def setMode(self,mode):
sent = c.send(mode.encode("utf-8"))
def receive(self):
self.received = c.recv(1024)
return self.received
def close(self):
c.close
testApp.py
import socket
import sys
from wgremote import WGRemote
remote = WGRemote()
remote.connect()
remote.setMode('orient')
while True:
data = remote.receive()
print(data)
Because, You wrote print(data) in the while loop :)
while True:
data = remote.receive()
print(data)
If you want to receive only an b'Oleft you can make a filter :
count = 0
while True:
data = remote.receive()
if data == "b'Oleft":
if count < 1:
count = count + 1
print(data)
Or you must send the only an b'Oleft :)
this is my client:
import socket
import threading
class ThreadedServer(object):
def __init__(self, host, port):
self.host = host
self.port = port
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
self.sock.bind((self.host, self.port))
def listen(self):
self.sock.listen(5)
while True:
client, address = self.sock.accept()
client.settimeout(60)
threading.Thread(target = self.listenToClient,args = (client,address)).start()
def listenToClient(self, client, address):
size = 1024
while True:
try:
data = client.recv(size)
if data:
# Set the response to echo back the recieved data
response = data
client.send(response)
else:
raise error('Client disconnected')
except:
client.close()
return False
if __name__ == "__main__":
ThreadedServer('',5000).listen()
I'm trying parse the message received. I have tried the following:
if data:
if data == "command":
print("Command Received")
elif data != "command":
response = data
client.send(response)
else:
raise error('Client disconnected')
I have printed data to console and it comes out as b'command', I have tried using that in the equal to string and it still does not print 'Command receieved' when I send it from my client which just connects to the server and sends a message doing the following mySocket.send(message.encode()) I was wondering how I could fix my issue?
Just found the solution: you have to decode the UDP message when printing it.
Example:
print data.decode()
I'm implementing a simple messaging application using Python. I've defined a client CClient which instantiates a CMsgGateway and then begins a thread which asks the user for input (while 1). The client is as follows:
from msginterface import CMsgGateway
class CClient():
def __init__(self):
self.port = 2400
self.msgGate = CMsgGateway(self.port)
Thread(target=self.inputMsg).start()
def inputMsg(self):
while 1:
msg = input("Enter message:")
self.msgGate.sendMsg(msg)
if __name__ == '__main__':
cmdchat = CClient()
The message gateway listens for messages being received in a separate thread (while 1). This class is defined as:
from threading import Thread
import socket, struct, sys
class CMsgGateway():
def __init__(self, port):
self.port = port
print("Listening on port " + str(self.port) + ".\n")
Thread(target=self.serve).start()
def serve(self):
while 1:
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
self.sock.bind(("", self.port))
self.sock.listen(1)
self.rcvMsg()
def rcvMsg(self):
serverSocket, info = self.sock.accept()
while 1:
try:
buf = bytearray(4000)
view = memoryview(buf)
bytes = serverSocket.recv_into(view, 4000)
if bytes:
stx = view[0]
Size = view[1:3]
bSize = Size.tobytes()
nTuple = struct.unpack(">H", bSize)
nSize = nTuple[0]
message = view[0:3+nSize]
messageString = message.tobytes().decode("utf-8").strip()
print("Received message:\n\n" + messageString)
sys.stdout.flush()
else:
break
except socket.timeout:
break
def sendMsg(self, msgToSend):
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
host = socket.gethostname()
self.sock.connect((host, self.port))
self.sock.send(msgToSend.encode())
self.sock.close()
When I run the program the thread within CMsgGateway begins, listening for messages. However, as far as I can tell the thread within CClient, the inputMsg method, does not begin (I see no prompt to enter input e.g. "Enter message" is not displayed).
Could someone please help me figure out what is wrong with this? Many thanks.