socket option: SO_REUSEADDR选项一例
程序代码:
#include <stdio.h> /* */ #include <stdlib.h> /* exit() */ #include <string.h> /* memset(), memcpy() */ #include <sys/utsname.h> /* uname() */ #include <sys/types.h> #include <sys/socket.h> /* socket(), bind(), listen(), accept() */ #include <netinet/in.h> #include <arpa/inet.h> #include <netdb.h> #include <unistd.h> /* fork(), write(), close() */ /* * prototypes */ int _GetHostName(char *buffer, int length); /* * constants */ const char MESSAGE[] = "Hello, World!\n"; const int BACK_LOG = 5; int main(int argc, char *argv[]) { int serverSocket = 0, on = 0, port = 0, status = 0, childPid = 0; struct hostent *hostPtr = NULL; char hostname[80] = ""; struct sockaddr_in serverName = { 0 }; if (2 != argc) { fprintf(stderr, "Usage: %s <port>\n", argv[0]); exit(1); } port = atoi(argv[1]); serverSocket = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); if (-1 == serverSocket) { perror("socket()"); exit(1); } /* * turn off bind address checking, and allow * port numbers to be reused - otherwise * the TIME_WAIT phenomenon will prevent * binding to these address.port combinations * for (2 * MSL) seconds. */ on = 1; status = setsockopt(serverSocket, SOL_SOCKET, SO_REUSEADDR, (const char *) &on, sizeof(on)); if (-1 == status) { perror("setsockopt(...,SO_REUSEADDR,...)"); } /* * when connection is closed, there is a need * to linger to ensure all data is * transmitted, so turn this on also */ { struct linger linger = { 0 }; linger.l_onoff = 1; linger.l_linger = 30; status = setsockopt(serverSocket, SOL_SOCKET, SO_LINGER, (const char *) &linger, sizeof(linger)); if (-1 == status) { perror("setsockopt(...,SO_LINGER,...)"); } } /* * find out who I am */ status = _GetHostName(hostname, sizeof(hostname)); if (-1 == status) { perror("_GetHostName()"); exit(1); } hostPtr = gethostbyname(hostname); if (NULL == hostPtr) { perror("gethostbyname()"); exit(1); } (void) memset(&serverName, 0, sizeof(serverName)); (void) memcpy(&serverName.sin_addr, hostPtr->h_addr, hostPtr->h_length); /* * to allow server be contactable on any of * its IP addresses, uncomment the following * line of code: * serverName.sin_addr.s_addr=htonl(INADDR_ANY); */ serverName.sin_family = AF_INET; /* network-order */ serverName.sin_port = htons(port); status = bind(serverSocket, (struct sockaddr *) &serverName, sizeof(serverName)); if (-1 == status) { perror("bind()"); exit(1); } status = listen(serverSocket, BACK_LOG); if (-1 == status) { perror("listen()"); exit(1); } for (;;) { struct sockaddr_in clientName = { 0 }; int slaveSocket, clientLength = sizeof(clientName); (void) memset(&clientName, 0, sizeof(clientName)); slaveSocket = accept(serverSocket, (struct sockaddr *) &clientName, &clientLength); if (-1 == slaveSocket) { perror("accept()"); exit(1); } childPid = fork(); switch (childPid) { case -1: /* ERROR */ perror("fork()"); exit(1); case 0: /* child process */ close(serverSocket); if (-1 == getpeername(slaveSocket, (struct sockaddr *) &clientName, &clientLength)) { perror("getpeername()"); } else { printf("Connection request from %s\n", inet_ntoa(clientName.sin_addr)); } /* * Server application specific code * goes here, e.g. perform some * action, respond to client etc. */ write(slaveSocket, MESSAGE, strlen(MESSAGE)); close(slaveSocket); exit(0); default: /* parent process */ close(slaveSocket); } } return 0; } /* * Local replacement of gethostname() to aid * portability */ int _GetHostName(char *buffer, int length) { struct utsname sysname = { 0 }; int status = 0; status = uname(&sysname); if (-1 != status) { strncpy(buffer, sysname.nodename, length); } return (status); }
如果把
on = 1;
status = setsockopt(serverSocket, SOL_SOCKET,
SO_REUSEADDR,
(const char *) &on, sizeof(on));
if (-1 == status)
{
perror("setsockopt(...,SO_REUSEADDR,...)");
}
注释掉, 就会发现编译之后,多次运行
$ ./a.out 5555
会得到错误
bind(): Address already in use
这就是
SO_REUSEADDR的作用了