Standard setup for writing C inspired by Casey Muratori, Ryan Fleury, Mr. 4th Programmer, and others in the handmade community.
Você não pode selecionar mais de 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.
 
 
 

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