7 #define PUTXDWORD(b, d) \
8 (b)[0] = (byte)(((d) >> 24) & 0xFF); \
9 (b)[1] = (byte)(((d) >> 16) & 0xFF); \
10 (b)[2] = (byte)(((d) >> 8) & 0xFF); \
11 (b)[3] = (byte)( (d) & 0xFF);
14 (byte)(((d) >> 24) & 0xFF), \
15 (byte)(((d) >> 16) & 0xFF), \
16 (byte)(((d) >> 8) & 0xFF), \
19 #define GETXDWORD(b) (uint32)(((b)[0] << 24) | ((b)[1] << 16) | ((b)[2] << 8) | (b)[3])
22 #define PACKET_SIZE 6 * 1024 * 1024 //65536 //16384
23 #define MAXBUFFERSIZE 6 * 1024 * 1024 //104867572
24 #define BUFFER_SIZE 6 * 1024 * 1024 //65536 * 8 //16384 * 8 //5001265536
27 #define MAXBUFFERSIZE 1024*1024
28 #define PACKET_SIZE 65530 //131072
29 #define BUFFER_SIZE PACKET_SIZE * 8
31 // Packets sent from Client to Server
32 static struct NetworkClientPacket
38 static class OpenPacket : NetworkClientPacket
41 char fileName[1]; // Rest follows, null terminated
44 static struct ClosePacket : NetworkClientPacket
49 static struct ReadPacket : NetworkClientPacket
56 static struct SizePacket : NetworkClientPacket
61 static class NetDataBlock : struct
63 NetDataBlock prev, next;
67 static enum NetworkClientPacketType { open, close, read, getSize };
69 static class Request : struct
73 void (* CallBack)(Request request, const byte * buffer);
75 NetworkClientFile file;
77 uint readStart, readSize;
79 void ReadCallback(const byte * buffer)
81 NetworkClientFile file = this.file;
82 FileServerConnection connection = file.connection;
83 connection.mutex.Wait();
85 // Check where this goes in the buffer
86 int overlap = file.position + file.bufferCount - (int)readStart;
87 int bytesToRead = numBytes - (int)overlap;
89 // Make an exception if this is an actual read (it's been waited on...)
91 bytesToRead = Min(bytesToRead, MAXBUFFERSIZE - (int)file.bufferCount);
95 if(file.bufferSize < file.bufferCount + bytesToRead)
97 file.buffer = renew file.buffer byte[file.bufferCount + bytesToRead];
98 file.bufferSize = file.bufferCount + bytesToRead;
100 memcpy(file.buffer + file.bufferCount, buffer + overlap, bytesToRead);
101 file.bufferCount += bytesToRead;
104 // Request some more data!
105 if(file.bufferCount < BUFFER_SIZE)
107 // Don't do it if we know it's passed end of file
109 file.ReadMoreData(file.position + file.bufferCount, PACKET_SIZE, false);
113 // This request is being waited on... we can't free it yet
118 connection.mutex.Release();
121 void ReadResultCallback(const byte * buffer)
123 NetworkClientFile file = this.file;
124 file.connection.mutex.Wait();
125 // Reuse this request, reinsert it at the head
126 file.connection.requests.Insert(null, this);
127 numBytes = GETXDWORD(buffer);
128 CallBack = ReadCallback;
130 file.connection.mutex.Release();
133 void SizeCallback(const byte * buffer)
135 readSize = GETXDWORD(buffer);
139 void OpenCallback(const byte * buffer)
141 file.handle = GETXDWORD(buffer);
146 class FileClientThread : Thread
148 FileServerConnection socket;
153 //guiApp.WaitNetworkEvent();
154 //guiApp.ProcessNetworkEvents();
162 public class NetworkClientFile : File
164 // The only guys who will be moving where the buffer data is mapped to the file are Read or Seek!
165 int Read(byte * buffer, uint size, uint count)
167 FileServerConnection connection = this.connection;
172 connection.mutex.Wait();
174 // buffer is always data at virtual position in the file
175 // position is virtual position in the file
176 bytesRead = bufferCount;
177 if(bytesRead < count)
179 connection.mutex.Release();
180 // Here we are missing (count - bytesRead) bytes starting at file->position
181 // ReadMoreData(position + bufferCount, (count - bufferCount), true);
182 ReadMoreData(position + bufferCount, (Max(count, BUFFER_SIZE) - bufferCount), true);
183 connection.mutex.Wait();
185 bytesRead = Min(count, bufferCount);
187 memcpy(buffer, this.buffer, bytesRead);
188 memcpy(this.buffer, this.buffer + bytesRead, bufferCount - bytesRead);
189 bufferCount -= bytesRead;
190 position += bytesRead;
192 connection.mutex.Release();
196 int Write(byte * buffer, uint size, uint count)
211 bool Puts(char * string)
216 bool Seek(int pos, FileSeekMode mode)
218 uint newPosition = position;
230 connection.mutex.Wait();
232 Request request = connection.AddRequest(4, Request::SizeCallback, this, true);
233 connection.SendSizePacket(handle);
234 connection.mutex.Release();
235 request.semaphore.Wait();
236 connection.mutex.Wait();
237 delete request.semaphore;
238 newPosition = request.readSize;
241 connection.mutex.Release();
245 if(newPosition != position)
247 if(newPosition > position && newPosition < position + bufferCount)
249 memcpy(buffer, buffer + newPosition - position, bufferCount - (newPosition - position));
250 bufferCount -= (newPosition - position);
254 position = newPosition;
272 connection.mutex.Wait();
274 Request request = connection.AddRequest(4, Request::SizeCallback, this, true);
275 connection.SendSizePacket(handle);
276 connection.mutex.Release();
277 request.semaphore.Wait();
278 connection.mutex.Wait();
279 delete request.semaphore;
280 size = request.readSize;
283 connection.mutex.Release();
287 // Returns the last request needed to fill this needed size
288 Request FigureOutMissingData(uint * start, uint * size)
290 Request lastRequest = null;
291 FileServerConnection connection = this.connection;
294 NetDataBlock block { };
298 // Initialize block to what we need
299 block.start = *start;
300 block.end = *start + *size - 1;
302 for(request = connection.requests.first; request; request = request.next)
304 if(request.file == this &&
305 (request.CallBack == Request::ReadResultCallback || request.CallBack == Request::ReadCallback))
307 // If this request overlap with the entire block of data we need
308 if(*start + *size >= request.readStart &&
309 *start + *size < request.readStart + request.readSize)
312 for(block = blocks.first; block; block = next)
315 // If this block overlap with this request
316 if(request.readStart < block.end &&
317 request.readStart + request.readSize >= block.start)
319 // We have all this block, get rid of it
320 if(request.readStart <= block.start &&
321 request.readStart + request.readSize > block.end)
322 blocks.Delete(block);
323 // We're missing data at the end of this block
324 else if(request.readStart <= block.start)
325 block.start = request.readStart;
326 // We're missing data at the beginning of this block
327 else if(request.readStart + request.readSize > block.end)
328 block.end = request.readStart + request.readSize-1;
329 lastRequest = request;
338 // We still need data
339 *start = ((NetDataBlock)blocks.first).start;
340 *size = ((NetDataBlock)blocks.last).end - *start + 1;
342 blocks.Free(null); // eList_Delete(&blocks, NULL); ??
349 void ReadMoreData(uint start, uint size, bool wait)
351 FileServerConnection connection = this.connection;
352 Request lastRequestNeeded;
354 // If it's not already requested, request it
355 connection.mutex.Wait();
356 lastRequestNeeded = FigureOutMissingData(&start, &size);
357 // If we still need to request more
360 Request request = connection.AddRequest(4, Request::ReadResultCallback, this, wait);
361 request.readStart = start;
362 request.readSize = size;
363 connection.SendReadPacket(handle, start, size);
364 connection.mutex.Release();
367 request.semaphore.Wait();
368 delete request.semaphore;
374 if(wait && lastRequestNeeded)
376 if(!lastRequestNeeded.semaphore)
377 lastRequestNeeded.semaphore = Semaphore { };
379 connection.mutex.Release();
380 lastRequestNeeded.semaphore.Wait();
384 FileServerConnection connection;
392 public FileServerConnection ConnectToFileServer(char * hostName, int port)
394 FileServerConnection connection { };
397 if(connection.Connect(hostName, port))
398 connection.fileClientThread.Create();
405 public class FileServerConnection : Socket
409 NetworkClientFile Open(char * fileName, FileOpenMode mode)
411 NetworkClientFile f { };
418 request = AddRequest(4, Request::OpenCallback, f, true);
419 SendOpenPacket(fileName);
421 request.semaphore.Wait();
422 delete request.semaphore;
431 ~FileServerConnection()
435 fileClientThread.terminate = true;
436 //guiApp.SignalEvent();
437 fileClientThread.socket.Disconnect(0);
438 fileClientThread.Wait();
439 delete fileClientThread;
443 uint OnReceive(const byte * buffer, uint count)
446 uint bytesProcessed = 0;
449 while((request = requests.first))
451 uint numBytes = request.numBytes;
452 if(count - bytesProcessed < numBytes)
455 requests.Remove(request);
458 request.CallBack(request, buffer + bytesProcessed);
459 bytesProcessed += numBytes;
464 return bytesProcessed;
467 Request AddRequest(uint numBytes, void (* callback)(Request request, const byte * buffer), NetworkClientFile file, bool createSemaphore)
469 Request request { numBytes = numBytes, CallBack = callback, file = file };
471 request.semaphore = Semaphore { };
472 requests.Add(request);
474 // There is a possibility that OnReceive already got the data for our request
475 // If it is the case, and it had no other request, it will have returned 0.
476 // This mean that OnReceive won't be called again unless more data comes in.
477 // Signaling an event will unblock the thread to make sure OnReceive is called again.
478 guiApp.SignalEvent();
482 // Packet Sending Functions
483 void SendOpenPacket(char * fileName)
485 int len = strlen(fileName);
486 OpenPacket packet = (OpenPacket) new0 byte[sizeof(class OpenPacket) + len];
487 PUTXDWORD(packet.type, NetworkClientPacketType::open);
488 PUTXDWORD(packet.size, sizeof(class OpenPacket) + len);
489 PUTXDWORD(packet.fileNameLen, len);
490 strcpy(packet.fileName, fileName);
491 Send(packet, GETXDWORD(packet.size));
495 void SendReadPacket(int handle, uint start, uint size)
499 XDWORD(NetworkClientPacketType::read),
500 XDWORD(sizeof(class ReadPacket)),
505 Send(packet, GETXDWORD(packet.size));
508 void SendSizePacket(int handle)
512 XDWORD(NetworkClientPacketType::getSize),
513 XDWORD(sizeof(class SizePacket)),
516 Send(packet, GETXDWORD(packet.size));
519 FileClientThread fileClientThread { socket = this };