commit 385af01b7ea5b08bc7d03841c17b156afaa9401f
Author: evenfri <evenfri256@gmail.com>
Date: Fri, 28 Aug 2026 00:46:38 +0800
initial commit
Diffstat:
| A | .gitignore | | | 3 | +++ |
| A | build.sh | | | 21 | +++++++++++++++++++++ |
| A | client.c | | | 127 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| A | server.c | | | 150 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
4 files changed, 301 insertions(+), 0 deletions(-)
diff --git a/.gitignore b/.gitignore
@@ -0,0 +1,3 @@
+*.o
+server
+client
diff --git a/build.sh b/build.sh
@@ -0,0 +1,21 @@
+#!/bin/sh
+
+cflags="-Wall -Wextra"
+
+set -e
+
+compile() {
+ f="$1"
+
+ echo "cc $f"
+ gcc -c $cflags $f
+}
+
+compile server.c
+compile client.c
+
+echo "ld server"
+gcc -o server server.o
+
+echo "ld client"
+gcc -o client client.o
diff --git a/client.c b/client.c
@@ -0,0 +1,127 @@
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <poll.h>
+
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#define BUFFER_SIZE 1024
+
+int main(int argc, char *argv[]) {
+ int fd;
+ int n;
+ int result;
+ int message_size;
+ struct sockaddr_in addr;
+ struct pollfd fds[2];
+ char buffer[BUFFER_SIZE];
+ char message[BUFFER_SIZE];
+ char *nickname;
+
+ if (argc > 1) {
+ nickname = argv[1];
+ } else {
+ nickname = "Client";
+ }
+
+ fd = socket(AF_INET, SOCK_STREAM, 0);
+ if (fd == -1) {
+ perror("socket");
+ return 1;
+ }
+
+ memset(&addr, 0, sizeof(addr));
+
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons(8080);
+
+ if (inet_pton(AF_INET, "127.0.0.1", &addr.sin_addr) != 1) {
+ fprintf(stderr, "invalid address\n");
+ close(fd);
+ return 1;
+ }
+
+ if (connect(fd, (struct sockaddr *)&addr, sizeof(addr)) == -1) {
+ perror("connect");
+ close(fd);
+ return 1;
+ }
+
+ printf("connected\n");
+ printf("> ");
+ fflush(stdout);
+
+ fds[0].fd = fd;
+ fds[0].events = POLLIN;
+
+ fds[1].fd = STDIN_FILENO;
+ fds[1].events = POLLIN;
+
+ while (1) {
+ result = poll(fds, 2, -1);
+
+ if (result == -1) {
+ perror("poll");
+ break;
+ }
+
+ /*
+ * Server sent something.
+ */
+ if (fds[0].revents & POLLIN) {
+ n = recv(fd, buffer, sizeof(buffer) - 1, 0);
+
+ if (n == -1) {
+ perror("recv");
+ break;
+ }
+
+ if (n == 0) {
+ printf("\nserver disconnected\n");
+ break;
+ }
+
+ buffer[n] = '\0';
+
+ printf("\r%s", buffer);
+ printf("> ");
+ fflush(stdout);
+ }
+
+ /*
+ * User typed something.
+ */
+ if (fds[1].revents & POLLIN) {
+ if (fgets(buffer, sizeof(buffer), stdin) == NULL) {
+ break;
+ }
+
+ message_size = snprintf(
+ message,
+ sizeof(message),
+ "%s: %s",
+ nickname,
+ buffer
+ );
+
+ if (message_size < 0 || message_size >= (int)sizeof(message)) {
+ fprintf(stderr, "message too long\n");
+ continue;
+ }
+
+ if (send(fd, message, message_size, 0) == -1) {
+ perror("send");
+ break;
+ }
+
+ printf("> ");
+ fflush(stdout);
+ }
+ }
+
+ close(fd);
+
+ return 0;
+}
diff --git a/server.c b/server.c
@@ -0,0 +1,150 @@
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <poll.h>
+
+#include <sys/socket.h>
+#include <netinet/in.h>
+
+#define MAX_CLIENTS 16
+#define BUFFER_SIZE 1024
+
+int main() {
+ int server_fd;
+ int client_fd;
+ int client_count;
+ int i;
+ int j;
+ int n;
+ int result;
+ struct sockaddr_in addr;
+ struct pollfd fds[MAX_CLIENTS + 1];
+ char buffer[BUFFER_SIZE];
+
+ server_fd = socket(AF_INET, SOCK_STREAM, 0);
+ if (server_fd == -1) {
+ perror("socket");
+ return 1;
+ }
+
+ memset(&addr, 0, sizeof(addr));
+
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons(8080);
+ addr.sin_addr.s_addr = htonl(INADDR_ANY);
+
+ if (bind(server_fd, (struct sockaddr *)&addr, sizeof(addr)) == -1) {
+ perror("bind");
+ close(server_fd);
+ return 1;
+ }
+
+ if (listen(server_fd, 16) == -1) {
+ perror("listen");
+ close(server_fd);
+ return 1;
+ }
+
+ memset(fds, 0, sizeof(fds));
+
+ fds[0].fd = server_fd;
+ fds[0].events = POLLIN;
+
+ client_count = 0;
+
+ printf("listening on port 8080\n");
+
+ while (1) {
+ result = poll(fds, client_count + 1, -1);
+
+ if (result == -1) {
+ perror("poll");
+ break;
+ }
+
+ /*
+ * New client.
+ */
+ if (fds[0].revents & POLLIN) {
+ client_fd = accept(server_fd, NULL, NULL);
+
+ if (client_fd == -1) {
+ perror("accept");
+ } else if (client_count >= MAX_CLIENTS) {
+ printf("too many clients\n");
+ close(client_fd);
+ } else {
+ client_count++;
+
+ fds[client_count].fd = client_fd;
+ fds[client_count].events = POLLIN;
+
+ printf("client %d connected\n", client_fd);
+ }
+ }
+
+ /*
+ * Existing clients.
+ */
+ for (i = 1; i <= client_count; i++) {
+ if (!(fds[i].revents & POLLIN)) {
+ continue;
+ }
+
+ n = recv(fds[i].fd, buffer, sizeof(buffer), 0);
+
+ if (n == -1) {
+ perror("recv");
+
+ close(fds[i].fd);
+
+ for (j = i; j < client_count; j++) {
+ fds[j] = fds[j + 1];
+ }
+
+ client_count--;
+ i--;
+
+ continue;
+ }
+
+ if (n == 0) {
+ printf("client %d disconnected\n", fds[i].fd);
+
+ close(fds[i].fd);
+
+ for (j = i; j < client_count; j++) {
+ fds[j] = fds[j + 1];
+ }
+
+ client_count--;
+ i--;
+
+ continue;
+ }
+
+ /*
+ * Broadcast.
+ */
+ printf("client %d sent %d bytes\n", fds[i].fd, n);
+
+ for (j = 1; j <= client_count; j++) {
+ if (j == i) {
+ continue;
+ }
+
+ if (send(fds[j].fd, buffer, n, 0) == -1) {
+ perror("send");
+ }
+ }
+ }
+ }
+
+ for (i = 1; i <= client_count; i++) {
+ close(fds[i].fd);
+ }
+
+ close(server_fd);
+
+ return 0;
+}