Domanda

I'm trying to make a simple Peer to Peer Network in Python 2.7. The problem is, I can't seem to be able to create a connection between two machines in which they both act as a server and a client. I can get it to work when one is a server and the other is a client but not when they are both, both. Do I need to create 2 sockets? Also I'm using TCP to connect.

UPDATE:

import socket, sys               # Import socket module

s = socket.socket()         # Create a socket object
host = socket.gethostname() # Get local machine name
port = 12345                # Reserve a port for your service.
s.bind((host, port))        # Bind to the port

if sys.argv[1] == "connect":
    host = sys.argv[2]
    s.connect((host, port))
    s.close 
else:
    s.listen(5)                 # Now wait for client connection.
    while True:
       c, addr = s.accept()     # Establish connection with client.
       print 'Got connection from', addr
       c.send('Thank you for connecting')
       c.close()

The codes not very good because for someone to connect as a client they have to use the argument "connect" followed by the hostname or IP of the second machine. I can't get the two to connect and serve to each other simultaneously.

È stato utile?

Soluzione

Yes, two sockets are necessary. The listening socket should open on a constant port, and the client port should be opened on a different (potentially dynamic) port, usually higher in the port range. As an example:

Server sockets on port 1500, client sockets on port 1501.

Peer1: 192.168.1.101

Peer2: 192.168.1.102

When peer1 connects to peer2 it looks like this: 192.168.1.101:1501 -> 192.168.1.102:1500.

When peer2 connects to peer1 it looks like this: 192.168.1.102:1501 -> 192.168.1.101:1500.

Listening TCP sockets are also generally run on a separate thread since they are blocking.

Altri suggerimenti

Yes, you'll need to use two sockets, one for accepting connections (server) and one for initiating connections (client). However, you can bind both sockets to the same local port, using that port number as both the source and destination port and thereby ensuring that you'll end up with only a single connection between each pair of peers. If both peers try to connect simultaneously (e.g. because they discovered each other at the same time), one of the client connection attempts will fail (where the peer's server socket accepted the connection), you'll have to handle (ignore) that. To bind two sockets on the same port, you'll need to set the SO_REUSEPORT/SO_REUSEADDR flags on both.

Here is an example program demonstrating this technique (using the excellent trio on Python 3):

from errno import EADDRNOTAVAIL
from functools import partial
from itertools import count
import trio
import socket

async def peer(SRC, DEST):
    counter = count(start=1)
    async def sender(stream, n):
        print(f"sender{n}@{SRC}: started!")
        while True:
            data = bytes(f"Hello from {n}@{SRC}", "utf8")
            print(f"sender{n}@{SRC}: sending {data!r}")
            await stream.send_all(data)
            await trio.sleep(1)

    async def receiver(stream, n):
        print(f"receiver{n}@{SRC}: started!")
        async for data in stream:
            print(f"receiver{n}@{SRC}: got data {data!r}")
        print(f"receiver{n}@{SRC}: connection closed")

    async with trio.open_nursery() as nursery:
        async def run(connection: trio.SocketStream):
            count = next(counter)
            print(f"peer@{SRC} got connection{count} from {method}() with {connection.socket.getpeername()}")
            async with connection:
                async with trio.open_nursery() as nursery:
                    print(f"peer@{SRC}: spawning sender...")
                    nursery.start_soon(sender, connection, count)

                    print(f"peer@{SRC}: spawning receiver...")
                    nursery.start_soon(receiver, connection, count)

        print(f"peer: listening at {SRC}")
        servers = await trio.open_tcp_listeners(SRC[1], host=SRC[0])
        servers[0].socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        servers[0].socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)
        await nursery.start(trio.serve_listeners, partial(run, "listen"), servers)

        print(f"peer: connecting from {SRC} to {DEST}")
        client = trio.socket.socket()
        client.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        client.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)
        await client.bind(address=SRC)
        try:
            await client.connect(address=DEST)
        except OSError as err:
            if err.errno != EADDRNOTAVAIL:
                raise
            # the other client was faster than us
            print(f"peer@{SRC}: {err.strerror}")
        else:
            await run('connect', trio.SocketStream(client))

async def main():
    async with trio.open_nursery() as nursery:
        a = ("127.0.0.1", 12345)
        b = ("127.0.0.1", 54321)
        nursery.start_soon(peer, a, b)
        nursery.start_soon(peer, b, a)

trio.run(main)

In this small demonstration, the two peers run within the same program on different ports on the same host, but it works just the same with two programs using the same ports but different hosts. Notice that if you comment out the client.bind(address=SRC) bit, they will use ephemeral source ports, and create two separate connections not just a single one.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top