本文将会阐述两种解决端口占用的方法。
本文会用到的服务器端的程序如下:
#include "unp.h"
#include <time.h>
int main(int argc, char **argv)
{
int listenfd, connfd;
socklen_t len;
struct sockaddr_in servaddr, cliaddr;
char buff[MAXLINE];
time_t ticks;
listenfd = Socket(AF_INET, SOCK_STREAM, 0);
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(13);/* daytime server */
int on = 1;
Setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
Bind(listenfd, (SA *) &servaddr, sizeof(servaddr));
Listen(listenfd, LISTENQ);
for(; ;)
{
len = sizeof(cliaddr);
connfd = Accept(listenfd, (SA *)&cliaddr, &len);
printf("connection from %s, port %d\n",
Inet_ntop(AF_INET, &cliaddr.sin_addr, buff, sizeof(buff)),
ntohs(cliaddr.sin_port));
ticks = time(NULL);
snprintf(buff, sizeof(buff), "%.24s\r\n", ctime(&ticks));
Write(connfd, buff, strlen(buff));
Close(connfd);
}
exit(0);
}
该程序试图将TCP套接字绑定到13号端口(“给请求主机发送日期和时间”)。
问题描述
如果一个端口已经被占用,而我们的程序又绑定到该端口,则我们在运行程序的时候系统会提示错误:“bind error: Address already in use”,即端口已被占用。
解决方法1:关闭使用该端口的进程
这种方法应该是最笨的了,而且在实际中我们不大可能这样子做。
我们运行了上边提到的程序,得到了下边的结果:
很明显,已经有进程占用了这个端口。我们可以先看一下是哪些服务占用了端口(在这里是13):
从上图可以看出是TCP服务占用了这个端口。我们接下来看一下是什么进程开启了这个TCP服务:
现在我们知道是xinetd服务占用了这个接口。我们直接关闭掉这个服务:
现在开启我们的服务端程序就没问题了。
解决方法2:设置端口为可重用
在IBM的一篇文章Linux 套接字编程中的 5 个隐患中作者提出用函数setsockopt来设定端口可重用,代码如下:
/* Enable address reuse */
on = 1;
ret = setsockopt( sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on) );
但在我们的程序中,用了这种方法还是不行()。
出现这个问题的原因在于开启我们自己的程序前我们已经开启了其它占用13号端口的服务(如xinetd),而该已占用13号端口的服务却没有设定13号端口为可重用,最终导致我们的程序绑定端口错误。
正确的做法是第一个使用13号(对其它端口号也一样)端口的进程要设定13号端口为可重用,这样后续使用该端口的进程方能绑定成功。
所以我们要先运行我们的程序,将13号端口设定为可重用,然后再运行其它使用该端口的进程(如xinetd)。用这种方法,我们的程序和xinetd服务终于能够同时绑定端口号13,如下图所示: