函数说明 fcntl、ioctl、getsockopt / setsockopt、epoll、

windows OS

getsockopt()

setsockopt()

closesocket()

KeepAliveTime
Parameters\KeepAliveInterval


linux

epoll


#define MAX_EVENTS 10
struct epoll_event ev, events[MAX_EVENTS];
int listen_sock, conn_sock, nfds, epollfd;

/* Set up listening socket, 'listen_sock' (socket(),
   bind(), listen()) */

epollfd = epoll_create(10);
if (epollfd == -1) {
    perror("epoll_create");
    exit(EXIT_FAILURE);
}

ev.events = EPOLLIN;
ev.data.fd = listen_sock;
if (epoll_ctl(epollfd, EPOLL_CTL_ADD, listen_sock, &ev) == -1) {
    perror("epoll_ctl: listen_sock");
    exit(EXIT_FAILURE);
}

for (;;) {
    nfds = epoll_wait(epollfd, events, MAX_EVENTS, -1);
    if (nfds == -1) {
        perror("epoll_pwait");
        exit(EXIT_FAILURE);
    }

   for (n = 0; n < nfds; ++n) {
        if (events[n].data.fd == listen_sock) {
            conn_sock = accept(listen_sock,
                            (struct sockaddr *) &local, &addrlen);
            if (conn_sock == -1) {
                perror("accept");
                exit(EXIT_FAILURE);
            }
            setnonblocking(conn_sock);
            ev.events = EPOLLIN | EPOLLET;
            ev.data.fd = conn_sock;
            if (epoll_ctl(epollfd, EPOLL_CTL_ADD, conn_sock,
                        &ev) == -1) {
                perror("epoll_ctl: conn_sock");
                exit(EXIT_FAILURE);
            }
        } else {
            do_use_fd(events[n].data.fd);
        }
    }
}


[fcntl ioctl 路由套接字等 相同点,不同点](http://www.cnblogs.com/scotth/p/4741008.html#第7章 套接字选项(重点))

ioctl

参考 《读书笔记系列》unix network programming(3rd)Vol.1 >>16~22章

fcntl

功能描述:根据文件描述词来操作文件的特性。
http://www.cnblogs.com/andtt/articles/2178875.html

Linux下getsockopt/setsockopt 函数说明

getsockopt/setsockopt

使用较新的SO_RCVTIMEO和SO_SNTIMEO 套接字选项。这个方法的问题在于并非所有的实现 都支持这两个套接字选项。(只支持套接字描述符)
Setsockopt(sockfd, SOL_SOCKET, SO_RECVTIMEO, &tv, sizeof(tv));

功能描述:
获取或者设置与某个套接字关联的选 项。选项可能存在于多层协议中,它们总会出现在最上面的套接字层。当操作套接字选项时,选项位于的层和选项的名称必须给出。为了操作套接字层的选项,应该 将层的值指定为SOL_SOCKET。为了操作其它层的选项,控制选项的合适协议号必须给出。例如,为了表示一个选项由TCP协议解析,层应该设定为协议 号TCP。

用法:

#include <sys/types.h>
#include <sys/socket.h>

int getsockopt(int sock, int level, int optname, void *optval, socklen_t *optlen);

int setsockopt(int sock, int level, int optname, const void *optval, socklen_t optlen);

参数:  
sock:将要被设置或者获取选项的套接字。
level:选项所在的协议层。
optname:需要访问的选项名。
optval:对于getsockopt(),指向返回选项值的缓冲。对于setsockopt(),指向包含新选项值的缓冲。
optlen:对于getsockopt(),作为入口参数时,选项值的最大长度。作为出口参数时,选项值的实际长度。对于setsockopt(),现选项的长度。

   
返回说明:  
成功执行时,返回0。失败返回-1,errno被设为以下的某个值  
EBADF:sock不是有效的文件描述词
EFAULT:optval指向的内存并非有效的进程空间
EINVAL:在调用setsockopt()时,optlen无效
ENOPROTOOPT:指定的协议层不能识别选项
ENOTSOCK:sock描述的不是套接字

KEEPALIVE 其实是发送心跳包,判断对方是否在线。

首先知道setsockopt()函数的KEEPALIVE属性是周期地测试连接是否仍存活,
我上网查了很多资料还是不知道如何使用,
最后硬着头皮自己写了一个服务器端和一个客户端的套接字连接
分别设置了两端的KEEPALIVE属性为打开

服务器端:


#include<stdio.h>
#include<stdlib.h>
#include<sys/socket.h>
#include<error.h>
#include<string.h>
#include<sys/types.h>
#include<netinet/in.h>
#include<sys/wait.h>
#include<arpa/inet.h>
#include<unistd.h>

#define SERVPORT 6000 //设定服务器服务端口为6000
#define MAX_LISTEN_SOCK_NUM 20 //设定可监听套接字的最大个数为20
int main()
{
   //sockfd为本地监听套接字标识符,client_fd为客户端套接字标识符
   int sockfd,client_fd;
   struct sockaddr_in my_addr;
   struct sockaddr_in client_addr;

   //创建本地监听套接字
   if((sockfd=socket(AF_INET,SOCK_STREAM,0))==-1){
      perror("套接字创建失败!/n");
   exit(1);
   }

   //设置套接字的属性使它能够在计算机重启的时候可以再次使用套接字的端口和IP
   int err,sock_reuse=1;
   err=setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,(char *)&sock_reuse,sizeof(sock_reuse));
   if(err!=0){
      printf("套接字可重用设置失败!/n");
      exit(1);
   }
   my_addr.sin_family=AF_INET;
   my_addr.sin_port=htons(SERVPORT);
   my_addr.sin_addr.s_addr=INADDR_ANY;
   bzero(&(my_addr.sin_zero),8);
   //绑定套接字
   if(bind(sockfd,(struct sockaddr *)&my_addr,sizeof(struct sockaddr))==-1){
      perror("绑定失败!/n");
      exit(1);
   }
   //设置监听
   if((listen(sockfd,MAX_LISTEN_SOCK_NUM))==-1){
      perror("设置监听失败!/n");
      exit(1);
   }
   printf("套接字进入监听状态,等待请求连接:/n");

   //int time_to_quit=1;
   //while(time_to_quit){ //可以通过设置time_to_quit来主动关闭服务器端
   while(1){
      //有连接请求时进行连接
		socklen_t sin_size=sizeof(struct sockaddr_in);
		if((client_fd=accept(sockfd,(struct sockaddr *)&client_addr,&sin_size))==-1){
			perror("接受连接失败!/n");
			continue;
		}

		int opt;
		socklen_t len=sizeof(int);
		if((getsockopt(sockfd,SOL_SOCKET,SO_KEEPALIVE,(char*)&opt,&len))==0){
			printf("SO_KEEPALIVE Value: %d/n", opt);
		}
		printf("接到一个来自%s的连接/n",inet_ntoa(client_addr.sin_addr));
		//创建子进程来处理已连接的客户端套接字

		if(send(client_fd,"您好,您已经连接成功!/n",50,0)==-1){
			perror("发送通知信息失败!/n");
			exit(0);
		}

	}
   close(client_fd);
   return 0;
}

客户端:

#include<stdio.h>
#include<stdlib.h>
#include<error.h>
#include<string.h>
#include<netdb.h>
#include<sys/types.h>
#include<netinet/in.h>
#include<sys/socket.h>
#include<unistd.h>
#include<arpa/inet.h>
#define MAXDATASIZE 100 /*每次最大数据传输量*/
int main()
{
	int sockfd,nbytes,serv_port;
	char buf_serv_ip[16],buf[26];
	struct sockaddr_in serv_addr;
	if((sockfd=socket(AF_INET,SOCK_STREAM,0))==-1){
		perror("创建套接字失败!/n");
		exit(1);
	}
	//创建套接字成功后设置其可重用的属性
	int KeepAlive=1;
	socklen_t KPlen=sizeof(int);
	if(setsockopt(sockfd,SOL_SOCKET,SO_KEEPALIVE,(char *)&KeepAlive,KPlen)!=0){
		perror("设置周期测试连接是否仍存活失败!/n");
		exit(1);
	}
	printf("请输入要连接主机的IP地址:/n");
	scanf("%s",buf_serv_ip);
	printf("请输入要连接主机的端口号:/n");
	scanf("%d",&serv_port);
	serv_addr.sin_family=AF_INET;
	serv_addr.sin_addr.s_addr=inet_addr(buf_serv_ip);
	serv_addr.sin_port=htons(serv_port);
	bzero(&(serv_addr.sin_zero),8);
	if(connect(sockfd,(struct sockaddr *)&serv_addr,sizeof(struct sockaddr))==-1){
		perror("连接服务器失败!/n");
		exit(1);
	}
	printf("连接服务器成功!/n");
	//在此处可以先接受判断将要接受数据的长度再创建数组
	if((nbytes=recv(sockfd,buf,26,0))==-1){
		perror("接受数据失败!/n");
		exit(1);
	}
	buf[nbytes]='/0';
	printf("接受的数据为:%s/n",buf);
	close(sockfd);
	return 0;
}

分别在两个终端运(在同一个主机上)行:先启动服务器端;再启动客户端,向服务器端请求连接,连接成功后,客户端套接字关闭,服务器端套接字始终打开,等待两个小时多也没有反应,

肯请高手指点:
(1)这样做能达到预期报告连接是否仍存在的效果吗,
(2)不能的话应该如何做,
(3)还有getsockopt()的相关功能又是什么哪?在上面的程序中没有用到该函数
(4)看了unix网络编程上的一点关于该问题的解释还是不明白,因为她提到要两个小时才回应,但等了撒小时也没有反应,她还说可以缩短时间,我却不知道如何缩短
getsockopt和setsockopt 

int getsockopt(int sockfd,int level,int optname,void *optval,socklen_t *optlen)
int setsockopt(int sockfd,int level,int optname,const void *optval,socklen_t *optlen)

level指定控制套接字的层次.可以取三种值:
1)SOL_SOCKET:通用套接字选项.
2)IPPROTO_IP:IP选项.
3)IPPROTO_TCP:TCP选项. 
optname指定控制的方式(选项的名称),我们下面详细解释 

optval获得或者是设置套接字选项.根据选项名称的数据类型进行转换 

选项名称        说明                  数据类型

SOL_SOCKET

SO_BROADCAST      允许发送广播数据            int
SO_DEBUG        允许调试                int
SO_DONTROUTE      不查找路由               int
SO_ERROR        获得套接字错误             int
SO_KEEPALIVE      保持连接                int
SO_LINGER        延迟关闭连接              struct linger
SO_OOBINLINE      带外数据放入正常数据流         int
SO_RCVBUF        接收缓冲区大小             int
SO_SNDBUF        发送缓冲区大小             int
SO_RCVLOWAT       接收缓冲区下限             int
SO_SNDLOWAT       发送缓冲区下限             int
SO_RCVTIMEO       接收超时                struct timeval
SO_SNDTIMEO       发送超时                struct timeval
SO_REUSERADDR      允许重用本地地址和端口         int
SO_TYPE         获得套接字类型             int
SO_BSDCOMPAT      与BSD系统兼容              int

IPPROTO_IP

IP_HDRINCL       在数据包中包含IP首部          int
IP_OPTINOS       IP首部选项               int
IP_TOS         服务类型
IP_TTL         生存时间                int

IPPRO_TCP

TCP_MAXSEG       TCP最大数据段的大小           int
TCP_NODELAY       不使用Nagle算法             int

SO_RCVBUFSO_SNDBUF每个套接口都有一个发送缓冲区和一个接收缓冲区,使用这两个套接口选项可以改变缺省缓冲区大小。

// 接收缓冲区
int nRecvBuf=32*1024;         //设置为32K
setsockopt(s,SOL_SOCKET,SO_RCVBUF,(const char*)&nRecvBuf,sizeof(int));

//发送缓冲区
int nSendBuf=32*1024;//设置为32K
setsockopt(s,SOL_SOCKET,SO_SNDBUF,(const char*)&nSendBuf,sizeof(int));

注意:

当设置TCP套接口接收缓冲区的大小时,函数调用顺序是很重要的,因为TCP的窗口规模选项是在建立连接时用SYN与对方互换得到的。对于客户,SO_RCVBUF选项必须在connect之前设置;对于服务器,SO_RCVBUF选项必须在listen前设置。


SO_REUSEADDR和SO_REUSEPORT异同

Windows
windows上只有SO_REUSEADDR选项,没有SO_REUSEPORT。在windows上设置了SO_REUSEADDR的socket其行为与BSD上设定了SO_REUSEPORT和SO_REUSEADDRd的行为大致一样,只有一个差别:一个设置了SO_REUSEADDR的socket总是可以绑定到已经被绑定过的源地址和源端口,不管之前在这个地址和端口上绑定的socket是否设置了SO_REUSEADDR没有。这种行为在某种程度上有些危险因为它允许一个应用程序从别的应用程序上"偷取"已连接的端口。不用说,这对安全性有极大的影响,Microsoft意识到了这个问题,就加入了另一个socket选项: SO_EXECLUSIVEADDRUSE。设置了SO_EXECLUSIVEADDRUSE的socket确保一旦绑定成功,那么被绑定的源端口和地址就只属于这一个socket,其它的socket不能绑定,甚至他们使用了SO_REUSEADDR也没用。

posted @ 2015-11-06 00:27  scott_h  阅读(1185)  评论(0编辑  收藏  举报