Standard setup for writing C inspired by Casey Muratori, Ryan Fleury, Mr. 4th Programmer, and others in the handmade community.
Nevar pievienot vairāk kā 25 tēmas Tēmai ir jāsākas ar burtu vai ciparu, tā var saturēt domu zīmes ('-') un var būt līdz 35 simboliem gara.
 
 
 

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