So I am working on a project that requires me to simulate a TCP three handshake protocol using UDP. I want to divided the project into two parts: the first is to establish and close connection; the second is to implement the flow control protocol without any package loss. Since the client and server will be interacting with each other, so I need two sockets, which means I also need two ports - 19990 and 19991 - to send and receive data between client and server.
I am having trouble with the three way handshake
Client -> (Send SYN)
Sever -> (Receive SYN and Send SYN ACK)
Client -> Receives SYN ACK and Sends ACK with Data
Server then establishes a connection and says that data has been received
Client -> Server ACK the connection & received data. So send FIN to close the connection
Server -> Receives FIN and closes the connection (with server_socket.close)
output on the client side:
Send: a,1
Receive: b, num:2
Send: c,3
Receive: d, num:4
Client done
output on server side:
Receive: a, num:1
Send: b,2
Receive c, num:3
Send: d,4
Server done.
However, what I am hoping for them to look like is like so:
client.py
Send SYN
Send: ,1
Receive: num,2
Received SYN-ACK. Send ACK & Data
Send: I love ABC University in New York City.,3
Receive: ,num:4
Received ACK for data. Send FIN. Connection closed.
Send: ,4
server.py
Receive: ,num: 1
Received SYN. Send SYN-ACK
Send: ,2
Receive: I love ABC University in New York City.,num:3
Receive ACK. Connection Established. Received Data. Send ACK
Send: ,4
Receive: ,num:4
Connection Closed
Here is the code I have for server.py:
import socket
import sys
import struct
server_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
server_socket.bind(("0.0.0.0", 19990))
def send(c,num):
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
ss = struct.pack("!50si",c.encode(),num)
s.sendto(ss,("127.0.0.1",19991))
s.close()
print("Send:%s,%d" % (c,num))
def recv():
global server_socket
data, addr = server_socket.recvfrom(1024)
str,num = struct.unpack("!50si",data)
str = str.decode("utf-8").replace("\0","")
print("Receive:%s,num:%d" % (str,num))
return str,num
while True:
str,num = recv()
num = num + 1
str = chr(ord(str)+1)
send(str,num)
if str == "d":
break
print("Server Done.")
server_socket.close()
Here is the code I have for client.py
import socket
import sys
import struct
server_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
server_socket.bind(("0.0.0.0", 19991))
def send(c,num):
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
ss = struct.pack("!50si",c.encode(),num)
s.sendto(ss,("127.0.0.1",19990))
s.close()
print("Send:%s,%d" % (c,num))
def recv():
global server_socket
data, addr = server_socket.recvfrom(1024)
str,num = struct.unpack("!50si",data)
str = str.decode("utf-8").replace("\0","")
print("Receive:%s,num:%d" % (str,num))
return str,num
str = 'a'
num = 1
while True:
send(str,num)
str,num = recv()
if str == "d":
break
str = chr(ord(str)+1)
num = num + 1
print("Client done.")
server_socket.close()
#If the number = 1, sender will send and the receiver will receive
#c is the data and num is the sequence number, for the first three msgs it is the flag
#The payload we want to send is seven characters with one sentence
#Window size is 4 with 4 being four characters
#First package is "i lo"
#I is sent as a package, then space as a package, then l as a package, and o as a package as a window
#First byte is i, second is space, l is third, o is forth.
#Send out 4 bytes, receive 4 acknowledgements.
#When the sender sends out the last byte "."
#To do: Change 'a' to past establishment
#Instead of Send: a, 1
#professor wants to see "Send Think message"
#Receive: Think ack message
#Send: Ack msg
#a b c d are just give you some examples
Related
I am trying to implement UDP stop-and-wait protocol using python socket programming. I have been trying out my code with different retransmission times. And sometimes I get all the files correctly received at the receiver, but sometimes some packets get lost. For example when I ran it with 40ms five times, twice the file was received correctly and three times incorrectly. Why is this variability happening?
Here is my code for the sender and receiver:
senderSocket = socket(AF_INET, SOCK_DGRAM) # Create UDP socket for sender
r = 0 # Number of retransmissions
for i in range(0, len(messages)):
senderSocket.settimeout(retryTimeout) # After the message is sent, set retransmission timeout to listen for acknowledgement
while True:
senderSocket.sendto(messages[i], (hostName, portNumber))
acknowledged = False
while not acknowledged:
try:
ack, receiverAddress = senderSocket.recvfrom(2) # Receive ACK
acknowledged = True
except: # Socket timeout exception occurs when timeout expires but no ACK received
senderSocket.sendto(messages[i], (hostName, portNumber)) # Retransmit the message
r = r + 1 # Increment the number of retransmissions
break # On to the next message
senderSocket.close()
while True:
message, senderAddress = receiverSocket.recvfrom(1027) # Read from UDP socket into message, getting sender's address (sender IP and port)
header = message[:3] # Header is the first 3 bytes (index 0, 1, 2)
data = message[3:] # Rest is the data
first_byte = '{0:08b}'.format(header[0])
second_byte = '{0:08b}'.format(header[1])
seq_num = int(first_byte + second_byte, 2) # Convert bytes to decimal
if seq_num not in seq_nums: # Detect duplicates
seq_nums.append(seq_num)
file_content.extend(data)
ack = header[:2] # ACK is the receipt of the received message (sequence number)
receiverSocket.sendto(ack, senderAddress) # Send ACK
if header[2] == 1: # Sent multiple ACKs at lat message to make sure it receives and the sender closes
receiverSocket.sendto(ack, senderAddress)
receiverSocket.sendto(ack, senderAddress)
receiverSocket.sendto(ack, senderAddress)
break
receiverSocket.close()
Backstory: what I have done
{Codes at the bottom} I've already coded the multithreaded client and server programs using python socket, with the help of the following sites:
I. Echo Client and Server
II. Socket Server with Multiple Clients | Multithreading | Python
III. Python Socket Receive Large Amount of Data
Regarding Encryption & Decryption
(1) Exactly at what places in my codes should I encrypt/decrypt my message? Do
I encrypt the messages themselves after the user inputs or do I encrypt the byte streams after the input messages have been encoded?
(2) And how am I supposed to encrypt/decrypt the communication properly and efficiently? (It'd be nice to see code solutions with explanation, many thanks)
My Codes Currently
_server.py
import socket
import os
from _thread import *
import struct # Here to convert Python data types into byte streams (in string) and back
# ---- To Avoid Message Boundary Problem on top of TCP protocol ----
def send_msg(sock: socket, msg): # ---- Use this to send
# Prefix each message with a 4-byte length (network byte order)
msg = struct.pack('>I', len(msg)) + msg
sock.sendall(msg)
def recv_msg(sock: socket): # ---- Use this to receive
# Read message length and unpack it into an integer
raw_msglen = recvall(sock, 4)
if not raw_msglen:
return None
msglen = struct.unpack('>I', raw_msglen)[0]
# Read the message data
return recvall(sock, msglen)
def recvall(sock: socket, n: int):
# Helper function to receive n bytes or return None if EOF is hit
data = bytearray()
while len(data) < n:
packet = sock.recv(n - len(data))
if not packet:
return None
data.extend(packet)
return data
# ---- Server Communication Setup
HOST = '127.0.0.1' # Standard loopback interface address (localhost)
PORT = 65432 # Port to listen on (non-privileged ports are > 1023)
ThreadCount = 0
try: # create socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
print ("Socket successfully created")
except socket.error as err:
print ("socket creation failed with error %s" %(err))
try: # bind socket to an address
s.bind((HOST, PORT))
except socket.error as e:
print(str(e))
print('Waitiing for a Connection..')
s.listen(3)
def threaded_client(conn: socket):
conn.send(str.encode('Welcome to the Server'))
while True:
# data = conn.recv(2048) # receive message from client
data = recv_msg(conn)
reply = 'Server Says: ' + data.decode('utf-8')
if not data:
break
# conn.sendall(str.encode(reply))
send_msg(conn, str.encode(reply))
conn.close()
while True:
Client, addr = s.accept()
print('Connected to: ' + addr[0] + ':' + str(addr[1]))
start_new_thread(threaded_client, (Client, )) # Calling threaded_client() on a new thread
ThreadCount += 1
print('Thread Number: ' + str(ThreadCount))
s.close()
_client.py
import socket
import struct # Here to convert Python data types into byte streams (in string) and back
# ---- To Avoid Message Boundary Problem on top of TCP protocol ----
def send_msg(sock: socket, msg): # ---- Use this to send
# Prefix each message with a 4-byte length (network byte order)
msg = struct.pack('>I', len(msg)) + msg
sock.sendall(msg)
def recv_msg(sock: socket): # ---- Use this to receive
# Read message length and unpack it into an integer
raw_msglen = recvall(sock, 4)
if not raw_msglen:
return None
msglen = struct.unpack('>I', raw_msglen)[0]
# Read the message data
return recvall(sock, msglen)
def recvall(sock: socket, n: int):
# Helper function to receive n bytes or return None if EOF is hit
data = bytearray()
while len(data) < n:
packet = sock.recv(n - len(data))
if not packet:
return None
data.extend(packet)
return data
# ---- Client Communication Setup ----
HOST = '127.0.0.1' # The server's hostname or IP address
PORT = 65432 # The port used by the server
try:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
print ("Socket successfully created")
except socket.error as err:
print ("socket creation failed with error %s" %(err))
print('Waiting for connection')
try:
s.connect((HOST, PORT))
except socket.error as e:
print(str(e))
Response = s.recv(1024)
while True:
Input = input('Say Something: ')
# s.send(str.encode(Input))
send_msg(s, str.encode(Input))
# Response = s.recv(1024)
Response = recv_msg(s)
print(Response.decode('utf-8'))
s.close()
You only need to Encrypt the Message itself. I would use RSA to Encrypt the Messages.
If you plan on using multiple Servers, you can open a second Port and if someone connects to it, the Server/Host. Sends the Public Key to the Client.
After that, the Client sends his Public key to the Server.
Now Server and Client switch Ports, and Communicate over there while using the Public key of the other to Encrypt the Messages and decrypt them with their Private key.
You can also Hard-code the Public Keys, if you only have one Server and one Client.
A good Module for RSA Encryption is PyCrytodome.
Here is an Example of to encrypt messages with PyCrytodome.
from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_OAEP
def encrypt(pk_receiver, message):
key = RSA.import_key(pk_receiver)
cipher = PKCS1_OAEP.new(key)
c = cipher.encrypt(message.encode())
return c
def decrypt(sk, c):
key = RSA.import_key(sk)
cipher = PKCS1_OAEP.new(key)
m = cipher.decrypt(c)
return m
def generate_sk(key_length):
key = RSA.generate(key_length)
with open('./secret_key.pem', 'wb') as f:
f.write(key.export_key(format='PEM'))
return key
def generate_pk(sk):
pk = sk.public_key()
with open('./public_key.pem', 'wb') as f:
f.write(pk.export_key(format='PEM'))
return
I am a beginner of socket programming using python. I am working on my course project. Part of my project requires sending and receiving UDP messages with different port. The server program called robot is provided and I need to write the client program called student which can interact with the robot. Thus, I cannot show all source code in the server program.
This is the part related to the UDP socket in the server program
############################################################################# phase 3
# Create a UDP socket to send and receive data
print ("Preparing to receive x...")
addr = (localhost, iUDPPortRobot)
s3 = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s3.bind(addr)
x, addr = s3.recvfrom(1)
print ("Get x = %d" % (int(x)))
############################################################################# phase 3
time.sleep(1)
print ("Sending UDP packets:")
messageToTransmit = ""
for i in range(0,int(x) * 2):
messageToTransmit += str(random.randint(0,9999)).zfill(5)
print ("Message to transmit: " + messageToTransmit)
for i in range(0,5):
s3.sendto(messageToTransmit.encode(),(studentIP,iUDPPortStudent))
time.sleep(1)
print ("UDP packet %d sent" %(i+1))
############################################################################# phase 4
This is my client program. s3 is the UDP socket. I can send message to the server program successfully but I cannot receive the message from it. Is this due to the difference in the ports? If yes, what should I do in order to fix it?
import os
import subprocess
import socket
import random
import time
sendPort = 3310
localhost = '127.0.0.1'
socket.setdefaulttimeout(10)
command = "python robot_for_python_version_3.py"
subprocess.Popen(command)
print("ROBOT IS STARTED")
sendSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sendSocket.connect((localhost, sendPort))
studentId = '1155127379'
sendSocket.send(studentId.encode())
s_2Port = sendSocket.recv(5)
sendSocket.close()
s_2 = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s_2.bind((localhost, int(s_2Port)))
s_2.listen(5)
s2, address = s_2.accept()
s_2.close()
step4Port = s2.recv(12)
iUDPPortRobot, dummy1 = step4Port.decode().split(",")
iUDPPortStudent, dummy2 = dummy1.split(".")
s3 = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
num = random.randint(5,10)
time.sleep(3)
s3.sendto(str(num).encode(), (localhost, int(iUDPPortRobot)))
print("Test1")
charStr = s3.recvfrom(1024)
print("Test2")
print(charStr)
exit()
The reason why you are not receiving the message is because the server sends it to an endpoint that is not listening for messages. As the protocol is UDP (no guarantees, etc.), the server sends the message successfully to a non-listening endpoint, while the listening endpoint waits forever.
In more detail, addr as returned by x, addr = s3.recvfrom(1) is not (studentIP, iUDPPortStudent). Try the following to see the difference (note that you have omitted the piece where iUDPPortRobot is defined and shared, I set it to 50000 for illustration purposes):
# in one interactive session 1 (terminal), let's call it session 1
>>> import socket
>>> import random
>>> import time
>>>
>>> iUDPPortRobot = 50000
>>> addr = ('localhost', iUDPPortRobot)
>>> s3 = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
>>> s3.bind(addr)
>>> x, addr = s3.recvfrom(1) # <= this will block
# in another interactive session (terminal), let's call it session 2
>>> import socket
>>> import random
>>> import time
>>>
>>> iUDPPortRobot = 50000
>>> s3 = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
>>> num = random.randint(5,10)
>>> s3.sendto(str(num).encode(), ('localhost', int(iUDPPortRobot))) # <= this will unblock recvfrom in session 1, i.e., the message got received
1
# back to session 1
>>> addr <= check address, this is the main issue you are facing
('127.0.0.1', 60911)
>>> messageToTransmit = ""
>>> for i in range(0,int(x) * 2):
... messageToTransmit += str(random.randint(0,9999)).zfill(5)
...
>>> print ("Message to transmit: " + messageToTransmit)
Message to transmit: 06729020860821106419048530205105224040360495103025
# back to session 2, let's prepare for receiving the message
>>> charStr = s3.recvfrom(1024) # <= this will block
# back to session 1 to send a message
# you do not share what (studentIP,iUDPPortStudent), but from
# what you describe it is not ('127.0.0.1', 60911), let's say
# studentIP = 'localhost' and iUDPPortStudent = 50001
>>> studentIP = 'localhost'
>>> iUDPPortStudent = 50001
# now let send a message that will be sent successfully but not received, i.e.,
# it will not unblock recvfrom in session 2
>>> s3.sendto(messageToTransmit.encode(),(studentIP,iUDPPortStudent))
50
# ... but if try to send to the listening endpoint it will get received
>>> s3.sendto(messageToTransmit.encode(), addr)
50
# back to session 2, to check things
>>> charStr
(b'06729020860821106419048530205105224040360495103025', ('127.0.0.1', 50000)) # <= SUCCESS
There are two ways to fix this. The one shown above which involves changing the server code, which essentially involves what is shown above, i.e., send the message to a listening endpoint by modifying the address passed to s3.sendto. If I understand things correctly, this is not an option as you are trying to write the client code. The second way is to send the message to (studentIP, iUDPPortStudent), but have a listening endpoint at the other end. If studentIP and iUDPPortStudent are known to your "client" program, which I assume is the case, you can add code similar to what you have at the top of the server program code snippet.
Specifically, add in place of charStr = s3.recvfrom(1024) something like:
addr = (studentIP, iUDPPortStudent)
s4 = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s4.bind(addr)
charStr = s4.recvfrom(1024) # <= this will block and unblock when you send the message using s3.sendto(messageToTransmit.encode(),(studentIP,iUDPPortStudent))
For completeness, you will need to change localhost to 'localhost' and if in your experiments you encounter a OSError: [Errno 98] Address already in use you will have to wait for the TIME-WAIT period to pass or set the SO_REUSEADDR flag by adding s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) before bind.
I'm trying to send random size int array from multiple-clients to server which will keep adding the newly received int array to a global array and return accumulated sorted array to client. My client code is able to send and receive int array to/from server. But server is not able to read the int array and sort and send back to client (My server can just read and send back original int array to client, but it's not what I want).
In my server code, commented part is not working. I am very new in python and socket programming.
Client.py
# Import socket module
import socket, pickle
import random
def Main():
# local host IP '127.0.0.1'
host = '127.0.0.1'
# Define the port on which you want to connect
port = 12345
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# connect to server on local computer
s.connect((host, port))
while True:
# Generate Random array to be sent to server
arr = []
# Generate random size between 10 and 20
# random_size = random.randint(10, 20)
random_size = random.randint(1, 3)
for i in range(0, random_size):
arr.append(random.randint(0, 10))
print('Array = ' + str(arr))
# Serialise the array to byte stream before sending to server
data_stream = pickle.dumps(arr)
#Array byte stream sent to server
s.send(data_stream)
# messaga received from server
data = s.recv(1024)
#deserialise the byte stream into array after receiving from server
data_arr = pickle.loads(data)
# print the received message
#print('Received', repr(data_arr))
print('Received from Server: ', data_arr)
# ask the client whether he wants to continue
ans = input('\nDo you want to continue(y/n) :')
if ans == 'y':
continue
else:
break
# close the connection
s.close()
if __name__ == '__main__':
Main()
Server.py
# import socket programming library
import socket, pickle
# import thread module
from _thread import *
import threading
from sortedcontainers import SortedList
import bisect
#Container to store accumulated int array from multiple clients
sl = SortedList()
# To protect
print_lock = threading.Lock()
# thread fuction
def threaded(c):
while True:
# data received from client
data = c.recv(1024)
# Data from client can't be printed =============== why?
print(data)
if not data:
print('No data received from client - Bye')
# lock released on exit
print_lock.release()
break
c.send(data) # ===> It works but I want to add received int array into global sl before sending back to client
'''
////////////////////// Code in this comment section is not working //////////////////
#Deserialise Byte stream array from client into array list
data_arr = pickle.loads(data)
#Add received int array from client to global sortedList sl in sorted order
for i in data_arr:
bisect.insort(sl, i)
sl.add(i)
print(sl)
#Serialise sorted sl into Byte stream before sending to client
data_stream = pickle.dumps(sl)
# send back sorted integer list to client
c.send(data_stream)
'''
# connection will never be closed, server will run always
#c.close()
def Main():
host = ""
# We can use a port on our specific computer
# But in this case it is 12345 (it can be anything)
port = 12345
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind((host, port))
print("socket binded to post", port)
# put the socket into listening mode
s.listen(5)
print("socket is listening")
# a forever loop until client wants to exit
while True:
# establish connection with client
c, addr = s.accept()
# lock acquired by client
print_lock.acquire()
print('Connected to :', addr[0], ':', addr[1])
# Start a new thread and return its identifier
start_new_thread(threaded, (c,))
s.close()
if __name__ == '__main__':
Main()
I run it in terminal and I see error
NotImplementedError: use ``sl.add(value)`` instead
but it seems to be incomplete message.
After removing
bisect.insort(sl, i)
it starts working.
Probably there was: use ``sl.add(value)`` instead of ``bisect.insort(sl, i)``
I am having issue with below code in TCP python socket prog. I am trying to send integer messages from (lets say) 3 clients. So, There is 3 clients, so the proxy knows that N = 3, and it will wait until it receives 3 numbers. 1st client sends 10 to the proxy, 2nd client sends 20 to the proxy, and the 3rd client sends 30 to the proxy. Because N is reached, the proxy starts to transmit to the server. It will send (in order): 10, 20, 30, END (where every comma indicates a new message). The server receives the 4 messages, and after receiving the END message, it starts to calculate the average. The average is 20, so the server sends 20 to the proxy, and closes the connection. The proxy receives 20, and sends it to all the 3 clients, and then closes the connection. The clients receive 20.
Please check below code and let me know where I am making mistake... I am able to send number from client but not getting response from proxy/server. Am I doing any mistake ?
For client:
#!/usr/bin/env python
import socket
HOST = 'localhost'
PORT = 21001
proxysock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
proxysock.connect((HOST, PORT))
while True:
try:
data = str(raw_input())
except valueError:
print 'invalid number'
proxysock.sendall(data)
data = proxysock.recv(1024)
print 'server reply:' +data
proxysock.close()
For Proxy:
#!/usr/bin/env python
import select
import sys
import socket
from thread import *
HOST = 'localhost'
PORT = 21001
PORT1 = 22000
max_conn = 5
buffer_size = 2048
proxyserversock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
proxyserversock.bind((HOST, PORT))
proxyserversock.listen(max_conn)
input = [proxyserversock,sys.stdin]
running = 1
while running:
inputready,outputready,exceptready = select.select(input,[],[])
for proxysock in inputready:
if proxysock == proxyserversock:
client_sock, addr = proxyserversock.accept()
input.append(client_sock)
elif proxysock == sys.stdin:
junk = sys.stdin.readline()
running = 0
else:
data = proxysock.recv(buffer_size)
if data:
proxysock.send(data)
else:
proxysock.close()
input.remove(proxysock)
serversock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
serversock.connect((HOST, PORT1))
serversock.sendall(data,END)
serversock.recv(buffer_size)
client_sock.sendall()
serversock.close()
client_sock.close()
For Server:
#!/usr/bin/env python
import socket
HOST = 'localhost'
PORT1 = 22000
serversock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
serversock.bind((HOST, PORT1))
serversock.listen(10)
while 1:
serversock, addr = serversock.accept()
data = serversock.recv(2048)
data.pop()
average = float(sum(data))/len(data)
serversock.sendall(average)
print 'avg' + average
serversock.close()