很多socket编程的初学者可能会遇到这样的问题:如果先ctrl+c结束服务器端程序的话,再次启动服务器就会出现Address already in use这个错误,或者你的程序在正常关闭服务器端socket后还是有这个问题。正如下面的这段简单的socket程序。

server.c

#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdlib.h> #define BUFFER_SIZE 40 int main()
{
char buf[BUFFER_SIZE];
int server_sockfd, client_sockfd;
int sin_size=sizeof(struct sockaddr_in);
struct sockaddr_in server_address;
struct sockaddr_in client_address;
memset(&server_address,0,sizeof(server_address));
server_address.sin_family = AF_INET;
server_address.sin_addr.s_addr = INADDR_ANY;
server_address.sin_port = htons(12000);
// 建立服务器端socket
if((server_sockfd = socket(AF_INET, SOCK_STREAM, 0))<0)
{
perror("server_sockfd creation failed");
exit(EXIT_FAILURE);
}
// 将套接字绑定到服务器的网络地址上
if((bind(server_sockfd,(struct sockaddr *)&server_address,sizeof(struct sockaddr)))<0)
{
perror("server socket bind failed");
exit(EXIT_FAILURE);
}
// 建立监听队列
listen(server_sockfd,5);
// 等待客户端连接请求到达
client_sockfd=accept(server_sockfd,(struct sockaddr *)&client_address,(socklen_t*)&sin_size);
if(client_sockfd<0)
{
perror("accept client socket failed");
exit(EXIT_FAILURE);
}
// 接收客户端数据
if(recv(client_sockfd,buf,BUFFER_SIZE,0)<0)
{
perror("recv client data failed");
exit(EXIT_FAILURE);
}
printf("receive from client:%s/n",buf);
// 发送数据到客户端
if(send(client_sockfd,"I have received your message.",BUFFER_SIZE,0)<0)
{
perror("send failed");
exit(EXIT_FAILURE);
}
close(client_sockfd);
close(server_sockfd);
exit(EXIT_SUCCESS);
}

client.c

#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdlib.h> #define BUFFER_SIZE 40 int main()
{
char buf[BUFFER_SIZE];
int client_sockfd;
int len;
struct sockaddr_in address;// 服务器端网络地址结构体
int result;
client_sockfd = socket(AF_INET, SOCK_STREAM, 0);// 建立客户端socket
address.sin_family = AF_INET;
address.sin_addr.s_addr = inet_addr("127.0.0.1");
address.sin_port = htons(12000);
len = sizeof(address);
// 与远程服务器建立连接
result = connect(client_sockfd, (struct sockaddr *)&address, len);
if(result<0)
{
perror("connect failed");
exit(EXIT_FAILURE);
}
printf("Please input the message:");
scanf("%s",buf);
send(client_sockfd,buf,BUFFER_SIZE,0);
recv(client_sockfd,buf,BUFFER_SIZE,0);
printf("receive data from server: %s/n",buf);
close(client_sockfd);
return 0;
}

在成功的运行了第一次之后,当你再次启动服务器端程序时,./server就变得邪恶起来,在bind()这个函数中居然出现了Address already in use这个错误。

然后你开始迷惑了,难道是忘记将socket给关闭了,或是关闭socket的顺序不对?经过种种猜测与试验,你发现问题毫无进展......过了一会,当你再次抱着试试看的态度重新在linux的“黑色终端”中输入./server时,程序居然运行了,什么情况?究其原因,是socket选项在捣鬼。下面是IBM官网上对这一情况的具体解释,参见http://www.ibm.com/developerworks/cn/linux/l-sockpit/

bind 普遍遭遇的问题是试图绑定一个已经在使用的端口。该陷阱是也许没有活动的套接字存在,但仍然禁止绑定端口(bind 返回 EADDRINUSE),它由 TCP 套接字状态 TIME_WAIT 引起。该状态在套接字关闭后约保留 2 到 4 分钟。在 TIME_WAIT 状态退出之后,套接字被删除,该地址才能被重新绑定而不出问题。

等待 TIME_WAIT 结束可能是令人恼火的一件事,特别是如果您正在开发一个套接字服务器,就需要停止服务器来做一些改动,然后重启。幸运的是,有方法可以避开 TIME_WAIT 状态。可以给套接字应用 SO_REUSEADDR 套接字选项,以便端口可以马上重用。

考虑清单 3 的例子。在绑定地址之前,我以 SO_REUSEADDR 选项调用 setsockopt。为了允许地址重用,我设置整型参数(on)为 1 (不然,可以设为 0 来禁止地址重用)。

按照IBM的做法,我重新改写了server.c的代码。

server.c

#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdlib.h> #define BUFFER_SIZE 40 int main()
{
char buf[BUFFER_SIZE];
int server_sockfd, client_sockfd;
int sin_size=sizeof(struct sockaddr_in);
struct sockaddr_in server_address;
struct sockaddr_in client_address;
memset(&server_address,0,sizeof(server_address));
server_address.sin_family = AF_INET;
server_address.sin_addr.s_addr = INADDR_ANY;
server_address.sin_port = htons(12000);
// 建立服务器端socket
if((server_sockfd = socket(AF_INET, SOCK_STREAM, 0))<0)
{
perror("server_sockfd creation failed");
exit(EXIT_FAILURE);
}
// 设置套接字选项避免地址使用错误
int on=1;
if((setsockopt(server_sockfd,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on)))<0)
{
perror("setsockopt failed");
exit(EXIT_FAILURE);
}
// 将套接字绑定到服务器的网络地址上
if((bind(server_sockfd,(struct sockaddr *)&server_address,sizeof(struct sockaddr)))<0)
{
perror("server socket bind failed");
exit(EXIT_FAILURE);
}
// 建立监听队列
listen(server_sockfd,5);
// 等待客户端连接请求到达
client_sockfd=accept(server_sockfd,(struct sockaddr *)&client_address,(socklen_t*)&sin_size);
if(client_sockfd<0)
{
perror("accept client socket failed");
exit(EXIT_FAILURE);
}
// 接收客户端数据
if(recv(client_sockfd,buf,BUFFER_SIZE,0)<0)
{
perror("recv client data failed");
exit(EXIT_FAILURE);
}
printf("receive from client:%s/n",buf);
// 发送数据到客户端
if(send(client_sockfd,"I have received your message.",BUFFER_SIZE,0)<0)
{
perror("send failed");
exit(EXIT_FAILURE);
}
close(client_sockfd);
close(server_sockfd);
exit(EXIT_SUCCESS);
}

这次,让我们再次反复的启动服务器,尽情的在“黑窗户”里面输入./server ./server ./server ......服务器的程序好像突然间变乖了,呵呵,童鞋们,为自己的成就庆祝吧!!!

地址已经被使用 - Address already in use的更多相关文章

  1. socket编程小问题:地址已经被使用——Address already in use

    很多socket编程的初学者可能会遇到这样的问题:如果先ctrl+c结束服务器端程序的话,再次启动服务器就会出现Address already in use这个错误,或者你的程序在正常关闭服务器端so ...

  2. WCF :IIS寄宿方式的Web地址、BaseAddress和EndPoint Address的关系

    对于在IIS中通过W3SVC或WAS寄宿的WCF Service,其在浏览器中显示的地址(Web地址),与其配置文件中的BaseAddress和EndPoint Address有什么关系呢?让我们来分 ...

  3. 获取本机IP地址

    这里有两种方法: //获取本机IP - (NSString *)localIPAddress { NSString *localIP = nil; struct ifaddrs *addrs; ) { ...

  4. Java里面获取当前服务器的IP地址

    public static void main(String[] args) { try { InetAddress address = InetAddress.getLocalHost();//获取 ...

  5. 实验1_IPv6地址配置

    IPv6地址配置 实验任务 (1)掌握如何在路由器及PC上配置IPv6地址 (2)掌握如何用IPv6 ping命令进行IPv6地址可达性检查 (3)掌握如何用命令来查看IPv6地址配置 实验过程 在R ...

  6. 语义化HTML:p、h1-6、q、blockquote、hr、address、code、pre、var、cite、dfn和samp

    一.元素语义 p标签 W3C草案: The p element represents a paragraph.W3C specification 语义化的 <p>元素 表示:文章中的段落. ...

  7. [PE结构分析] 8.输入表结构和输入地址表(IAT)

    在 PE文件头的 IMAGE_OPTIONAL_HEADER 结构中的 DataDirectory(数据目录表) 的第二个成员就是指向输入表的.每个被链接进来的 DLL文件都分别对应一个 IMAGE_ ...

  8. php读取qqwry.dat ip地址定位文件的类

    <?php// +----------------------------------------------------------------------// |// +---------- ...

  9. 纯真IP根据IP地址获得地址

    <?php /** * 纯真IP根据IP地址获得地址 */ class ipLocation { public $fp; public $firstip; //第一条ip索引的偏移地址 publ ...

随机推荐

  1. js实现手机页面定位

    <script type="text/javascript"> function Location() {}; Location.prototype.getLocati ...

  2. hadoop fs:du & count统计hdfs文件(目录下文件)大小的用法

    hadoop fs 更多用法,请参考官网:http://hadoop.apache.org/docs/r1.0.4/cn/hdfs_shell.html 以下是我的使用hadoop fs -du统计文 ...

  3. 从Date类型字段获得当日周几的DAYNAME函数

    例: select dayname(date) from pos.daywork

  4. 关于LayoutInflater的错误用法

    转自:http://www.doubleencore.com/2013/05/layout-inflation-as-intended/ Layout inflation is the term us ...

  5. ArcEngine中的缩放地图

    在ArcEngine地图操作中,缩放地图的功能经常用到,这里做一个小结. 缩放地图一般可分为以下几种情况: 1.缩放地图:与放大地图相对,一般是手动绘制区域或固定比例缩放,可调用命令或Expand函数 ...

  6. VMware Workstation 12下载安装与激活图文教程

    一.简介: VMware Workstation 12专门为Win10的安装和使用做了优化,支持DX10.4K高分辨率显示屏.OpenGL 3.3.7.1声道,以及各种新硬件和新技术.从vm11版本开 ...

  7. 提高ASP.NET首页性能的十大方法

    本文是我对ASP.NET页面载入速度提高的一些做法,这些做法分为以下部分: http://www.cnblogs.com/xiachufeng/archive/2011/11/09/2242130.h ...

  8. alias的使用

    alias,即别名.可以通过较短的别名,来实现对应的真实的命令.将alias定义在.bash_profile文件中,即可在任意目录下执行自己定义的这些命令了. 1. 命令 alias myDir='c ...

  9. vue 渲染流程

    1.DOM 节点树 高效的更新所有这些节点会是比较困难的,因为原生的DOM节点属性很多,渲染性能差. 2.虚拟 DOM “虚拟 DOM”是我们对由 Vue 组件树建立起来的整个 VNode 树的称呼. ...

  10. 解决Eclipse的Servers视图中无法添加Tomcat6/Tomcat7

    如何解决 . 关闭Eclipse . 打开WorkSpace所在的位置. {workspace-directory}/.metadata/.plugins/org.eclipse.core.runti ...