setsockopt与getsockopt的使用

setsockopt()和getsockopt()函数----解决地址不可复用,即Address already in use  

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,终止程序,重启时,端口可以复用(重复使用)。不会出现问题。

你可能感兴趣的:(程序员之路)