Building TCP Socket Servers with File Logging and XOR Encryption in Python
Network communication in Python relies heavily on the socket library, which provides low-level access to the transport layer. This technical overview demonstrates establishing a TCP connection between a server and a client, extending functionality with file logging, and implementing basic data encryption.
Basic Server-Client Architecture
The foundation involves creating a server that binds to a specific interface and port, listening for incoming connections, and a client that initiates the handshake. The following implementation uses IPv4 and TCP (SOCK_STREAM).
Server Implementation
The server initializes a socket, binds to localhost on port 5050, and enters a loop to handle incoming data. Connection management is handled via try...finally blocks to ensure resources are released.
import socket
def start_server(host='127.0.0.1', port=5050):
listener = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
listener.bind((host, port))
listener.listen(1)
print(f'Server listening on {host}:{port}')
conn, addr = listener.accept()
with conn:
print(f'Connected by {addr}')
while True:
incoming = conn.recv(1024).decode('utf-8')
if not incoming:
break
print(f'Received: {incoming}')
reply = input('Enter response: ')
conn.sendall(reply.encode('utf-8'))
if input('Continue? (y/n): ').lower() == 'n':
break
finally:
listener.close()
if __name__ == '__main__':
start_server()
Client Implementation
The client connects to the server's address and engages in a send-receive loop. It encodes user input before transmission and decodes incoming byte streams.
import socket
def start_client(host='127.0.0.1', port=5050):
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
client.connect((host, port))
print('Connected to server')
while True:
msg = input('Message to server: ')
client.sendall(msg.encode('utf-8'))
data = client.recv(1024).decode('utf-8')
if not data:
break
print(f'Server reply: {data}')
if input('Continue? (y/n): ').lower() == 'n':
break
finally:
client.close()
if __name__ == '__main__':
start_client()
Integrating File I/O
To persist communication history, both endpoints can log transactions to local files. Using context managers (with statement) ensures files are properly closed after writing.
Server-Side Logging
The server appends received messages and sent responses to server_log.txt.
import socket
def run_logged_server(host='127.0.0.1', port=5050):
srv = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
srv.bind((host, port))
srv.listen(1)
conn, addr = srv.accept()
with conn, open('server_log.txt', 'a', encoding='utf-8') as log:
while True:
pkt = conn.recv(1024).decode('utf-8')
if not pkt:
break
log.write(f'IN: {pkt}\n')
print(f'Client: {pkt}')
resp = input('Response: ')
conn.sendall(resp.encode('utf-8'))
log.write(f'OUT: {resp}\n')
if input('Stop? (y/n): ') == 'y':
break
srv.close()
Client-Side Logging
Similarly, the client records outgoing messages and incoming replies to client_log.txt.
import socket
def run_logged_client(host='127.0.0.1', port=5050):
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect((host, port))
with sock, open('client_log.txt', 'a', encoding='utf-8') as log:
while True:
text = input('Send: ')
sock.sendall(text.encode('utf-8'))
log.write(f'SENT: {text}\n')
reply = sock.recv(1024).decode('utf-8')
if not reply:
break
print(f'Reply: {reply}')
log.write(f'RECV: {reply}\n')
if input('Stop? (y/n): ') == 'y':
break
Data Encryption Strategies
Secure transmission requires encrypting payloads before sending. While libraries like pycryptodome offer AES encryption, dependency issues may arise during installation. A lightweight alternative is XOR encryption for demonstration purposes.
XOR Cipher Helper
A reusbale function handles both encryption and decryption since XOR is symmetric.
def xor_transform(data_bytes, key_bytes):
key_len = len(key_bytes)
return bytes(b ^ key_bytes[i % key_len] for i, b in enumerate(data_bytes))
Encrypted Server
The server receives binary data, applies the XOR transformation using a shared secret, and saves the plaintext to a file.
import socket
def secure_server(host='127.0.0.1', port=6000):
secret = b'SecureKey1234567'
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind((host, port))
server.listen(1)
print(f'Listening on {port}...')
conn, _ = server.accept()
try:
cipher_text = conn.recv(4096)
plain_text = xor_transform(cipher_text, secret)
with open('decoded_data.bin', 'wb') as f:
f.write(plain_text)
print('Decryption complete. Saved to decoded_data.bin')
finally:
conn.close()
server.close()
Encrypted Client
The client reads a local file, encrypts the content, and transmits the binary stream.
import socket
def secure_client(host='127.0.0.1', port=6000):
secret = b'SecureKey1234567'
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
client.connect((host, port))
with open('source_data.bin', 'rb') as f:
raw_data = f.read()
encrypted = xor_transform(raw_data, secret)
client.sendall(encrypted)
print('Encrypted payload sent.')
except FileNotFoundError:
print('Error: Source file not found.')
finally:
client.close()
Common Configuration Issues
During development, several environment-specific challenges may occur. Package managers like pip might fail to resolve dependencies for cryptographic libraries due to network restrictions or compiler missing errors. In such cases, verifying the Python version compatibility or switching to pure Python implementations (like the XOR example above) resolves the issue.
Additionally, configuration strings such as IP addresses or ports often contain hidden whitespace when copied from documentation. This leads to connection refused errors. Validating string inputs using .strip() ensures clean parameters are passed to socket binding functions.