[内核版本]

linux-2.6.31


[尚存缺憾]

1、getsockopt和setsockopt的某些特定参数的调用(net/unix/af_unix.c中定义的*sockop函数均保留接口,返回EOPNOTSUPP错误);

2、ss命令查看到的本地socket状态需要进一步确认;


[注意事项]

1、使用本地socket进行通信时,其通信过程并不通过报文交互进行状态机切换:

    a)server端在执行listen函数之后,该socket即处于监听状态;

    b)client端在执行connect函数时,正常情况下,内核将sock状态设置为SS_CONNECTED,将sk状态设置为TCP_ESTABLISHED,然后通知server端有client请求(向server的socket发送SIGIO信号);

    c)一旦出现某一情况导致server端的最大链接请求(应该是由sk_max_ack_backlog成员定义,在listen函数中指定)使用完毕,如果不设置连接超时时间,client端应该在2147483647秒的时间超时

// include/linux/kernel.h

#define LONG_MAX((long)(~0UL>>1))

// include/linux/sched.h

#defineMAX_SCHEDULE_TIMEOUTLONG_MAX

// net/core/sock.c

void sock_init_data(struct socket *sock, struct sock *sk)

{

        ...

sk->sk_sndtimeo=MAX_SCHEDULE_TIMEOUT;

        ...

}

2、本地socket和网络socket的通信机制有些不同(比如,tcp状态机不通过报文交互实现,getsockopt不能取到tcp的连接状态,内核在处理本地socket时,可能不会有一些协议栈的过程,即可能忽略了报文的完整性检查),很多网络socket上使用的函数在迁移至本地socket时需要酌情考虑。


[server端代码]

int ct_ipc_sock_create(struct ct_fd * sockfd, int server_flag)
{
	log_debug("server_flag: %d", server_flag);
	int ret = CT_RET_SUCCESS;
	int result = 0;
	struct sockaddr_un srv_addr;

	memset(&srv_addr, 0, sizeof(struct sockaddr_un));

	if (SERVER_SOCKET == server_flag)
	{
		sockfd->fd = socket(PF_UNIX, SOCK_DGRAM, 0);
		if (0 > sockfd->fd)
		{
			log_err("create Unix Socket error");
			ret = CT_RET_SYS_SOCK_CREATE_ERR;
			goto ct_ipc_sock_create_err;
		}

		srv_addr.sun_family=AF_UNIX; 
		strncpy(srv_addr.sun_path, UNIX_DOMAIN, sizeof(srv_addr.sun_path)-1);    
		unlink(UNIX_DOMAIN);
		
		ret=bind(sockfd->fd,(struct sockaddr*)&srv_addr,sizeof(srv_addr));    
		if(ret==-1)
		{
			log_err("cannot bind server socket (%d)", sockfd->fd);        
			ret = CT_RET_SYS_SOCK_BIND_ERR;
			goto ct_ipc_sock_bind_listen_err;
		}
		
	}
	else if (CLIENT_SOCKET == server_flag)
	{
		sockfd->fd = socket(PF_UNIX, SOCK_STREAM, 0);
		if (0 > sockfd->fd)
		{
			log_err("create Unix Socket error");
			ret = CT_RET_SYS_SOCK_CREATE_ERR;
			goto ct_ipc_sock_create_err;
		}
		log_debug("sockfd: %d", sockfd->fd);
	
		setsockopt(sockfd->fd, SOL_SOCKET, TCP_NODELAY, &result, sizeof(int));

		srv_addr.sun_family=AF_UNIX; 
		strncpy(srv_addr.sun_path, 
		        UNIX_DOMAIN_CLOUD, 
		        sizeof(srv_addr.sun_path)-1);    
		unlink(UNIX_DOMAIN_CLOUD);
		ret=bind(sockfd->fd,(struct sockaddr*)&srv_addr,sizeof(srv_addr));
		
		if(ret==-1)
		{
			log_err("cannot bind server socket (%d)", sockfd->fd);        
			ret = CT_RET_SYS_SOCK_BIND_ERR;
			goto ct_ipc_sock_bind_listen_err;
		}
		log_debug("%s", strerror(errno));

		ret=listen(sockfd->fd, MAX_LISTEN_BACKLOG);   
		if(ret==-1)
		{       
			log_err("cannot listen the client connect request (%d)", sockfd);
			ret = CT_RET_SYS_SOCK_LISTEN_ERR; 
			goto ct_ipc_sock_bind_listen_err;
		}
	}
	else
	{
	}
	
	log_debug("%s", strerror(errno));
	
	return ret;
	
ct_ipc_sock_bind_listen_err:
	close(sockfd->fd);
ct_ipc_sock_create_err:
	sockfd->fd = -1;
	return ret;
}
int ct_select_run()
{
	int ret = CT_RET_SUCCESS;
	fd_set  rset, allset;

	int nfds = 0;
	int maxfd = 0;
	int cloudfd = 0;
	int read_num = 0;
	int write_num = 0;
	socklen_t addrLen;
	struct sockaddr_in addr;
	char rsp[] = "ACK";
	char data_buf[MAX_BUF] = {0};

	log_debug("sockfd: %d, nlfd.fd: %d, cloud_sockfd.fd: %d", 
	                ipc_sockfd.fd, nlfd.fd, cloud_sockfd.fd);

	group_filter_init();
	
	maxfd = ipc_sockfd.fd ;
	
	FD_ZERO(&allset);
	FD_SET(cloud_sockfd.fd, &allset);
	log_debug("sockfd: %d, maxfd: %d, cloud_sockfd.fd: %d", 
	                ipc_sockfd.fd, maxfd, cloud_sockfd.fd);

	while (1/*nfds == 0*/)
	{
		rset = allset;
		log_debug("sockfd: %d", ipc_sockfd.fd);
		nfds = select(maxfd + 1, &rset, NULL, NULL, NULL);
		if (0 > nfds)
		{
			log_err("select error: %s", strerror(errno));
			ret = CT_RET_SYS_SOCK_SELECT_ERR;
			return ret;
		}

		if (FD_ISSET(cloud_sockfd.fd, &rset)) {
			log_debug("cloud_sockfd: %d", cloud_sockfd.fd);
			cloudfd = accept(cloud_sockfd.fd, 
			                (struct sockaddr *)&addr, 
			                &addrLen);

			if (cloudfd < 0) {
				
                log_err("accept (%d) client error: %d", cloud_sockfd.fd, ret);
                continue;
			}
			
			read_num = read(cloudfd, data_buf, MAX_BUF);
			log_debug("client socket is: %d, read_num: %d", 
			            cloudfd, read_num);
			
            if (read_num > 0) {
				write_num = write(cloudfd, sta_info, 
				                  (sizeof(sta_list_attr)*num + 
				                         sizeof(int)));
				log_debug("client socket is: %d, write_num: %d", 
				            cloudfd, write_num);;
				}
            }

			close(cloudfd);
		}
	}

	return ret;
}


[client端代码]

int do_filter_notify(cloud_cmd_attr * attr, char * buf, int buf_len)
{
	int ret = 0;
	int client_fd = -1;
	char recv_buf[128] = {0};
	struct sockaddr_un srv_addr;
	int maxfd = 0;
	int nfds = 0;
	fd_set  rset, allset;
	struct timeval tv;
	struct timeval t;
	/* Wait up to five seconds. */
	tv.tv_sec = CLI_TIME_OUT;
	tv.tv_usec = 0;
	
	memset(&srv_addr, 0, sizeof(struct sockaddr_un));

	client_fd = socket(AF_UNIX, SOCK_STREAM, 0);
	log_debug("client_fd: %d\n", client_fd);
	printf("client_fd: %d\n", client_fd);
	if (-1 != client_fd) 
	{
		t.tv_sec = UNIX_SOCK_CONNECT_TIMEO;
		t.tv_usec = 0; 
		
		// 对于SOL_SOCKET的level,setsockopt将调用BSD socket提供的setsockopt,
		// 否则将会调用sock本身绑定的setsockopt(AF_UNIX类型的setsockopt和
		// getsockopt都返回EOPNOTSUPP错误)
		ret = setsockopt(client_fd, SOL_SOCKET, SO_SNDTIMEO, &t, sizeof(t));
		
		srv_addr.sun_family = AF_UNIX;
		memcpy(srv_addr.sun_path, UNIX_DOMAIN_CLOUD, strlen(UNIX_DOMAIN_CLOUD));
		log_debug("srv_addr.sun_path: %s\n", srv_addr.sun_path);
		printf("srv_addr.sun_path: %s\n", srv_addr.sun_path);
		ret = connect(client_fd, (struct sockaddr *)&srv_addr, sizeof(srv_addr));
		if (-1 == ret)
		{
			log_err("error: %d, %s\n", errno, strerror(errno));
			ret = CT_RET_SYS_SOCK_CONNECT_ERR;
			goto close_fd;
		}
		else 
		{
			int write_num = 0;
			write_num = write(client_fd, attr, sizeof(cloud_cmd_attr));
			if (sizeof(cloud_cmd_attr) == write_num) 
			{
				FD_ZERO(&allset);	
				FD_SET(client_fd, &allset);
				maxfd = client_fd;
				rset = allset;
				nfds = select(maxfd + 1, &rset, NULL, NULL, &tv);
				log_debug("nfds: %d\n", nfds);
				printf("nfds: %d\n", nfds);
				if (FD_ISSET(client_fd, &rset))
				{
					int num = 0;
					if (NULL == buf)
					{
						num = read(client_fd, recv_buf, sizeof(recv_buf));
					}
					else
					{
						num = read(client_fd, buf, buf_len);
					}
					if (num <= 0) 
					{
						log_err("read %d error", client_fd);
						ret = CT_RET_FD_READ_ERR;
					}
					else
					{
						log_debug("client_fd: %d, read length: %d", client_fd, num);
						printf("client_fd: %d, read length: %d", client_fd, num);
						if (NULL == buf)
						{
							log_debug("recv_buf: %s\n", recv_buf);
							if (!strcasecmp(recv_buf, "ack"))
							{
								ret = CT_RET_SUCCESS;
							}
						}
						else
						{
							// do nothing
						}
					}
					goto close_fd;
				}
				else
				{
					log_err("no response from: %d", client_fd);
					printf("no response from: %d", client_fd);
					ret = CT_RET_SOCK_SELECT_TIMEOUT;
					goto close_fd;
				}
			}
			else
			{
				log_err("(%d) write to peer %s error (num: %d)", client_fd, UNIX_DOMAIN_CLOUD, ret);
				printf("(%d) write to peer %s error (num: %d)", client_fd, UNIX_DOMAIN_CLOUD, ret);
				ret = CT_RET_FD_WRITE_ERR;
				goto close_fd;
			}	
		}
	} 
	else
	{
		log_err("create Unix socket error");
		printf("create Unix socket error");
		ret = CT_RET_SYS_SOCK_CREATE_ERR;
	}

	return ret;
close_fd:
	close(client_fd);
	client_fd = -1;
	return ret;
}