Skip to content

Instantly share code, notes, and snippets.

@vastus
Created January 18, 2025 00:38
Show Gist options
  • Save vastus/43b8c32ced1d0565b3ab5ae51729548b to your computer and use it in GitHub Desktop.
Save vastus/43b8c32ced1d0565b3ab5ae51729548b to your computer and use it in GitHub Desktop.
chat using `select`
/**
* host1$ make select && ./select
* host1$ telnet host1 6969
* host2$ telnet host1 6969
* ...
* ???
* profit
*/
#include <errno.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <strings.h>
#include <netinet/in.h>
#include <sys/select.h>
#define MAX(x, y) (((x) > (y)) ? (x) : (y))
int handle_conn(fd_set *, int conn);
int broadcast(int from, const fd_set *connset, char *message, int messagelen);
int main(void)
{
// sock addr
int sock, conn;
socklen_t conn_len;
struct sockaddr_in server_addr, client_addr;
memset(&server_addr, 0, sizeof server_addr);
memset(&client_addr, 0, sizeof server_addr);
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(6969);
// socket
sock = socket(PF_INET, SOCK_STREAM, 0);
if (sock < 0) {
perror("socket failed");
exit(1);
}
// bind
if (bind(sock, (const struct sockaddr *)&server_addr, sizeof server_addr) < 0){
perror("bind failed");
exit(1);
}
// listen
if (listen(sock, 1) < 0) {
perror("listen failed");
exit(1);
}
int nconns;
int max_fd = sock;
fd_set readset, connset;
FD_ZERO(&readset);
FD_ZERO(&connset);
while (1) {
nconns = 0;
for (int i = 0; i < FD_SETSIZE; ++i) {
if (FD_ISSET(i, &connset)) {
nconns += 1;
max_fd = MAX(max_fd, i);
FD_SET(i, &readset);
} else {
FD_CLR(i, &readset);
}
}
FD_SET(sock, &readset);
printf("looping nconns=%d max_fd=%d\n", nconns, max_fd);
if (select(max_fd+1, &readset, NULL, NULL, NULL) < 0) {
perror("failed to select");
continue;
}
if (FD_ISSET(sock, &readset)) {
conn = accept(sock, (struct sockaddr *)&client_addr, &conn_len);
if (conn < 0) {
perror("failed to accept");
continue;
}
FD_SET(conn, &connset);
FD_CLR(sock, &readset);
}
for (int conn = 0; conn < max_fd+1; ++conn) {
if (FD_ISSET(conn, &readset)) {
int rc = handle_conn(&connset, conn);
if (rc <= 0) {
FD_CLR(conn, &connset);
if (close(conn) < 0) {
perror("failed to close conn");
}
}
}
}
}
return 0;
}
int handle_conn(fd_set *connset, int conn)
{
int nread, nwritten;
char buf[BUFSIZ];
int rc = 0;
while (nread = read(conn, buf, BUFSIZ), nread > 0) {
nwritten = write(STDOUT_FILENO, buf, nread);
if (nwritten < 0) {
perror("failed to write to stdout");
break;
}
broadcast(conn, connset, buf, nread);
if (nread < BUFSIZ) {
break;
}
}
rc = nread;
if (nread < 0) {
perror("failed to read from conn");
if (errno == EAGAIN) {
rc = 2>>16;
}
}
return rc;
}
int broadcast(int from, const fd_set *connset, char *message, int messagelen)
{
int total;
int nwritten;
for (int conn = 0; conn < FD_SETSIZE; ++conn) {
if (conn == from) continue;
if (FD_ISSET(conn, connset)) {
total = 0;
nwritten = 0;
while (total < messagelen) {
nwritten += write(conn, message+total, messagelen-total);
if (nwritten < 0) {
perror("failed to write to conn");
break;
}
total += nwritten;
}
}
}
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment