Ist es in Ordnung, wenn zwei oder mehr Prozesse gleichzeitig lesen / schreiben unix socket
?
Ich habe einige Tests durchgeführt.
Hier ist meine sock_test.sh
, die 50 Clients hervorbringt, von denen jeder gleichzeitig 5K-Nachrichten schreibt:
#! /bin/bash --
SOC='/tmp/tst.socket'
test_fn() {
soc=$1
txt=$2
for x in {1..5000}; do
echo "${txt}" | socat - UNIX-CONNECT:"${soc}"
done
}
for x in {01..50}; do
test_fn "${SOC}" "Test_${x}" &
done
Ich erstelle dann ein unix socket
und erfasse den gesamten Datenverkehr zur Datei sock_test.txt
:
# netcat -klU /tmp/tst.socket | tee ./sock_test.txt
Schließlich führe ich mein Testskript ( sock_test.sh
) aus und überwache auf dem Bildschirm alle 50 Mitarbeiter, die ihre Arbeit erledigen. Am Ende überprüfe ich, ob alle Nachrichten ihr Ziel erreicht haben:
# ./sock_test.sh
# sort ./sock_test.txt | uniq -c
Zu meiner Überraschung gab es keine Fehler und alle 50 Mitarbeiter haben alle 5K-Nachrichten erfolgreich gesendet.
Ich muss wohl zu dem Schluss kommen, dass das gleichzeitige Schreiben an unix sockets
OK ist?
War mein Parallelitätsgrad zu niedrig, um Kollisionen zu erkennen?
Stimmt etwas mit meiner Testmethode nicht? Wie teste ich es dann richtig?
BEARBEITEN
Nach der hervorragenden Antwort auf diese Frage gibt es für diejenigen, die sich besser auskennen python
, meinen Prüfstand:
#! /usr/bin/python3 -u
# coding: utf-8
import socket
from concurrent import futures
pow_of_two = ['B','KB','MB','GB','TB']
bytes_dict = {x: 1024**pow_of_two.index(x) for x in pow_of_two}
SOC = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
SOC.connect('/tmp/tst.socket')
def write_buffer(
char: 'default is a' = 'a',
sock: 'default is /tmp/tst.socket' = SOC,
step: 'default is 8KB' = 8 * bytes_dict['KB'],
last: 'default is 2MB' = 2 * bytes_dict['MB']):
print('## Dumping to the socket: {0}'.format(sock))
while True:
in_memory = bytearray([ord(char) for x in range(step)])
msg = 'Dumping {0} bytes of {1}'
print(msg.format(step, char))
sock.sendall(bytes(str(step), 'utf8') + in_memory)
step += step
if last % step >= last:
break
def workers(concurrency=5):
chars = concurrency * ['a', 'b', 'c', 'd']
with futures.ThreadPoolExecutor() as executor:
for c in chars:
executor.submit(write_buffer, c)
def parser(chars, file='./sock_test.txt'):
with open(file=file, mode='rt', buffering=8192) as f:
digits = set(str(d) for d in range(0, 10))
def is_digit(d):
return d in digits
def printer(char, size, found, junk):
msg = 'Checking {}, Expected {:8s}, Found {:8s}, Junk {:8s}, Does Match: {}'
print(msg.format(char, size, str(found), str(junk), size == str(found)))
char, size, found, junk = '', '', 0, 0
prev = None
for x in f.read():
if is_digit(x):
if not is_digit(prev) and prev is not None:
printer(char, size, found, junk)
size = x
else:
size += x
else:
if is_digit(prev):
char, found, junk = x, 1, 0
else:
if x==char:
found += 1
else:
junk += 1
prev = x
else:
printer(char, size, found, junk)
if __name__ == "__main__":
workers()
parser(['a', 'b', 'c', 'd'])
Dann können Sie in der Ausgabe Zeilen wie die folgenden beobachten:
Checking b, Expected 131072 , Found 131072 , Junk 0 , Does Match: True
Checking d, Expected 262144 , Found 262144 , Junk 0 , Does Match: True
Checking b, Expected 524288 , Found 219258 , Junk 0 , Does Match: False
Checking d, Expected 524288 , Found 219258 , Junk 0 , Does Match: False
Checking c, Expected 8192 , Found 8192 , Junk 0 , Does Match: True
Checking c, Expected 16384 , Found 16384 , Junk 0 , Does Match: True
Checking c, Expected 32768 , Found 32768 , Junk 610060 , Does Match: True
Checking c, Expected 524288 , Found 524288 , Junk 0 , Does Match: True
Checking b, Expected 262144 , Found 262144 , Junk 0 , Does Match: True
Sie können sehen, dass die Nutzdaten in einigen Fällen ( b
, d
) unvollständig sind, fehlende Fragmente jedoch später empfangen werden ( c
). Einfache Mathematik beweist es:
# Expected
b + d = 524288 + 524288 = 1048576
# Found b,d + extra fragment on the other check on c
b + d + c = 219258 + 219258 + 610060 = 1048576
Daher gleichzeitiges Schreiben unix sockets
ist OK nicht OK.
nc
die ausgegebenen Daten verwechselt werden? Gemäß diesem Beitrag sollte stackoverflow.com/questions/9644251/… für jede Verbindung auf der Serverseite einen separaten fd erstellen, und daher sollte für jede Verbindung ein separater Serverpuffer vorhanden sein.