I started learning socket programming these days, but I got stuck in this glitch. I have used SO_REUSEADDR
in my code, and the port I used is 6666
, when I tried to run my code, the complier told me bind() failed: Address already in use
. When I tried lsof -i:6666
but there is nothing.
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <errno.h>
#include <libc.h>
// When port = 8888, bind() failed: Illegal byte sequence
const int DEFAULT_PORT = 6666;
int main() {
int listen_fd, max_fd, new_fd;
fd_set master_set, working_set;
int end_server = 0;
int close_conn;
char buffer[100];
listen_fd = socket(PF_LOCAL, SOCK_STREAM, 0);
// -1 is returned if an error occurs.
if(listen_fd == -1) {
perror("socket() failed");
exit(-1);
}
// Make the address and port bound to listen socket reusable.
const int on = 1;
int rc = setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on));
if (rc < 0) {
perror("setsockopt() failed");
close(listen_fd);
exit(-1);
}
// Set socket to be nonblocking. All the sockets for
// the incoming connections will also be nonblocking since
// they will inherit that state from the listening socket.
rc = ioctl(listen_fd, FIONBIO, (char *)&on);
if (rc < 0)
{
perror("ioctl() failed");
close(listen_fd);
exit(-1);
}
// struct sockaddr is a general structure valid for any protocol.
// https://qr.ae/pv4WMc
// https://stackoverflow.com/a/21099172/16317008
struct sockaddr_in addr;
memset(&addr, 0, sizeof(addr));
// AF_LOCAL is used for native communication and AF_INET is used for network communication
addr.sin_family = AF_LOCAL;
// Let the system automatically obtain the IP address of the machine
addr.sin_addr.s_addr = htonl(INADDR_ANY);
addr.sin_port = htons(DEFAULT_PORT);
rc = bind(listen_fd, (const struct sockaddr *)&addr, sizeof(addr));
if(rc == -1) {
perror("bind() failed");
close(listen_fd);
exit(-1);
}
rc = listen(listen_fd, 30);
if(rc == -1) {
perror("listen() failed");
close(listen_fd);
exit(-1);
}
// Initialize fd_set
max_fd = listen_fd;
FD_ZERO(&master_set);
FD_SET(listen_fd, &master_set);
// Initialize the timeval struct to 3 minutes.
// If no activity after 3 minutes this program will end.
struct timeval timeout;
timeout.tv_sec = 3 * 60;
timeout.tv_usec = 0;
int desc_ready;
do {
// Copy the master fd_set over to the working fd_set.
memcpy(&working_set, &master_set, sizeof(master_set));
// On return, select() replaces the given descriptor sets with subsets consisting of those
// descriptors that are ready for the requested operation.
printf("Waiting on select()...\n");
rc = select(max_fd + 1, &working_set, NULL, NULL, &timeout);
if(rc < 0) {
perror("select() failed");
break;
}
// Check to see if the 3 minutes time out expired.
if (rc == 0) {
printf("select() timed out. End program.\n");
break;
}
// One or more descriptors are readable.
// Need to determine which ones they are.
desc_ready = rc;
for (int i = 0; i <= max_fd && desc_ready > 0; ++i) {
// Check to see if this descriptor is ready.
// FD_ISSET(fd, &fdset) is non-zero if fd is a member of fdset, zero otherwise.
if (FD_ISSET(i, &working_set)){
desc_ready -= 1;
// Check to see if this is the listening socket.
if(i == listen_fd) {
printf("Listening socket is readable\n");
do {
// Accept each incoming connection. If accept fails with EWOULDBLOCK,
// then we have accepted all of them. Any other failure on accept
// will cause us to end the server.
new_fd = accept(listen_fd, NULL, NULL);
if (new_fd < 0) {
if (errno != EWOULDBLOCK) {
perror("accept() failed");
end_server = 1;
}
break;
}
// Add the new incoming connection to the master read set.
printf("New incoming connection - %d\n", new_fd);
FD_SET(new_fd, &master_set);
if (new_fd > max_fd)
max_fd = new_fd;
} while (new_fd != -1);
}
/****************************************************/
/* This is not the listening socket, therefore an */
/* existing connection must be readable */
/****************************************************/
else {
printf(" Descriptor %d is readable\n", i);
close_conn = 0;
// Receive all incoming data on this socket
// before we loop back and call select again.
do {
// Receive data on this connection until the recv fails with EWOULDBLOCK.
// If any other failure occurs, we will close the connection.
rc = (int)recv(i, buffer, sizeof(buffer), 0);
if (rc < 0) {
if (errno != EWOULDBLOCK) {
perror("recv() failed");
close_conn = 1;
}
break;
}
// Check to see if the connection has been closed by the client.
if (rc == 0) {
printf("Connection closed\n");
close_conn = 1;
break;
}
// Data was received.
int len = rc;
printf("%d bytes received\n", len);
// Echo the data back to the client.
rc = (int)send(i, buffer, len, 0);
if (rc < 0) {
perror("send() failed");
close_conn = 1;
break;
}
} while (1);
/*************************************************/
/* If the close_conn flag was turned on, we need */
/* to clean up this active connection. This */
/* clean up process includes removing the */
/* descriptor from the master set and */
/* determining the new maximum descriptor value */
/* based on the bits that are still turned on in */
/* the master set. */
/*************************************************/
if (close_conn) {
close(i);
FD_CLR(i, &master_set);
// Determining the new maximum descriptor value
// based on the bits that are still turned on in the master set.
if (i == max_fd) {
while (FD_ISSET(max_fd, &master_set) == 0)
max_fd -= 1;
}
}
} // End of existing connection is readable.
} // End of if (FD_ISSET(i, &working_set))
} // End of loop through selectable descriptors.
}while (end_server == 0);
// Clean up all the sockets that are open.
for (int i=0; i <= max_fd; ++i) {
if (FD_ISSET(i, &master_set))
close(i);
}
return 0;
}