欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

socket 超时设置

程序员文章站 2022-04-24 14:19:50
...

int nNetTimeout=1000;//1秒
//发送时限
setsockopt(socket,SOL_S0CKET, SO_SNDTIMEO, (char *)&nNetTimeout,sizeof(int));
//接收时限
setsockopt(socket,SOL_S0CKET, SO_RCVTIMEO, (char *)&nNetTimeout,sizeof(int));

这样做在Linux环境下是不会产生效果的,须如下定义:struct timeval timeout = {3,0}; 
//设置发送超时
setsockopt(socket,SOL_SOCKET,SO_SNDTIMEO,(char *)&timeout,sizeof(struct timeval));
//设置接收超时
setsockopt(socket,SOL_SOCKET,SO_RCVTIMEO,(char *)&timeout,sizeof(struct timeval));
有两点注意就是:
1)recv ()的第四个参数需为MSG_WAITALL,在阻塞模式下不等到指定数目的数据不会返回,除非超时时间到。还要注意的是只要设置了接收超时,在没有MSG_WAITALL时也是有效的。说到底超时就是不让你的程序老在那儿等,到一定时间进行一次返回而已。
2)即使等待超时时间值未到,但对方已经关闭了socket, 则此时recv()会立即返回,并收到多少数据返回多少数据。
补充:
同样可以设置连接(connect)超时:即通过SO_SNDTIMO套节字参数让超时操作跳过select。
原因是:Linux内核源码中connect的超时参数和SO_SNDTIMO操作的参数一致。
因此,在linux平台下,可以通过connect之前设置SO_SNDTIMO来达到控制连接超时的目的。

socket连接设置超时的几种方法

1.alarm方法

#include<stdio.h>
#include<unistd.h>
#include<string.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<netdb.h>
#include<errno.h>
#include<signal.h>

#define PORT 1234
#define MAXDATA   100
#define CONNECT_TIMEOUT 6
static int sTimeout=0;
static void alarmhandler(int sig);
void process(FILE *fp,int sockfd);
char *getmessage(char * sendline,int len,FILE *fp);

int main(int argc,char *argv[])
{
	int fd;
	struct hostent *he;
	struct sockaddr_in server;
	if(argc!=2){
	printf("usage:%s <ip address>/n",argv[0]);
	exit(1);
	}

	if((he=gethostbyname(argv[1]))==NULL){
		printf("gethostbyname() error/n");
		exit(1);
	}

	if((fd=socket(AF_INET,SOCK_STREAM,0))==-1){
		printf("socket() error/n");
		exit(1);
	}
	signal(SIGALRM,alarmhandler);
	sTimeout=0;
	alarm(CONNECT_TIMEOUT);
	bzero(&server,sizeof(server));
	server.sin_family=AF_INET;
	server.sin_port=htons(PORT);
	server.sin_addr=*((struct in_addr *)he->h_addr);

	if(connect(fd,(struct sockaddr *)&server,sizeof(struct sockaddr))==-1){
		if(sTimeout)
			perror("timeout connecting stream socket");
		else
			perror("connecting failed");
			close(fd);
		//printf("connect() error/n");
		exit(1);
	}
	sTimeout=0;
	alarm(0);

	process(stdin,fd);
	close(fd);
}

该方法在多线程下有问题。

2.select方法

原理上是这样的:
1.建立socket
2.将该socket设置为非阻塞模式
3.调用connect()
4.使用select()检查该socket描述符是否可写(注意,是可写)
5.根据select()返回的结果判断connect()结果
6.将socket设置为阻塞模式(如果你的程序不需要用阻塞模式的,这步就省了,不过一般情况下都是用阻塞模式的,这样也容易管理)
如果你对网络编程很熟悉的话,其实我一说出这个过程你就知道怎么写你的程序了,下面给出我写的一段程序,仅供参考。
 

/******************************
* Time out for connect()
* Write by Kerl W
******************************/
#include <sys/socket.h>
#include <sys/types.h>
#define TIME_OUT_TIME 20 //connect超时时间20秒
int main(int argc , char **argv)
{
  ………………
  int sockfd = socket(AF_INET, SOCK_STREAM, 0);
  if(sockfd < 0) exit(1);
  struct sockaddr_in serv_addr;
  ………//以服务器地址填充结构serv_addr
  int error=-1, len;
  len = sizeof(int);
  timeval tm;
  fd_set set;
  unsigned long ul = 1;
  ioctl(sockfd, FIONBIO, &ul); //设置为非阻塞模式
  bool ret = false;
  if( connect(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) == -1)
  {
	  tm.tv_set = TIME_OUT_TIME;
	  tm.tv_uset = 0;
	  FD_ZERO(&set);
	  FD_SET(sockfd, &set);
	  if( select(sockfd+1, NULL, &set, NULL, &tm) > 0)
	  {
		getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &error, (socklen_t *)&len);
		if(error == 0) ret = true;
		else ret = false;
	  } else ret = false;
	}
	else ret = true;
	ul = 0;
	ioctl(sockfd, FIONBIO, &ul); //设置为阻塞模式
	if(!ret)
	{
	close( sockfd );
	fprintf(stderr , "Cannot Connect the server!n");
	  return;
	  }
	fprintf( stderr , "Connected!n");
	//下面还可以进行发包收包操作
	……………
}

3.SO_SNDTIMEO方法

SO_RCVTIMEO和SO_SNDTIMEO套接口选项可以给套接口的读和写,来设置超时时间,在unix网络编程中,说是他们只能用于读和写,而像 accept和connect都不能用他们来设置.可是我在阅读内核源码的过程中看到,在linux中,accept和connect可以分别用 SO_RCVTIMEO和SO_SNDTIMEO套接口来设置超时,这里他们的超时时间也就是sock的sk_rcvtimeo和sk_sndtimeo 域.accept和connect的相关代码我前面都介绍过了,这里再提一下.其中accept的相关部分在inet_csk_accept中,会调用 sock_rcvtimeo来取得超时时间(如果是非阻塞则忽略超时间).而connect的相关代码在inet_stream_connect中通过调用sock_sndtimeo来取得超时时间(如果非阻塞则忽略超时时间).

测试代码
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <errno.h>

int main(int argc, char *argv[])
{
        int fd;
        struct sockaddr_in addr;
        struct timeval timeo = {3, 0};
        socklen_t len = sizeof(timeo);

         fd = socket(AF_INET, SOCK_STREAM, 0);
        if (argc == 4)
                 timeo.tv_sec = atoi(argv[3]);
        setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &timeo, len);
         addr.sin_family = AF_INET;
         addr.sin_addr.s_addr = inet_addr(argv[1]);
         addr.sin_port = htons(atoi(argv[2]));
        if (connect(fd, (struct sockaddr*)&addr, sizeof(addr)) == -1) {
                if (errno == EINPROGRESS) {
                        fprintf(stderr, "timeout/n");

                        return -1;
                }
                perror("connect");
                return 0;
        }
        printf("connected/n");

        return 0;
}