Standard setup for writing C inspired by Casey Muratori, Ryan Fleury, Mr. 4th Programmer, and others in the handmade community.
Du kannst nicht mehr als 25 Themen auswählen Themen müssen entweder mit einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.
 
 
 

206 Zeilen
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. }