pythonsocketshl7-v2

I can not get the data from my server after having sent a file thought sockets


I have a problem when I try to send an Acknowledgment after receiving data.
To resume the situation, I have a client that have a file to send to my server that is listening. When the server receive this file it returns an acknowledgment file. And here is my problem. When my server send the ACK file my client don't get it and just stand here and don't do anything else.

server.py

import socket
import signal
import os

# "all" interfaces
SERVER_HOST = "0.0.0.0"
SERVER_PORT = 8000

BUFFER_SIZE = 4096
SEPARATOR = "<SEPARATOR>"


# Setting Dicionnaries for HL7 ACK Requirements
MSH = {3:'HOSPITAL', 4:'RESULTS', 5:'LAB400', 6:'RESULTS',7:'20201030084524',9:'ACK', 10:'1452357', 11:'P',12:'2.2', 15:'AL', 16:'NE', 17:'CHE'}
MSA = {1:'AA', 2:'1722178'}

socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

# test the binding
try:
    socket.bind((SERVER_HOST, SERVER_PORT))

except socket.error as error:

    print('Bind failed. Error Code : '
          + str(error[0]) + ' Message ' 
          + error[1])

    exit()

def signal_handler(sign, frame):

    print('[*] Shutting down')
    exit(0)

while signal.signal(signal.SIGINT, signal_handler):

    # connection limit(5 connection try then deny)
    socket.listen(5)

    print(f"[*] Listening as {SERVER_HOST}:{SERVER_PORT}")

    # accept the connection if there is any
    client_socket, address = socket.accept() 

    # Below code is executed if sender is connected
    print(f"[+] {address} is connected.")

    # get what the client is sending
    received = client_socket.recv(BUFFER_SIZE).decode()

    filename, fileSize = received.split(SEPARATOR)

    #convert to integer
    fileSize = int(fileSize)

    # remove absolute path if there is
    filename = os.path.basename(filename)

    # start receiving the file from the socket and writing to the file stream
    with open(f".\\Files\\{filename}", "wb") as f:

        while True:

            # read 1024 bytes from the socket (receive)
            bytes_read = client_socket.recv(BUFFER_SIZE)     

            if not bytes_read:    

                # file transmitting is done
                print(f"[+] File transfert is done")
                print(f"[+] File saved")
                break

            # write to the file the bytes we just received
            f.write(bytes_read)
    
    
    myfile = open(".\\Files\\ack.hl7", "rb")
    client_socket.send(myfile.read())
    
    client_socket.close()

    socket.close

client.py

import socket
import os
import random

SEPARATOR = "<SEPARATOR>"
BUFFER_SIZE = 4096

HOST = "127.0.0.1"
PORT = 8000

files = ["test1.HL7","test2.HL7","test3.HL7","test4.HL7","test5.HL7","test6.HL7","test7.HL7","test8.HL7"]

# fileName = f".\\ClientFiles\\{files[random.randrange(1,8)]}"

fileName = f".\\ClientFiles\\{files[0]}"

filesize = os.path.getsize(fileName)

socket = socket.socket()

print(f"[+] Connecting to {HOST}:{PORT}")

socket.connect((HOST, PORT))

print("[+] Connected.")

socket.send(f"{fileName}{SEPARATOR}{filesize}".encode())

# opening file
with open(fileName, "rb") as f:
    while True:
        # reading bytes
        bytes_read = f.read(BUFFER_SIZE)
        if not bytes_read:
            # Transmitting is done
            break
        # send all the buffer
        socket.sendall(bytes_read)
print(f"[+] File {fileName} sent")



data = socket.recv(BUFFER_SIZE)

print(data)

print("[*] Closing")
socket.close()

I cannot figure out why it doesn't work

Here is my files that need to be transferred(it's some HL7 V2.5.1 btw)

Ack.hl7

MSH|^~\&|HOSPITAL|RESULTS|LAB400|RESULTS|20201030084524||ACK|1452357|P|2.2
MSA|AA|1722178

testFile.hl7

MSH|^~\&|ADT1|MCM|LABADT|MCM|198808181126|SECURITY|ADT^A04|MSG00001|P|2.4
EVN|A01-|198808181123
PID|||PATID1234^5^M11||JONES^WILLIAM^A^III||19610615|M-||2106-3|1200 N ELM STREET^^GREENSBORO^NC^27401-1020|GL|(919)379-1212|(919)271-3434~(919)277-3114||S||PATID12345001^2^M10|123456789|9-87654^NC
NK1|1|JONES^BARBARA^K|SPO|||||20011105
NK1|1|JONES^MICHAEL^A|FTH
PV1|1|I|2000^2012^01||||004777^LEBAUER^SIDNEY^J.|||SUR||-||1|A0-
AL1|1||^PENICILLIN||PRODUCES HIVES~RASH
AL1|2||^CAT DANDER
DG1|001|I9|1550|MAL NEO LIVER, PRIMARY|19880501103005|F||
PR1|2234|M11|111^CODE151|COMMON PROCEDURES|198809081123
ROL|45^RECORDER^ROLE MASTER LIST|AD|CP|KATE^SMITH^ELLEN|199505011201
GT1|1122|1519|BILL^GATES^A
IN1|001|A357|1234|BCMD|||||132987
IN2|ID1551001|SSN12345678
ROL|45^RECORDER^ROLE MASTER LIST|AD|CP|KATE^ELLEN|199505011201

What I've tried so far

On my client

I've tried to recreate a socket in the case that it wasn't able to respond

try:
    data = socket.recv(BUFFER_SIZE)
    print(data)
except:
    # recreate the socket and reconnect
    socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    socket.connect(HOST, PORT)
    data = socket.recv(BUFFER_SIZE)
    print(data)

Solution

  • I could reproduce and understand your problem.

    The underlying cause is that TCP is a stream protocol. That means that the only guarantee is that all the bytes that are sent from one side will the received in the same ordre by the peer. But packets mays be concatenated or splitted by any element on the network, including the protocol stack of either side.

    So in server.py, this is wrong:

    # get what the client is sending
    received = client_socket.recv(BUFFER_SIZE).decode()
    
    filename, fileSize = received.split(SEPARATOR)
    
    #convert to integer
    fileSize = int(fileSize)
    

    because received can contain the beginning of the data...

    The robust way would be:

    # get what the client is sending
    received = client_socket.recv(BUFFER_SIZE)
    
    filename, bytes_read = received.split(SEPARATOR.encode())
    fileSize = bytes(itertools.takewhile(
        lambda i: bytes((i,)).isdigit(), bytes_read))
    bytes_read = bytes_read[len(fileSize):]
    #convert to integer
    fileSize = int(fileSize)
    filename = filename.decode()
    

    And you will have the beginning of the data (as bytes) in bytes_read and should write it immediately:

    # start receiving the file from the socket and writing to the file stream
    with open(f".\\Files\\{filename}", "wb") as f:
        f.write(bytes_read)
    
        while True:
            ...
    

    But that is not all. Still in server.py the reading loop will not return an empty buffer until the peer closes or better shutdowns the socket.

    So in client.py you must signal the end of transmission:

    print(f"[+] File {fileName} sent")
    
    socket.shutdown(SHUT_WR)   # tells peer that nothing will be sent any more
    
    data = socket.recv(BUFFER_SIZE)