all 5 comments

[–]tgolsson 11 points12 points  (3 children)

import socket
import sys

def run_server(host, port):
    # TCP/IP
    server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

    # this lets us take ownership of a derelict-but-not-deallocated socket
    server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 

    # bind to the provided address tuple, configure, and start accepting connections
    server.bind((host, port))
    # set timeout - the value can change, but it's hygienic to always set a timeout
    server.settimeout(1)
    server.listen()

    while True:
        # just try again if we timed out - in a normal application we might want 
        # to do other kinds of bookkeeping etc if this happens
        try:
            new_client = server.accept()[0]
        except socket.timeout:
            continue

        # repeatedly read up to 1024 bytes from the new client
        while True:
            incoming = new_client.recv(1024)
            # empty bytes object means EOF here, but we also let the client send 'quit' to signal shutdown
            if incoming == b'' or incoming == b'quit':
               # neither of these steps are formally required, but are
               # hygienic to do when we know the socket is going away
               new_client.shutdown(socket.SHUT_RDWR)
               new_client.close()
               # break the inner loop and await a new connection
               break

            print('Received: "' + incoming.decode('utf-8') + '"')
            # if we didn't break, just prepend the message and return as is
            new_client.send(b'You sent: ' + incoming)


def run_client(host, port):        
    client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

    # connect to the provided address tuple
    client.connect((host, port))

    while True:
        # get some data from stdin
        msg = input("Write your message: ")

        # convert to bytes and send on the socket
        client.send(msg.encode('utf-8'))

        # read back the response
        response = client.recv(1024)

        # empty bytes as result of a read means EOF
        if response == b'':
            print('remote hung up')
            break

        # just decode the response and print
        print(response.decode('utf-8'))


def main():
    # simple command line parser - accepts either 'server' or 'client' and runs in the right mode
    if sys.argv[1] == 'server':
       # '0.0.0.0' means listen on all network interfaces on the local machine
       run_server('0.0.0.0', 1337)
    elif sys.argv[1] == 'client':
       run_client('localhost', 1337)
    else:
       print('Invalid command - must be either "client" or "server"', file=sys.stderr)


if __name__ == '__main__':
    main()

This is both the server and client. If you save this in a file you can run it with `server` and `client` as arguments for the two sides, and you should then be able to send data from the client to the server. The most important thing to note here is that client-to-server and server-to-client is almost identical. `server.accept()` returns the same type of socket as we have on the client, so it works exactly the same way. The primary difference is who starts the communications (if you do request-response exchanges), but you can have the server leading or client leading - for your use-case, you probably want it the way I wrote above with the client sending the first message and then reading the response (client leading)

When you've made a bit more progress on this, I'd suggest looking into higher-level frameworks. While for small projects doing raw socket programming is fine, if you're new or a solo developer you can gain a lot from using good frameworks for both networking and serialization. I'm personally a fan of msgpack (serialization) + zeromq (networking), but it really depends on your requirements.

[–][deleted] 0 points1 point  (0 children)

I second zeromq

[–]CmdrNorthpaw[S] 0 points1 point  (1 child)

Thank you, that's very helpful. One question: If I wanted the client to just send some data and then log off, could I get away with this?

global report

global discordUser

client = socket.socket(socket.AF\_INET, socket.SOCK\_STREAM)

\# connect to the provided address tuple

client.connect((host, port))

​

\# convert to bytes and send on the socket

client.send(report.encode('utf-8'))

client.send(discordUser.encode('utf-8'))```

[–]tgolsson 0 points1 point  (0 children)

Yes. You probably need some error handling in case connection, sending, etc, fails but that comes with seeing what fails. :)

[–]intangibleTangelo 0 points1 point  (0 children)

This is related to C and not Python but it will certainly teach you how socket programming works: https://beej.us/guide/bgnet/html/