2011-03-15 11:54:56| 分类: Linux网络编程|举报|字号 订阅
Linux所提供的socket库有一个错误(bug);此错误表现为你不能为一个套接字重新启动同一个端口号。
即:比如一个程序,在IP"192.168.1.234"和Port"12357"上创建了一个套接字。启动程序后,在recvfrom数据,我们用ctrl+z强制终止程序。
当我们重启程序时,被提示::
Address already in use //绑定时发生的错误。
产生问题的原因是::
Linux内核在一个绑定套接字的进程结束后,从不把端口标记为未用。
解决问题的方法::
当套接字sockfd已经打开,即socket()创建套接字之后,使用setsockopt系统调用在这个sockfd上设定选项(options)。
Linux内核提供的系统调用::
#include
#include
int getsockopt(int sockfd,int level,int name,char *value,int optlen);
int setsockopt(int scokfd,int level,int name,char *value,int *optlen);
参数详解::
sockfd 必须是一个已经打开的sockfd,setsockopt应该在socket()之后就调用。
level 是函数使用的协议标准(protocol level),Linux使用的是套接字,套接字
的标准表示是SOL_SOCKET,其次TCP/IP协议使用的是IPPROTO_TCP,
我们并不关心这个。
name 在套接字中的说明,比如:SO_REUSEADDR,就是表示端口可以复用。
SO_BROADCAST,就表示将sockfd设置为可以进行广播的fd。
man手册中,name的取值比较多,我们比较关系的就是上面的两个。
value 我们用setsockopt设置数据的地址,getsockopt将在这个地址上得到值。
optlen 设置value时,value的大小。
举例:: 要实现端口复用(重复使用,即解决Address already use)。
/*创建套接字*/
int sockfd = socket(AF_INET,SOCK_DGRAM或这SOCK_STREAM,0);
/*设定参数的值,即上面value和optlen的值*/
int opt = 1;
int len = sizeof(opt);
/*设置套接字属性,实现复用*/
setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,(char *)&opt,len);
举例::
这里只举例setsockopt用来设置端口可以复用(重复使用的情况)的情况,
getsockopt的并没有举例。getsockopt,主要用来获取sockfd的一些opt信息。
getsockopt的例子,以后有机会再补充。
代码举例 src.c :: 按ctrl+z强行终止后,会出现端口不可用的程序(Address already use)
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define MAX_LINE 4096
int main(int argc,char *argv[])
{
struct sockaddr_in srvaddr;
struct sockaddr_in cltaddr;
int socklen=sizeof(struct sockaddr_in);
int sockfd;
char msgbuf[MAX_LINE];
int msglen;
srvaddr.sin_family = AF_INET;
srvaddr.sin_addr.s_addr = inet_addr("172.25.81.16");
srvaddr.sin_port = htons(12357);
bzero(srvaddr.sin_zero,sizeof(srvaddr.sin_zero));
sockfd = socket(AF_INET,SOCK_DGRAM,0);
if(sockfd == -1)
{
perror("creat socket fd fail:");
exit(1);
}
if(bind(sockfd,(struct sockaddr *)&srvaddr,sizeof(struct sockaddr_in))==-1)
{
perror("bind socket fd fail:");
close(sockfd);
exit(1);
}
memset(msgbuf,0,MAX_LINE);
msglen = recvfrom(sockfd,msgbuf,MAX_LINE,0,(struct sockaddr *)&cltaddr,&socklen);
if(msglen < 0)
{
perror("recv data fail:");
close(sockfd);
exit(1);
}
else
{
msgbuf[msglen] = '\0';
printf("recv data from clt:%s\n",msgbuf);
memset(msgbuf,0,MAX_LINE);
strcpy(msgbuf,"hello clt!");
sendto(sockfd,msgbuf,strlen(msgbuf),0,(struct sockaddr *)&cltaddr,socklen);
close(sockfd);
}
return 0;
}
上面的src.c编译成src,运行后,ctrl+z强制终止后,再启动,将提示:
bind socket fd fail:Address already use
代码举例 src.c :: 按ctrl+z强行终止后,不会出现端口不可用的程序(Address already use)
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define MAX_LINE 4096
int main(int argc,char *argv[])
{
struct sockaddr_in srvaddr;
struct sockaddr_in cltaddr;
int socklen=sizeof(struct sockaddr_in);
int sockfd;
char msgbuf[MAX_LINE];
int msglen;
int opt = 1;
int len = sizeof(opt);
srvaddr.sin_family = AF_INET;
srvaddr.sin_addr.s_addr = inet_addr("172.25.81.16");
srvaddr.sin_port = htons(12357);
bzero(srvaddr.sin_zero,sizeof(srvaddr.sin_zero));
sockfd = socket(AF_INET,SOCK_DGRAM,0);
if(sockfd == -1)
{
perror("creat socket fd fail:");
exit(1);
}
if(setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,(char *)&opt,len)==-1)
{
perror("setsockopt fail:");
close(sockfd);
exit(1);
}
if(bind(sockfd,(struct sockaddr *)&srvaddr,sizeof(struct sockaddr_in))==-1)
{
perror("bind socket fd fail:");
close(sockfd);
exit(1);
}
memset(msgbuf,0,MAX_LINE);
msglen = recvfrom(sockfd,msgbuf,MAX_LINE,0,(struct sockaddr *)&cltaddr,&socklen);
if(msglen < 0)
{
perror("recv data fail:");
close(sockfd);
exit(1);
}
else
{
msgbuf[msglen] = '\0';
printf("recv data from clt:%s\n",msgbuf);
memset(msgbuf,0,MAX_LINE);
strcpy(msgbuf,"hello clt!");
sendto(sockfd,msgbuf,strlen(msgbuf),0,(struct sockaddr *)&cltaddr,socklen);
close(sockfd);
}
return 0;
}
编译成src,运行过程中,按ctrl+z,终止程序,重启时,端口可以复用(重复使用)。不会出现问题。