Ich habe select() schon als eierlegende Wollmilchsau angekündigt. Eigentlich hat select() so viele verschiedene Aspekte und Anwendungsmöglichkeiten, daß man ihm mit einem Beispiel gar nicht gerecht werden kann. Ich habe mich trotzdem für einen Fall entschieden, der meiner Meinung nach am besten zeigt, was alles drinsteckt.
In diesem Fall handelt es sich um einen Server, der beliebig viele Clients annehmen kann. Diese werden in einer Liste abgelegt, und können mit den anderen jeweils kommunizieren. Damit es nicht so langweilig wird, habe ich auch noch eine Beteiligung des Servers vorgesehen, der über die Tastatur von der Konsole, von der aus er gestartet wurde, auch als eine Art Client fungieren kann (klappt natürlich nur unter UNIX, für Windows den Teil rauswerfen).
Das Thema Timeouts ist da leider nich mit drin, aber ich denke das kann sich jeder denken (oder?).
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/fcntl.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <unistd.h>
struct list_entry {
int sock;
struct list_entry *next;
};
struct list_type {
struct list_entry *data;
unsigned count;
};
#define BUF_SIZ 4096
#define PORT 7000
void init_list(struct list_type *list)
{
list->data = NULL;
list->count = 0;
}
int add_client(struct list_type *list, int sock)
{
struct list_entry *n;
n = malloc(sizeof(*n));
if (!n)
{
perror("malloc() failed");
return 1;
}
n->sock = sock;
n -> next = list->data;
list->data = n;
list->count++;
return 0;
}
int remove_client(struct list_type *list, int sock)
{
struct list_entry *lz, *lst = NULL;
if (!list->count)
return 1;
for (lz = list->data; lz; lz = lz->next)
{
if (lz->sock == sock)
break;
lst = lz;
}
if (!lz)
return 1;
if (lst)
lst->next = lz->next;
else
list->data = lz->next;
free(lz);
list->count--;
return 0;
}
int fill_set(fd_set *fds, struct list_type *list)
{
int max = 0;
struct list_entry *lz;
for (lz = list->data; lz; lz = lz->next)
{
if (lz->sock > max)
max = lz->sock;
FD_SET(lz->sock, fds);
}
return max;
}
int get_sender(fd_set *fds)
{
int i = 0;
while(!FD_ISSET(i, fds))
i++;
return i;
}
/* Sonderfall: STDIN_FILENO muss natuerlich auf STDOUT_FILENO
* beschrieben werden.
*/
int send_all(char *msg, int len, struct list_type *list, int sender)
{
struct list_entry *lz;
for (lz = list->data; lz; lz = lz->next)
{
if (lz->sock == sender)
continue;
if (lz->sock == STDIN_FILENO)
write(STDOUT_FILENO, msg, len);
else
write(lz->sock, msg, len);
}
return 0;
}
int main_loop(int s)
{
int c, max, sender, bytes;
fd_set fds;
struct list_type list;
char buf[BUF_SIZ];
init_list(&list);
add_client(&list, STDIN_FILENO);
for(;;)
{
FD_ZERO(&fds);
max = fill_set(&fds, &list);
FD_SET(s, &fds);
if (s > max)
max = s;
select(max + 1, &fds, NULL, NULL, NULL);
if (FD_ISSET(s, &fds))
{
c = accept(s, NULL, 0);
add_client(&list, c);
}
else
{
sender = get_sender(&fds);
bytes = read(sender, buf, sizeof(buf));
if (bytes == 0)
remove_client(&list, sender);
else
send_all(buf, bytes, &list, sender);
}
}
}
int main(void)
{
int s;
struct sockaddr_in addr;
s = socket(PF_INET, SOCK_STREAM, 0);
if (s == -1)
{
perror("socket() failed");
return 1;
}
addr.sin_addr.s_addr = INADDR_ANY;
addr.sin_port = htons(PORT);
addr.sin_family = AF_INET;
if (bind(s, (struct sockaddr*)&addr, sizeof(addr)) == -1)
{
perror("bind() failed");
return 2;
}
if (listen(s, 3) == -1)
{
perror("listen() failed");
return 3;
}
return main_loop(s);
}