Skip to content
Permalink
Browse files

Implement echo server and client with low-level socket library in python

  • Loading branch information
chelseatroy committed Dec 15, 2019
1 parent 63e3b56 commit 4d2c807f5769d964cf7e5c6f3cee2ddca9e03a64
Showing with 62 additions and 0 deletions.
  1. +30 −0 echo_client.py
  2. +32 −0 echo_server.py
@@ -0,0 +1,30 @@
import socket
import sys

# Create a TCP/IP socket
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

This comment has been minimized.

Copy link
@chelseatroy

chelseatroy Dec 22, 2019

Author Owner

This is where we create the client socket. There are actually two sockets in this PR: the client socket and the server socket. They're both initialized the same way, then later we tell them to do different things.


# Connect the socket to the port where the server is listening
server_address = ('localhost', 10000)
print(f"connecting to {server_address[0]} port {server_address[1]}")
sock.connect(server_address)

This comment has been minimized.

Copy link
@chelseatroy

chelseatroy Dec 22, 2019

Author Owner

Aha! This is how we know this is a client socket. We give it an address (consisting of a host and a port), and we ask it to connect to that address. Contrast this to what we tell the server socket to do.


try:

# Send data
message = input("Type your message:\n")

This comment has been minimized.

Copy link
@chelseatroy

chelseatroy Dec 22, 2019

Author Owner

We use input() to collect input from the user. message will contain whatever the user entered.

print(f"sending {message}")
sock.sendall(message.encode('utf-8'))

This comment has been minimized.

Copy link
@chelseatroy

chelseatroy Dec 22, 2019

Author Owner

This is where we tell the socket to send what the user entered. We have to encode it because sockets communicate bytes, not raw strings.


# Look for the response
amount_received = 0
amount_expected = len(message)

while amount_received < amount_expected:
data = sock.recv(16)
amount_received += len(data)
print(f"received {data}")

This comment has been minimized.

Copy link
@chelseatroy

chelseatroy Dec 22, 2019

Author Owner

This loopy part ensures that we get back the entire response message from the server. Sockets do not necessarily send all the data at once, so we need to make sure that we keep adding data to our response until we have it all.

This code didn't quite work right, in fact, because amount_expected is the length of the outgoing message. If the response from the server was longer than the client message, the response was getting cut off. I fix that in a future commit, but I'm showing you this one because by the time I fix this there's a lot more going on than just the sockets, and I want you to be able to focus on the sockets.


finally:
print(f"closing socket")
sock.close()

This comment has been minimized.

Copy link
@chelseatroy

chelseatroy Dec 22, 2019

Author Owner

Always close your sockets to ensure that other clients can connect to this port later.

@@ -0,0 +1,32 @@
import socket
import sys

server_address = ('localhost', 10000)
print(f"starting up on {server_address[0]} port {server_address[1]}")

sock = socket.socket()

This comment has been minimized.

Copy link
@chelseatroy

chelseatroy Dec 22, 2019

Author Owner

This is where we create the server socket. There are actually two sockets in this PR: the client socket and the server socket. They're both initialized the same way, then later we tell them to do different things.

sock.bind(server_address)

This comment has been minimized.

Copy link
@chelseatroy

chelseatroy Dec 22, 2019

Author Owner

We bind the socket to an address consisting of a host (IP address) and a port. This is where client sockets must send their messages in order for them to reach this server.


sock.listen(1)

This comment has been minimized.

Copy link
@chelseatroy

chelseatroy Dec 22, 2019

Author Owner

Then we ask our socket to listen on that port for connections from clients.


while True:
print('waiting for a connection')
connection, client_address = sock.accept()

try:
print(f"connection from {client_address}")

# Receive the data in small chunks and retransmit it
while True:
data = connection.recv(16)

This comment has been minimized.

Copy link
@chelseatroy

chelseatroy Dec 22, 2019

Author Owner

We receive the data here (16 refers to the amount of data we can receive at a time)...

print(f"received {data}")
if data:
print(f"sending data back to the client")
connection.sendall(data)

This comment has been minimized.

Copy link
@chelseatroy

chelseatroy Dec 22, 2019

Author Owner

...and then we literally send that same data back verbatim to the client. This is why it's called an "echo" server.

else:
print(f"no more data from {client_address}")
break

finally:
# Clean up the connection
connection.close()

This comment has been minimized.

Copy link
@chelseatroy

chelseatroy Dec 22, 2019

Author Owner

And we close the connection such that our server is no longer listening for messages at the above address.

0 comments on commit 4d2c807

Please sign in to comment.
You can’t perform that action at this time.