** selectserver.c -- 一個 cheezy 的多人聊天室 server
#define PORT "9034" // 我們正在 listen 的 port
// 取得 sockaddr,IPv4 或 IPv6:
void *get_in_addr(struct sockaddr *sa)
if (sa->sa_family == AF_INET) {
return &(((struct sockaddr_in*)sa)->sin_addr);
return &(((struct sockaddr_in6*)sa)->sin6_addr);
fd_set master; // master file descriptor 清單
fd_set read_fds; // 給 select() 用的暫時 file descriptor 清單
int fdmax; // 最大的 file descriptor 數目
int listener; // listening socket descriptor
int newfd; // 新接受的 accept() socket descriptor
struct sockaddr_storage remoteaddr; // client address
char buf[256]; // 儲存 client 資料的緩衝區
char remoteIP[INET6_ADDRSTRLEN];
int yes=1; // 供底下的 setsockopt() 設定 SO_REUSEADDR
struct addrinfo hints, *ai, *p;
FD_ZERO(&master); // 清除 master 與 temp sets
// 給我們一個 socket,並且 bind 它
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE;
if ((rv = getaddrinfo(NULL, PORT, &hints, &ai)) != 0) {
fprintf(stderr, "selectserver: %s\n", gai_strerror(rv));
for(p = ai; p != NULL; p = p->ai_next) {
listener = socket(p->ai_family, p->ai_socktype, p->ai_protocol);
// 避開這個錯誤訊息:"address already in use"
setsockopt(listener, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int));
if (bind(listener, p->ai_addr, p->ai_addrlen) < 0) {
// 若我們進入這個判斷式,則表示我們 bind() 失敗
fprintf(stderr, "selectserver: failed to bind\n");
freeaddrinfo(ai); // all done with this
if (listen(listener, 10) == -1) {
// 將 listener 新增到 master set
FD_SET(listener, &master);
// 持續追蹤最大的 file descriptor
fdmax = listener; // 到此為止,就是它了
read_fds = master; // 複製 master
if (select(fdmax+1, &read_fds, NULL, NULL, NULL) == -1) {
for(i = 0; i <= fdmax; i++) {
if (FD_ISSET(i, &read_fds)) { // 我們找到一個!!
// handle new connections
addrlen = sizeof remoteaddr;
(struct sockaddr *)&remoteaddr,
FD_SET(newfd, &master); // 新增到 master set
if (newfd > fdmax) { // 持續追蹤最大的 fd
printf("selectserver: new connection from %s on "
inet_ntop(remoteaddr.ss_family,
get_in_addr((struct sockaddr*)&remoteaddr),
remoteIP, INET6_ADDRSTRLEN),
if ((nbytes = recv(i, buf, sizeof buf, 0)) <= 0) {
// got error or connection closed by client
printf("selectserver: socket %d hung up\n", i);
FD_CLR(i, &master); // 從 master set 中移除
for(j = 0; j <= fdmax; j++) {
if (FD_ISSET(j, &master)) {
if (j != listener && j != i) {
if (send(j, buf, nbytes, 0) == -1) {
} // END handle data from client
} // END got new incoming connection
} // END looping through file descriptors
} // END for( ; ; )--and you thought it would never end!