libevent源码分析(三)

在libevent(二)中最后我提到了socketpair,不同于系统调用socketpair,libevent自己使用本地通信实例(AF_UNIX)实现了socketpair的功能,我不知道二者实现方式是不是一直,但原理上应该是一致的。
我把evsignal_init函数中的evutil_socketpair提取出来并做测试,代码如下:
源码地址:https://github.com/jeremy505/multi-thread-communication

#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <memory.h>
#include <pthread.h>
#include <arpa/inet.h>
#include <unistd.h>
int evutil_socketpair(int family, int type, int protocol, int fd[2])
{
	/* This code is originally from Tor.  Used with permission. */
	 /* This socketpair does not work when localhost is down. So
	  * it's really not the same thing at all. But it's close enough
	  * for now, and really, when localhost is down sometimes, we
	  * have other problems too.
	  */
	 int listener = -1;
	 int connector = -1;
	 int acceptor = -1;
	 struct sockaddr_in listen_addr;
	 struct sockaddr_in connect_addr;
	 int size;
	 int saved_errno = -1;
	  if (protocol
	#ifdef AF_UNIX
	  || family != AF_UNIX
	#endif
	  ) {
		  return -1;
	 }
	 if (!fd) {
		  return -1;
	 }
	 listener = socket(AF_INET, type, 0);
	 if (listener < 0)
		  return -1;
	 memset(&listen_addr, 0, sizeof(listen_addr));
	 listen_addr.sin_family = AF_INET;
	 listen_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
	 listen_addr.sin_port = 0; /* kernel chooses port.  */
	 if (bind(listener, (struct sockaddr *) &listen_addr, sizeof (listen_addr))  == -1)
		  goto tidy_up_and_fail;
	 printf("listen_addr.sin_addr.s_addr=0x%x string=%s--listen_addr.sin_port=%d\n", 
	           listen_addr.sin_addr.s_addr, (char*)inet_ntoa(listen_addr.sin_addr), 
	           listen_addr.sin_port);
	 if (listen(listener, 1) == -1)
		  goto tidy_up_and_fail;		  
	 connector = socket(AF_INET, type, 0);   
	 if (connector < 0)
	  goto tidy_up_and_fail;
	 /* We want to find out the port number to connect to.  */
	 size = sizeof(connect_addr);
	 if (getsockname(listener, (struct sockaddr *) &connect_addr, &size) == -1)
	  goto tidy_up_and_fail;
	 printf("connect_addr.sin_addr.s_addr=0x%x string=%s--connect_addr.sin_port=%d\n",
	         connect_addr.sin_addr.s_addr, (char*)inet_ntoa(connect_addr.sin_addr), 
	         connect_addr.sin_port);
	 if (size != sizeof (connect_addr))
	  goto abort_tidy_up_and_fail;
	 if (connect(connector, (struct sockaddr *) &connect_addr,
	    sizeof(connect_addr)) == -1)
	  goto tidy_up_and_fail;
	  
	 size = sizeof(listen_addr);
	 acceptor = accept(listener, (struct sockaddr *) &listen_addr, &size);
	 if (acceptor < 0)
		  goto tidy_up_and_fail;
	 if (size != sizeof(listen_addr))
		  goto abort_tidy_up_and_fail;
	 /* Now check we are talking to ourself by matching port and host on the
	    two sockets.  */
	 printf("listen_addr.sin_addr.s_addr=0x%x string=%s--listen_addr.sin_port=%d\n", 
	         listen_addr.sin_addr.s_addr, (char*)inet_ntoa(listen_addr.sin_addr), 
	         listen_addr.sin_port);
	 if (getsockname(connector, (struct sockaddr *) &connect_addr, &size) == -1)
		  goto tidy_up_and_fail;
	 printf("connect_addr.sin_addr.s_addr=0x%x string=%s--connect_addr.sin_port=%d\n", 
                 connect_addr.sin_addr.s_addr, (char*)inet_ntoa(connect_addr.sin_addr), 
	         connect_addr.sin_port);
	 if (size != sizeof (connect_addr)
		  || listen_addr.sin_family != connect_addr.sin_family
		  || listen_addr.sin_addr.s_addr != connect_addr.sin_addr.s_addr
		  || listen_addr.sin_port != connect_addr.sin_port)
		  goto abort_tidy_up_and_fail;
	 fd[0] = connector;
	 fd[1] = acceptor;
	 return 0;
	 abort_tidy_up_and_fail:
	 tidy_up_and_fail:
	 return -1;
}
void* work_thread(void* argc)
{
	 int* fd = (int*)argc;
	 while(1)
	 {
		  char c[50];
		  int n = recv(fd[1], &c, sizeof(c), 0);
		  printf("recv%dbyte=%s\n", n, c);
		  // sleep(1);
		     char b[]="Goodbye!";
		  n = send(fd[1], &b, sizeof(b), 0);
		  printf("send%dbyte=%s\n", n, b);
	}
}

int main(int argc, char* argv[])
{
   int socket_pair[2];
   pthread_t tid;
   if (evutil_socketpair(AF_UNIX, SOCK_STREAM, 0, socket_pair) == -1)
   {
      perror("create socketpair failed\n");
   return -1;
   }
  /**//使用socketpair系统调用结果一致
   if(socketpair(AF_UNIX, SOCK_STREAM, 0, socket_pair) == -1)
   {
    perror("create socketpair failed\n");
    return -1;
   }
   **/
   pthread_create(&tid, NULL,
      work_thread, &socket_pair[0]);
   while(1)
   {
    char c[] = "Hello,thread!";
    int n = send(socket_pair[0], &c, sizeof(c), 0);
    printf("++send%dbyte=%s\n", n, c);
    sleep(1);
    n = recv(socket_pair[0], &c, sizeof(c), 0);
    printf("++recv%dbyte=%s\n", n, c);
   }
   pthread_join(tid, NULL);
   close(socket_pair[0]);
   close(socket_pair[1]);
   return 0;
}

以上代码实现了线程之间的全双工通信
evutil_socketpair(AF_UNIX, SOCK_STREAM, 0, socket_pair)
是libevent源码实现方式,
用系统调用socketpair(AF_UNIX, SOCK_STREAM, 0, socket_pair)
结果一致。
输出见如下:
使用evutil_socketpair(包含调试信息)
在这里插入图片描述
使用系统调用socketpair:
在这里插入图片描述
可以分析 打印信息,socketpair实现了线程之间的双向通信,使用pipe只能单向,如果需要双向通信,需要创建两组pipe来实现,所以我觉得如果需要双向通信可以优先选择使用socketpair,实现原理可以研究evutil_socketpair,同样,可以将sockertpair应用于进程之间。其实进程和线程之间的区别仅仅在于线程之间共享数据,而进程则是各自都有一份数据,互不影响,很多东西二者都是适用的,另外线程又被称为轻进程。

猜你喜欢

转载自blog.csdn.net/weixin_38134600/article/details/82807787