3. Find the socket with the highest file descriptor for calls to select().
Above also works with different type sockets (e.g. `AF_INET`, `AF_INET6`). It even works if you have different type sockets (e.g. one IPv4 and one IPv6) listening on the same port number. Dual stack sockets are a thing but this approach is more flexible.
## Create sockets
For this example I will be creating two sockets. One of them will have an IPv4 address and the other IPv6. Nothing out of the ordinary here.
```C
/* Create IPv4 socket */
struct sockaddr_in serv_addr4, cli_addr4;
int addrlen4 = sizeof(cli_addr4);
if ((sockfd_v4 = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
if (bind(sockfd_v6, (struct sockaddr *)&serv_addr6, sizeof(serv_addr6)) <0){
perror("bind");
return 1;
}
if (listen(sockfd_v6, BACKLOG) <0){
perror("listen");
return 1;
}
```
## Adding sockets to the set and calling select()
`FD_SET()` is called on each socket. Later on we compare the two sockets to see which one has the highest file descriptor (stored in `maxfd`). Additionally, there's an array of structures that also contain sockets (`clients`), their values are also checked to find the highest descriptor. Finally we can call `select()` with `maxfd + 1`.
```C
for (;;) {
FD_ZERO(&descriptors);
FD_SET(sockfd_v4, &descriptors);
FD_SET(sockfd_v6, &descriptors);
if (sockfd_v6 > sockfd_v4)
maxfd = sockfd_v6;
else
maxfd = sockfd_v4;
/* Add all socket descriptors to the read list. */
for (id = 0; id <maxclients;id++){
if (clients[id] != NULL) {
FD_SET(clients[id]->connfd, &descriptors);
/* Find highest file descriptor, needed for the select function. */
if (clients[id]->connfd > maxfd)
maxfd = clients[id]->connfd;
}
}
if (select(maxfd + 1 ,&descriptors, NULL, NULL, NULL) == -1)
perror("select");
...
```
## Full server example
The snippets in this post are taken from kernal-chat and simplified. You can find the full source code [here](https://gitlab.com/kernal/kchat/-/blob/master/src/kchat.c).