Hallo Stefanie.
Stefanie hat geschrieben.
Mir wäre es lieber, wenn es eine Möglichkeit beim Senden gäbe evtl. die Fragmentierung(die ja in meinen Fall das Zusammenfassen kleiner Streams zu einem Block scheinbar auch ünernimmt) auszuschalten. Einfach, dass ich einen Stream mit z.B. 10 Bytes sende und nur genau das wird als ein Block übertragen, nichts Fragmentiert bzw. zusammengefasst.
Naja, das funktioniert leider nicht in allen Fällen.
Du sendest in einem Netzwerk irgendwelche Daten zu einem Server.
Protokoll ist IPPROTO_TCP.
Du übergibst normalerweise die zu sendenden Daten dem OSI-Layer 1. = Socket.Send!!
Danach brauchst du dir um den Datentransport keine Sorgen mehr zu machen.
Der Sendebuffer und MTU Size ist je nach Einstellung des
OS, Treiber bzw, Netzwerkkarte unterschiedlich.
Wenn du kein Performance Freak bist, brauchst du dir darüber keinerlei Gedanken machen.
Die gesendeten Daten werden nun sicher (
TCP) zum Server übertragen.
Der Server (OSI-Layer 1. = Socket.recv) hat einen Empfangsbuffer, der je nach
OS, und Treiber und oder
Netzwerkkarte, natürlich auch unterschiedlich gross ist.
Also folgendes Szenario:
Client 1 sendet 10 Rote Kugeln, und Client 2 sendet zur gleichen Zeit 9 Grüne Kugeln.
Der Server Buffer hat Platz für 6 Kugeln (MTU * Socet Buffer Size)!
Im Buffer landen 2 Rote und 1 grüne Kugel, du wirst benachrichtigt, dass Daten empfangen wurden.
Du hast dir beim Connect des Clients am Server einen Empfangsbuffer für einen Grünen und einen Roten Client generiert.
Nun sortierst du die Grünen und die Roten Kugeln solange, bis alle Kugeln übertragen wurden.
Hier kommt nun das Problem das schon von Luckie, sirius, Mavarik angesprochen wurde.
Wenn du keine Informationen hast, wieviele Kugeln für einen bestimmten Client eigentlich übertragen werden müssen,
bist du auf verlorenem Posten. Du wirst es nie schaffen die genau gewünschte Anzahl der Kugeln zu übertragen.
Hier brauchst du einen Header (Info Kugel, die sagt dass für Rot 10 Kugeln kommen,
und eine Info Kugel, die sagt, dass für Grün 9 Kugeln kommen),
Also, Informationen wieviele Kugeln für einen bestimmten Climet übertragen werden müssen.
D.h. Wenn die Info Kugel übertragen wurde, müssen die Roten und die Grünen Kugeln solange getrennt werden, bis
die Anzahl der Kugeln für jeden Client erreicht ist.
Wo man nun richtig Hirnschmalz hineinstecken muss, sind die Sonderfälle, die auftreten können.
Der Empfangsbuffer enthält mehrere vollständig übertragene Datensätze (nur Rote Kugeln)
Der Empfangsbuffer enthält mehrere vollständig übertragene Datensätze,
inclusive Fragment des noch nicht vollständig gesendeten Datensatzes. (nur Rote Kugeln)
Der Empfangsbuffer enthält mehrere unvollständig übertragene Datensätze. (Rote und Grüne Kugeln)
Der Empfangsbuffer enthält mehrere vollständig übertragene Datensätze (Rote und Grüne Kugeln)
Der Empfangsbuffer enthält mehrere vollständig übertragene Datensätze,
inclusive Fragment des noch nicht vollständig gesendeten Datensatzes. (von Roten und Grünen Kugeln)
Der Empfangsbuffer enthält mehrere unvollständig übertragene Datensätze. (nur Rote Kugeln)
usw....
Hier ein Beispiel wie dies korrekt für Client und Server implementieren werden kann:
Clients wurden zuvor im Connect Evewnt in einer Hashliste gespeichet.
Jeder Client beinhaltet einen eigenen RecvBuffer = CliCon.FMemBuf = Aufteilung der Kugeln.
Socket Read Speichert die Daten in einem Ringbuffer, und der TServerObj.Thread
liest den Ringbuffer threadsave aus, und bereitet die Daten auf.
Delphi-Quellcode:
procedure TServerObj.Execute;
const
HEADER_LENGTH = SizeOf(byte) + SizeOf(DWORD);
var
cbRcv: integer;
cbWritten: integer;
cbRest: integer;
ptrByte: ^Byte;
nLen: integer;
nDataSize: integer;
cb: integer;
nMsgID: Byte;
p: pointer;
CliCon: TCliCon;
RcvBuf:
array [0..WSOCK_READ_BUFFER_SIZE * 8]
of byte;
RcvBufItem: TRcvBufItem;
begin
while not terminated
do begin
if FRecvBuffer.CountBytes > 0
then begin
RcvBufItem := TRcvBufItem(FRecvBuffer.Peek(nLen)^);
if FRecvBuffer.Remove(SizeOf(TRcvBufItem)) <> SizeOf(TRcvBufItem)
then
raise exception.Create('
nLen <> FRecvBuffer.Remove(nLen)');
cbRcv := RcvBufItem.cbData;
if cbRcv <= 0
then
raise exception.create('
cbRcv <= 0');
move(RcvBufItem.ptrData^, RcvBuf[0], cbRcv);
CliCon := FIntHash.ValueOf(RcvBufItem.Socket);
if CliCon =
nil then begin
if GlobalFree(RcvBufItem.hMemData) <> 0
then
raise exception.create('
GlobalFree(RcvBufItem.hMemData)) <> 0');
if GlobalFree(RcvBufItem.hMemStruct) <> 0
then
raise exception.create('
GlobalFree(RcvBufItem.hMemStruct) <> 0');
Continue;
end;
cbWritten := CliCon.FMemBuf.
Write(@RcvBuf[0], cbRcv);
cbRest := cbRcv - cbWritten;
if cbRest <> 0
then begin
CliCon.FMemBuf.SetBufSize((CliCon.FMemBuf.BufSize + cbRcv) * 2);
if CliCon.FMemBuf.
Write(@RcvBuf[cbWritten], cbRest) <> cbRest
then
raise exception.Create('
you should never see this');
end;
while true
do begin
nLen := HEADER_LENGTH;
ptrByte := CliCon.FMemBuf.Peek(nLen);
if ptrByte <>
nil then
begin
move(ptrByte^, nMsgID, SizeOf(Byte));
inc(ptrByte, SizeOf(Byte));
move(ptrByte^, nDataSize, SizeOf(DWORD));
end else begin
Break;
end;
if nMsgID > MSG_ID_KEEP_ALLIVE
then begin
CliCon.FMemBuf.Remove(CliCon.FMemBuf.BufSize);
raise exception.Create('
not (nMsgID in [MSG_ID_LOGON..MSG_ID_KEEP_ALIVE])');
end;
if CliCon.FMemBuf.CountBytes >= nDataSize + HEADER_LENGTH
then
begin //-- Mindestens ein Datensatz ist vorhanden
if CliCon.FMemBuf.Remove(HEADER_LENGTH) = 0
then
raise exception.Create('
Buffer.Remove(HEADER_LENGTH) = 0');
cb := nDataSize;
p := CliCon.FMemBuf.Peek(cb);
CliCon.ProcessMessage(nMsgID, p, nDataSize);
if CliCon.FMemBuf.Remove(nDataSize) = 0
then
raise exception.Create('
Buffer.Remove(nDataSize) = 0');
end else //-- if CliCon.FMemBuf.CountBytes >= nDataSize + HEADER_LENGTH
Break;
end;
//-- while true do begin
if GlobalFree(RcvBufItem.hMemData) <> 0
then
raise exception.create('
GlobalFreeII(RcvBufItem.hMemData)) <> 0');
if GlobalFree(RcvBufItem.hMemStruct) <> 0
then
raise exception.create('
GlobalFreeII(RcvBufItem.hMemStruct) <> 0');
end else begin
DoGarbageCollection;
Waitforsingleobject(FMsgDispatchEvent, INFINITE);
end;
end;
end;
Für das obige Beispiel ist auch vollständiger Source vorhanden.
Also, wenn du Asynchrone und oder Synchrone Socket brauchst, sag einfach bescheid, hab da fix und fertige
DLL's (Synchron, Asynchron) Client und Server incl. Source, hochlast getestet.
Asynchrone Client Server Anwendung mit ~10000 Client Verbindungen.
Synchrone Client Server Anwendung mit 2 X Quad Xeon mit ca. 3000 Requests/s bei ~ 25Kb Daten in einem 1 GB Netz.
Können dir auch als Tutorial dienen, um das Hirnschmalz Problem in den Griff zu bekommen.
Hoffe etwas geholfen zu haben.
lg. Astat