Created
January 18, 2025 00:38
-
-
Save vastus/43b8c32ced1d0565b3ab5ae51729548b to your computer and use it in GitHub Desktop.
chat using `select`
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* 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