6chan is an anonymous messaging board (like a group chat / forum) with file sharing. 
To ensure users' privacy, all user and message data is processed in RAM, so it's all destroyed once server is shut down.
Supports sockets as well as named pipes.
For pipe-enabled version, check with-pipe branch. 
The master branch includes socket version only.
Run server:
- server.exe
- server.exe [port]
- server.exe [host] [port]
- server.exe [pipe](for pipe version)
Default is 127.0.0.1:5000 (for sockets), \\.\pipe\6chan (for pipes) 
Server writes logs to stderr, which can be piped to file: server.exe 2> server.log
Run client:
- client.exe
- client.exe [port]
- client.exe [host] [port]
- client.exe [pipe](for pipe version)
Client connects to host:port or pipe and establishes session. On connect, message history syncs automatically.
- /file- upload file
- /dl <id>- download file or message by- #id
- /sync <id>- sync manually, starting after- #id(unused, unless network errors occur)
- /q- quit
- -D DEBUG(- ./CMakeLists.txt) to build debug version: extended logging, slower polling rates
- -D USE_COLOR(- ./client/CMakeLists.txt) to colorize console text (recommended)
- -D USE_PIPES(- ./CMakeLists.txt) to build pipe version. Blocking mode (- PIPE_WAIT) is used.
List of server controllers:
- 
startServer()- Initialize socket(), bind(), listen() (for socket version)
- Initialize global Client List and Message History
- Call startAllControllers()
- Clean up global lists
 
- 
startAllControllers()- Initialize Critical Section for Message History
- Create thread for clientMgmtController()
- Wait for stdin
- Close socket and set cv_stop flag
- Wait for clientMgmtController()
 
- 
clientMgmtController()- Call accept() in loop (for socket version), CreateFile() and ConnectNamedPipe() (for pipe version)
- For each client socket / pipe, create messageController()thread
- If socket / pipe is closed, wait for all controller threads
 
- 
messageController()- Receive client data in loop
- Call parseMessageFromClient()to form a Message from raw buffer
- Process message based on message type:
- Sync: for each new message (if any) call sendMessageToClient(), separate sent messages by\0, end with\0\0
- Download: find file in Message History, call sendFileToClient()
- File, Message: add record to Message History
 
- Sync: for each new message (if any) call 
 
List of client routines and services:
- 
runClient()- Initialize socket(), connect() (for socket version), _Create
- Call startAllServices()
 
- 
startAllServices()- Initialize Critical Sections for send() and recv()
- Create event for client stop
- Create threads for services:
- syncService()
- sendService()
 
- Wait for event
- Close socket
- Wait for remaining threads
 
- 
syncService()- Send /sync <last_msg_id>command in loop (uses lock for send())
- Receive until \0, print message (uses lock for recv())
- Update last_msg_idbased on last incoming message id
- Stop receiving on \0\0(message is empty)
- Sleep for polling delay
 
- Send 
- 
sendService()- Process user input in loop
- Parse commands:
- /file- call- clientUploadFile(), uses lock for send()
- /dl <id>- call- clientDownloadFile(), uses locks for send() and recv()
- /q- set event, set stop flag, and return
 
- If input is not a command, send message. uses lock for send()
 
Both client and server use the following recv() wrappers:
- recvuntil(char delim): Allocate buffer and receive until- delimcharacter encounters
- recvlen(int len): Allocate buffer and receive exactly- lenbytes
They work with named pipes as well, provided USE_PIPES flag is set.
They use thread-local (for server) or shared (for client) static buffer. Buffer state persists between calls.
buf = persistent buffer, shared by recvuntil() and recvlen()
end = current index of last byte in buffer 
size = current allocated buffer size
Here is pseudocode for recvuntil(). Function recvlen() has similar algorithm.
      if buf == NULL:
          allocate buf
          size = BASE_LEN
      while True:
          if delim in buf[0:end]:
              pos = index of delim
              allocate 'return buffer', copy first `pos` bytes
              pop first `pos` bytes from buffer, shift remaining data
              shrink buffer (if needed), decrease size
              ptr = 'return buffer'
              return len
          else:
              n = recv( &buf[end] <- (size-end) bytes )
              end = end + n
              if end > MAX_SIZE:
                  clear buffer, return 1
              if end >= size:
                  extend buffer, increase size
     0             end       end+n       size          
buf  ===============-----------|-----------
            recv -> ============
     0                         end       size          
buf  ===========================-----------
                        recv -> ==================
     0                         end                                 size
buf  ======================================--------------------------
                                   recv -> =======
     0                                          end    pos         size
buf  =============================================------|------------
                                          recv -> ======d=======
     0                                                 pos    end  size
buf  ===================================================d=======-----
     v    v    v    v    v    v    v    v    v    v    v
ret  ===================================================
     0     end                                                     size
buf  ========--------------------------------------------------------
     0     end                      size
buf  ========-------------------------
This lab uses recvuntil('\0') for messages and commands, which means data is received dynamically. Though it works fine, it's not the best approach, as we don't know message length beforehand, and message type is parsed from /-like commands.
Better implement your own TV / TLV protocol for messages (use some tags from Message struct) and receive values with recvlen().
And unicode... Technically should work but needs a bit of polishing. I'm too lazy for that ฅ ^•ﻌ•^ ฅ ° 。
 ˚∧_∧   +        —̳͟͞͞💗
 ( •‿•)つ  —̳͟͞͞ 💗         —̳͟͞͞💗 +
 (つ <               —̳͟͞͞💗
 |   _つ     +  —̳͟͞͞💗         —̳͟͞͞💗 ˚
`し´