linux进程间通信--本地socket(tcp部分)

[内核版本]

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; 
		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;
}
时间: 2024-10-26 15:28:11

linux进程间通信--本地socket(tcp部分)的相关文章

Linux进程间通信 -- 数据报套接字 socket()、bind()、sendto()、recvfrom()、close()

前一篇文章,Linux进程间通信——使用流套接字介绍了一些有关socket(套接字)的一些基本内容,并讲解了流套接字的使用,这篇文章将会给大家讲讲,数据报套接字的使用. 一.简单回顾——什么是数据报套接字 socket,即套接字是一种通信机制,凭借这种机制,客户/服务器(即要进行通信的进程)系统的开发工作既可以在本地单机上进行,也可以跨网络进行.也就是说它可以让不在同一台计算机但通过网络连接计算机上的进程进行通信.也因为这样,套接字明确地将客户端和服务器区分开来. 相对于流套接字,数据报套接字的

Linux进程间通信总结

Linux进程间通信总结 1. 管道 管道是Linux支持的最初Unix IPC形式之一,具有以下特点: (1)管道是半双工的,数据只能向一个方向流动:需要双方通信时,需要建立起两个管道: (2)只能用于父子进程或者兄弟进程之间(具有亲缘关系的进程): (3)单独构成一种独立的文件系统:管道对于管道两端的进程而言,就是一个文件,但它不是普通的文件,它不属于某种文件系统,而是自立门户,单独构成一种文件系统,并且只存在与内存中. (4)数据的读出和写入:一个进程向管道中写的内容被管道另一端的进程读出

linux网络编程之TCP/IP基础篇(一)

从今天起,将会接触到网络编程,平台是linux,实现语言C语言,最后将会实现一个简易的miniftp服务器. 主要的内容安排为:linux网络编程之TCP/IP基础篇,SOCKET编程篇,进程间通信篇,线程篇,实战ftp篇. 1.ISO/OSI参考模型:open system interconnection开放系统互联模型是由OSI(international organization for standardization )国际标准化组织定义的网络分层模型,共七层. 各层的具体含义: 物理层

网络编程学习笔记:linux下的socket编程

socket是进程通信的一种方式,通过调用一些API可以实现进程间通信,建立连接以及收发信息的过程如下图所示: 这些函数的用法如下: 1.int socket(int protocolFamily, int type, int protocol); 返回描述符sockfd l  protocolFamily:协议族,AF_INET(IPV4).AF_INET6(IPV6).AF_LOCAL(或称AF_UNIX,unix域socket).AF_ROUTE等.协议族决定了socket的地址类型,在通

嵌入式 Linux进程间通信(五)——进程间通信简介

嵌入式 Linux进程间通信(五)--进程间通信简介 一.进程间通信简介 Linux的进程通信方式基本上是从Unix平台上的进程通信方式继承而来的.在Unix发展过程中,贝尔实验室和BSD(加州大学伯克利分校的伯克利软件发布中心)是Unix发展的主要贡献者,但两者在进程间通信方面的侧重点有所不同.贝尔实验室对Unix早期的进程间通信方式进行了系统的改进和扩充,形成了 "system V IPC",通信进程局限在本地计算机内:BSD则跳过了进程通信局限在本地计算机的限制,形成了可以在计算

推荐两篇Linux下的Socket文章

Linux Socket Linux下Socket编程 HTTP 协议的简介 HTTP 协议的简介 HTTP协议是一种超文本传输协议(Hypertext Transfer Protocol),工作于网络应用层,自1990年起广泛应用于WWW 的全球信息服务,HTTP协议的详细说明可以在网上查阅RFC2518.RFC2616等文档. HTTP 协议老的标准是HTTP/1.0,目前最通用的标准是HTTP/1.1.HTTP/1.1是在HTTP/1.0基础上的升级,增加了一些功能,全面兼容HTTP/1.

Linux进程间通信(IPC)

一.进程间通信概述 进程通信有例如以下一些目的: A.传输数据:一个进程须要将它的数据发送给还有一个进程.发送的数据量在一个字节到几M字节之间 B.共享数据:多个进程想要操作共享数据.一个进程对共享数据的改动,别的进程应该立马看到. C.通知事件:一个进程须要向还有一个或一组进程发送消息.通知它(它们)发生了某种事件(如进程终止时要通知父进程). D.资源共享:多个进程之间共享相同的资源.为了作到这一点,须要内核提供锁和同步机制. E.进程控制:有些进程希望全然控制还有一个进程的运行(如Debu

Linux进程间通信—套接字

六.套接字(socket) socket也是一种进程间的通信机制,不过它与其他通信方式主要的区别是:它可以实现不同主机间的进程通信.一个套接口可以看做是进程间通信的端点(endpoint),每个套接口的名字是唯一的:其他进程可以访问,连接和进行数据通信. 套接口(socket)编程是实现Linux系统和其他大多数操作系统中进程间通信的主要方式之一.我们熟知的WWW服务.FTP服务.TELNET服务等都是基于套接口编程来实现的.除了在异地的计算机进程间以外,套接口同样适用于本地同一台计算机内部的进

Linux进程间通信--进程,信号,管道,消息队列,信号量,共享内存

Linux进程间通信--进程,信号,管道,消息队列,信号量,共享内存 参考:<linux编程从入门到精通>,<Linux C程序设计大全>,<unix环境高级编程> 参考:C和指针学习 说明:本文非常的长,也是为了便于查找和比较,所以放在一起了 Linux 传统的进程间通信有很多,如各类管道.消息队列.内存共享.信号量等等.但它们都无法介于内核态与用户态使用,原因如表 通信方法 无法介于内核态与用户态的原因 管道(不包括命名管道) 局限于父子进程间的通信. 消息队列 在