Standard setup for writing C inspired by Casey Muratori, Ryan Fleury, Mr. 4th Programmer, and others in the handmade community.
Nie możesz wybrać więcej, niż 25 tematów Tematy muszą się zaczynać od litery lub cyfry, mogą zawierać myślniki ('-') i mogą mieć do 35 znaków.
 
 
 

206 wiersze
7.2 KiB

  1. #include "core.h"
  2. #define DJSTD_BASIC_ENTRY
  3. #include "core.c"
  4. #include "signal.h"
  5. Server *openServer = NULL;
  6. SocketList *openSockets = NULL;
  7. void handleSigint(int dummy) {
  8. if (openServer) {
  9. println("");
  10. println("Closing server socket.");
  11. serverClose(openServer);
  12. println("Success.");
  13. }
  14. if (openSockets && openSockets->length) {
  15. println("");
  16. println("Closing open sockets.");
  17. for (EachEl(*openSockets, Socket, socket)) {
  18. socketClose(socket);
  19. }
  20. println("Success.");
  21. }
  22. signal(SIGINT, SIG_DFL);
  23. raise(SIGINT);
  24. }
  25. typedef struct ChatClient ChatClient;
  26. struct ChatClient {
  27. Socket *socket;
  28. string nickname;
  29. };
  30. DefineList(ChatClient, ChatClient);
  31. void startServer(Arena *arena, int32 port) {
  32. println("Starting server...");
  33. Server server = serverInit((ServerInitInfo){
  34. .concurrentClients=2,
  35. .port=port,
  36. .memory=Megabytes(64),
  37. .maxEvents=64,
  38. });
  39. openServer = &server;
  40. serverListen(&server);
  41. if (server.listening) {
  42. println("Listening on port %d", port);
  43. }
  44. Arena *serverLoopArena = arenaAlloc(Megabytes(64));
  45. ChatClientList chatClients = PushListZero(arena, ChatClientList, 256);
  46. ServerEvent *nextEvent;
  47. Forever {
  48. nextEvent = serverGetNextEvent(&server);
  49. switch (nextEvent->type) {
  50. case ServerEventType_AcceptClient: {
  51. Socket *client = serverAccept(&server);
  52. if (client != NULL) {
  53. println("New client connected from %d", client->address);
  54. }
  55. break;
  56. };
  57. case ServerEventType_ClientMessage: {
  58. StringResult clientMsg = socketReadStr(serverLoopArena, nextEvent->tClientMessage.client);
  59. ChatClient *chatClient = NULL;
  60. if (clientMsg.valid) {
  61. if (strStartsWith(clientMsg.result, s("hello-"))) {
  62. StringList nickSplit = strSplit(serverLoopArena, s("-"), clientMsg.result);
  63. if (nickSplit.length == 2 && nickSplit.data[1].length > 0) {
  64. string newNick = PushString(arena, nickSplit.data[1].length);
  65. newNick.length = nickSplit.data[1].length;
  66. memcpy(newNick.str, nickSplit.data[1].str, nickSplit.data[1].length);
  67. ChatClient newChatClient = (ChatClient){
  68. .socket=nextEvent->tClientMessage.client,
  69. .nickname=newNick,
  70. };
  71. AppendList(&chatClients, newChatClient);
  72. println("Client from %d calls themselves \"%S\"", newChatClient.socket->address, newChatClient.nickname);
  73. }
  74. } else {
  75. for (EachEl(chatClients, ChatClient, maybeChatClient)) {
  76. if (maybeChatClient->socket->handle == nextEvent->tClientMessage.client->handle) {
  77. chatClient = maybeChatClient;
  78. }
  79. }
  80. if (chatClient != NULL) {
  81. if (strStartsWith(clientMsg.result, s("say-"))) {
  82. StringList saySplit = strSplit(arena, s("-"), clientMsg.result);
  83. if (saySplit.length == 2 && saySplit.data[1].length > 0) {
  84. string broadcast = strPrintf(serverLoopArena, "%S says:\n%S", chatClient->nickname, saySplit.data[1]);
  85. for (EachEl(server.clients, Socket, client)) {
  86. socketWriteStr(client, broadcast);
  87. }
  88. }
  89. } else {
  90. // Invalid client message
  91. }
  92. }
  93. }
  94. }
  95. break;
  96. };
  97. case ServerEventType_None:
  98. default:
  99. break;
  100. }
  101. arenaFreeFrom(serverLoopArena, 0);
  102. }
  103. println("Shutting down chat.");
  104. serverClose(&server);
  105. }
  106. void clearStdInLn() {
  107. print("\r");
  108. print(ANSI_INSTRUCTION(J));
  109. }
  110. void clearStdInLnAfterInput() {
  111. print("\r");
  112. print(ANSI_INSTRUCTION(A));
  113. print(ANSI_INSTRUCTION(J));
  114. }
  115. void startClient(Arena *arena, string addr, int32 port, string nickname) {
  116. fcntl(0, F_SETFL, fcntl(0, F_GETFL) | O_NONBLOCK);
  117. println("Connecting to server at [%S]:%d with nickname \"%S\"", addr, port, nickname);
  118. Socket server = socketConnect(arena, (SocketConnectInfo){ .address=addr, .port=port });
  119. if (server.closed) {
  120. println("Connection error. Closing.");
  121. } else {
  122. println("Connected successfully");
  123. string message = strPrintf(arena, "hello-%S", nickname);
  124. CharList inputBuf = PushList(arena, CharList, 512);
  125. socketWriteStr(&server, message);
  126. print("(you)> ");
  127. Forever {
  128. Scratch scratch = scratchStart(&arena, 1);
  129. int32 numRead = read(0, inputBuf.data + inputBuf.length, inputBuf.capacity - inputBuf.length);
  130. if (numRead >= 0) {
  131. inputBuf.length += numRead;
  132. if (inputBuf.data[inputBuf.length - 1] == '\n') {
  133. clearStdInLnAfterInput();
  134. socketWriteStr(&server, strPrintf(scratch.arena, "say-%S", (string){.str=inputBuf.data,.length=inputBuf.length}));
  135. inputBuf.length = 0;
  136. }
  137. }
  138. StringResult serverMsg = socketReadStr(scratch.arena, &server);
  139. if (serverMsg.valid && serverMsg.result.length > 0) {
  140. clearStdInLn();
  141. println("%S", serverMsg.result);
  142. print("(you)> %S", (string){.str=inputBuf.data, inputBuf.length});
  143. }
  144. scratchEnd(scratch);
  145. }
  146. }
  147. socketClose(&server);
  148. }
  149. int djstd_entry(Arena *arena, StringList args) {
  150. signal(SIGINT, &handleSigint);
  151. bool argumentErr = true;
  152. bool isServer = strEql(args.data[0], s("server"));
  153. bool isClient = strEql(args.data[0], s("client"));
  154. if (isServer) {
  155. Int32Result portParsed = parsePositiveInt(args.data[1]);
  156. if (portParsed.valid) {
  157. startServer(arena, portParsed.result);
  158. argumentErr = false;
  159. }
  160. } else if (isClient) {
  161. if (args.length == 3) {
  162. StringList split = strSplit(arena, s("]:"), args.data[1]);
  163. if (split.length == 2) {
  164. Int32Result portParsed = parsePositiveInt(split.data[1]);
  165. string addr = strSlice(split.data[0], 1, split.data[0].length);
  166. string nickname = args.data[2];
  167. if (portParsed.valid && addr.length > 0 && nickname.length > 0) {
  168. startClient(arena, addr, portParsed.result, nickname);
  169. argumentErr = false;
  170. }
  171. }
  172. }
  173. }
  174. if (argumentErr) {
  175. println("Usage:");
  176. println("server [PORT]");
  177. println("OR");
  178. println("client [REMOTE_ADDRESS:PORT] [NICKNAME]");
  179. }
  180. return 0;
  181. }