很多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,,sizeof(server_address));
server_address.sin_family = AF_INET;
server_address.sin_addr.s_addr = INADDR_ANY;
server_address.sin_port = htons();
// 建立服务器端socket
if((server_sockfd = socket(AF_INET, SOCK_STREAM, ))<)
{
perror("server_sockfd creation failed");
exit(EXIT_FAILURE);
}
// 将套接字绑定到服务器的网络地址上
if((bind(server_sockfd,(struct sockaddr *)&server_address,sizeof(struct sockaddr)))<)
{
perror("server socket bind failed");
exit(EXIT_FAILURE);
}
// 建立监听队列
listen(server_sockfd,);
// 等待客户端连接请求到达
client_sockfd=accept(server_sockfd,(struct sockaddr *)&client_address,(socklen_t*)&sin_size);
if(client_sockfd<)
{
perror("accept client socket failed");
exit(EXIT_FAILURE);
}
// 接收客户端数据
if(recv(client_sockfd,buf,BUFFER_SIZE,)<)
{
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,)<)
{
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, );// 建立客户端socket
address.sin_family = AF_INET;
address.sin_addr.s_addr = inet_addr("127.0.0.1");
address.sin_port = htons();
len = sizeof(address);
// 与远程服务器建立连接
result = connect(client_sockfd, (struct sockaddr *)&address, len);
if(result<)
{
perror("connect failed");
exit(EXIT_FAILURE);
}
printf("Please input the message:");
scanf("%s",buf);
send(client_sockfd,buf,BUFFER_SIZE,);
recv(client_sockfd,buf,BUFFER_SIZE,);
printf("receive data from server: %s/n",buf);
close(client_sockfd);
return ;
}

在成功的运行了第一次之后,当你再次启动服务器端程序时,./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,,sizeof(server_address));
server_address.sin_family = AF_INET;
server_address.sin_addr.s_addr = INADDR_ANY;
server_address.sin_port = htons();
// 建立服务器端socket
if((server_sockfd = socket(AF_INET, SOCK_STREAM, ))<)
{
perror("server_sockfd creation failed");
exit(EXIT_FAILURE);
}
// 设置套接字选项避免地址使用错误
int on=;
if((setsockopt(server_sockfd,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on)))<)
{
perror("setsockopt failed");
exit(EXIT_FAILURE);
}
// 将套接字绑定到服务器的网络地址上
if((bind(server_sockfd,(struct sockaddr *)&server_address,sizeof(struct sockaddr)))<)
{
perror("server socket bind failed");
exit(EXIT_FAILURE);
}
// 建立监听队列
listen(server_sockfd,);
// 等待客户端连接请求到达
client_sockfd=accept(server_sockfd,(struct sockaddr *)&client_address,(socklen_t*)&sin_size);
if(client_sockfd<)
{
perror("accept client socket failed");
exit(EXIT_FAILURE);
}
// 接收客户端数据
if(recv(client_sockfd,buf,BUFFER_SIZE,)<)
{
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,)<)
{
perror("send failed");
exit(EXIT_FAILURE);
}
close(client_sockfd);
close(server_sockfd);
exit(EXIT_SUCCESS);
}

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

socket编程小问题:地址已经被使用——Address already in use的更多相关文章

  1. 关于socket编程获取客户端地址笔记

    因为最近刚好碰到这块,而且很不小心的在上面踩了个坑,所以把这个坑记录下来 首先,在我们都是在accept函数以后来获取客户端的地址: client_sd = accept(watcher->fd ...

  2. 文成小盆友python-num9 socket编程

    socket编程 网络上的两个程序通过一个双向的通信连接实现数据的交换,这个连接的一端称为一个socket. Socket的英文原义是“孔”或“插座”.作为BSD UNIX的进程通信机制,取后一种意思 ...

  3. Linux下的C Socket编程 -- 获取对方IP地址

    Linux下的C Socket编程(二) 获取域名对应的IP地址 经过上面的讨论,如果我们想要连接到远程的服务器,我们需要知道对方的IP地址,系统函数gethostbyname便能够实现这个目的.它能 ...

  4. socket 编程的端口和地址复用

    在linux socket网络编程中,大规模并发TCP或UDP连接时,经常会用到端口复用:   int opt = 1;   if(setsockopt(sockfd, SOL_SOCKET,SO_R ...

  5. python网络编程-socket编程

     一.服务端和客户端 BS架构 (腾讯通软件:server+client) CS架构 (web网站) C/S架构与socket的关系: 我们学习socket就是为了完成C/S架构的开发 二.OSI七层 ...

  6. Socket编程实践(1) 基本概念

    1. 什么是socket socket可以看成是用户进程与内核网络协议栈的编程接口.TCP/IP协议的底层部分已经被内核实现了,而应用层是用户需要实现的,这部分程序工作在用户空间.用户空间的程序需要通 ...

  7. [转]C语言SOCKET编程指南

    1.介绍 Socket编程让你沮丧吗?从man pages中很难得到有用的信息吗?你想跟上时代去编Internet相关的程序,但是为你在调用 connect() 前的bind() 的结构而不知所措?等 ...

  8. socket编程基础

    socket编程 什么是socket 定义 socket通常也称作套接字,用于描述IP地址和端口,是一个通信链的句柄,应用程序通常通过套接字向网络发出请求或者应答网络请求. socket起源于Unix ...

  9. Linux Socket编程

    “一切皆Socket!” 话虽些许夸张,但是事实也是,现在的网络编程几乎都是用的socket. ——有感于实际编程和开源项目研究. 我们深谙信息交流的价值,那网络中进程之间如何通信,如我们每天打开浏览 ...

随机推荐

  1. FFmpeg实现将图片转换为视频

    ##名称:ffmpeg实现将图片转换为视频 ##平台:ubuntu(已经安装好了ffmpeg工具) ##日期:2017年12月10日 简介: 因为学习需要,需要将连续图片转换成视频,昨天和今天早上用o ...

  2. MySql与SqlServer的区别

    1.SQL Server 是Microsoft 公司推出的关系型数据库管理系统.具有使用方便可伸缩性好与相关软件集成程度高等优点,可跨越从运行Microsoft Windows 98 的膝上型电脑到运 ...

  3. mybatis 的动态SQL

    在XML 中支持的几种标签: • if • choose.when.otherwise • where • set • trim • foreach OGNL 表达式 1. el or e22. el ...

  4. Python学习---IO的异步[twisted模块]

    安装twisted模块 Linux: pip3 install twisted Window: a. http://www.lfd.uci.edu/~gohlke/pythonlibs/#twiste ...

  5. Office 365实现单点登录系列(5)—配置单点登录

    这是单点登录系列的最后一篇文章,前面4篇文章其实都是在为这篇文章的内容做准备,我把这四篇文章的链接放在下面,如果大家有需要,可以参考我以下的链接: Office 365实现单点登录系列(1)—域环境搭 ...

  6. K8S Deployment 命令

    创建 Deployment kubectl create -f https://kubernetes.io/docs/user-guide/nginx-deployment.yaml --record ...

  7. zhuangxiu

    红宝石家居广场国际家居馆 党湾综合市场(青年文明路)

  8. 【教程】【FLEX】#006 控件位置的拖动

    上一篇笔记学习了怎么从 把一个控件拖放到另外一个控件里面(即 A --> B里面). 而现在呢,则是学习  怎么把 A 拖到另外一个位置. (A -->A位置改变): 先说一下实现的思路( ...

  9. 【Alpha】Daily Scrum Meeting——blog1

    团队成员 吴吉键 201421122007(组长) 魏修祺 201421122008 孙劲林 201421122022 1. 会议当天照片 忘记拍了,能补上不? 2. 每个人的工作 3. 燃尽图

  10. 使用ubuntu desktop是可能会用到的配置

    1.ubuntu desktop12.04 接双显示器 想要12.04版本在接上双显示器时能很好的工作,则需要进行如下设置: (1).编辑/etc/X11/xorg.conf文件: /etc/X11/ ...