Linux多进程

将有关多进程的处理整理于此

1.IBM GDB

http://www.ibm.com/developerworks/cn/linux/l-cn-gdbmp/

2.在 Linux 上实现基于 Socket 的多进程实时通信

http://www.ibm.com/developerworks/cn/linux/l-socket-ipc/


3Linux多任务编程编写多进程程序及其代码

http://www.linuxidc.com/Linux/2013-06/85903p6.htm3.



//***************************************************************************************************************

实验目的

通过编写多进程程序,熟练掌握fork()、exec()、wait()和waitpid()等函数的使用,进一步理解在Linux中多进程编程的步骤。

实验内容

该实验有3个进程,其中一个为父进程,其余两个是该父进程创建的子进程,其中一个子进程运行“ls -l”指令,另一个子进程在暂停5s后异常退出。父进程先用阻塞方式等待第一个进程的结束,然后用非阻塞方式等待另一个子进程的退出,待收集到第2个子进程结束的消息后,父进程就返回。

实验步骤

该实验的流程图如下

实验源代码

先看一下下面的代码,这个程序能得到我们所希望的结果吗?它的运行会产生几个进程?

执行结果如下图

也有可能会出现下面的结果

分析执行结果可以指知道,这里其实是产生了3个子进程,在子进程2中又产生了一个子进程1。

下面咱们贴出正确的实验代码:

执行结果如下图

不论程序执行多少次,结果都是一样的。对比上一个结果,咱们就可以理解多进程该怎么创建了。一定要明确创建子进程的父进程是哪一个。

相关实验代码上传到网站,multi_proc_wrong.c下载,multi_proc.c下载

免费下载地址在 http://linux.linuxidc.com/

用户名与密码都是www.linuxidc.com

具体下载目录在 /2013年资料/6月/12日/Linux多任务编程



//***************************************************************************************************************

使用 GDB 调试多进程程序

GDB 是 linux 系统上常用的调试工具,本文介绍了使用 GDB 调试多进程程序的几种方法,并对各种方法进行比较。

田 强 (tianq@cn.ibm.com), 软件工程师, IBM中国软件开发中心

2007 年 7 月 30 日

  • +内容

GDB 是 linux 系统上常用的 c/c++ 调试工具,功能十分强大。对于较为复杂的系统,比如多进程系统,如何使用 GDB 调试呢?考虑下面这个三进程系统:

进程
进程

Proc2 是 Proc1 的子进程,Proc3 又是 Proc2 的子进程。如何使用 GDB 调试 proc2 或者 proc3 呢?

实际上,GDB 没有对多进程程序调试提供直接支持。例如,使用GDB调试某个进程,如果该进程fork了子进程,GDB会继续调试该进程,子进程会不受干扰地运行下去。如果你事先在子进程代码里设定了断点,子进程会收到SIGTRAP信号并终止。那么该如何调试子进程呢?其实我们可以利用GDB的特点或者其他一些辅助手段来达到目的。此外,GDB 也在较新内核上加入一些多进程调试支持。

接下来我们详细介绍几种方法,分别是 follow-fork-mode 方法,attach 子进程方法和 GDB wrapper 方法。

follow-fork-mode

在2.5.60版Linux内核及以后,GDB对使用fork/vfork创建子进程的程序提供了follow-fork-mode选项来支持多进程调试。

follow-fork-mode的用法为:

set follow-fork-mode [parent|child]

  • parent: fork之后继续调试父进程,子进程不受影响。
  • child: fork之后调试子进程,父进程不受影响。

因此如果需要调试子进程,在启动gdb后:

(gdb) set follow-fork-mode child

并在子进程代码设置断点。

此外还有detach-on-fork参数,指示GDB在fork之后是否断开(detach)某个进程的调试,或者都交由GDB控制:

set detach-on-fork [on|off]

  • on: 断开调试follow-fork-mode指定的进程。
  • off: gdb将控制父进程和子进程。follow-fork-mode指定的进程将被调试,另一个进程置于暂停(suspended)状态。

注意,最好使用GDB 6.6或以上版本,如果你使用的是GDB6.4,就只有follow-fork-mode模式。

follow-fork-mode/detach-on-fork的使用还是比较简单的,但由于其系统内核/gdb版本限制,我们只能在符合要求的系统上才能使用。而且,由于follow-fork-mode的调试必然是从父进程开始的,对于fork多次,以至于出现孙进程或曾孙进程的系统,例如上图3进程系统,调试起来并不方便。

Attach子进程

众所周知,GDB有附着(attach)到正在运行的进程的功能,即attach <pid>命令。因此我们可以利用该命令attach到子进程然后进行调试。

例如我们要调试某个进程RIM_Oracle_Agent.9i,首先得到该进程的pid

[root@tivf09 tianq]# ps -ef|grep RIM_Oracle_Agent.9i
nobody    6722  6721  0 05:57 ?        00:00:00 RIM_Oracle_Agent.9i
root      7541 27816  0 06:10 pts/3    00:00:00 grep -i rim_oracle_agent.9i

通过pstree可以看到,这是一个三进程系统,oserv是RIM_Oracle_prog的父进程,RIM_Oracle_prog又是RIM_Oracle_Agent.9i的父进程。

[root@tivf09 root]# pstree -H 6722
通过 pstree 察看进程
通过 pstree 察看进程

启动GDB,attach到该进程

用 GDB 连接进程
用 GDB 连接进程

现在就可以调试了。一个新的问题是,子进程一直在运行,attach上去后都不知道运行到哪里了。有没有办法解决呢?

一个办法是,在要调试的子进程初始代码中,比如main函数开始处,加入一段特殊代码,使子进程在某个条件成立时便循环睡眠等待,attach到进程后在该代码段后设上断点,再把成立的条件取消,使代码可以继续执行下去。

至于这段代码所采用的条件,看你的偏好了。比如我们可以检查一个指定的环境变量的值,或者检查一个特定的文件存不存在。以文件为例,其形式可以如下:

void debug_wait(char *tag_file)
{
    while(1)
    {
        if (tag_file存在)
            睡眠一段时间;
        else
            break;
    }
}

当attach到进程后,在该段代码之后设上断点,再把该文件删除就OK了。当然你也可以采用其他的条件或形式,只要这个条件可以设置/检测即可。

Attach进程方法还是很方便的,它能够应付各种各样复杂的进程系统,比如孙子/曾孙进程,比如守护进程(daemon process),唯一需要的就是加入一小段代码。

GDB wrapper

很多时候,父进程 fork 出子进程,子进程会紧接着调用 exec族函数来执行新的代码。对于这种情况,我们也可以使用gdb wrapper 方法。它的优点是不用添加额外代码。

其基本原理是以gdb调用待执行代码作为一个新的整体来被exec函数执行,使得待执行代码始终处于gdb的控制中,这样我们自然能够调试该子进程代码。

还是上面那个例子,RIM_Oracle_prog fork出子进程后将紧接着执行RIM_Oracle_Agent.9i的二进制代码文件。我们将该文件重命名为RIM_Oracle_Agent.9i.binary,并新建一个名为RIM_Oracle_Agent.9i的shell脚本文件,其内容如下:

[root@tivf09 bin]# mv RIM_Oracle_Agent.9i RIM_Oracle_Agent.9i.binary
[root@tivf09 bin]# cat RIM_Oracle_Agent.9i
#!/bin/sh
gdb RIM_Oracle_Agent.binary

当fork的子进程执行名为RIM_Oracle_Agent.9i的文件时,gdb会被首先启动,使得要调试的代码处于gdb控制之下。

新的问题来了。子进程是在gdb的控制下了,但还是不能调试:如何与gdb交互呢?我们必须以某种方式启动gdb,以便能在某个窗口/终端与gdb交互。具体来说,可以使用xterm生成这个窗口。

xterm是X window系统下的模拟终端程序。比如我们在Linux桌面环境GNOME中敲入xterm命令:

xterm
xterm

就会跳出一个终端窗口:

终端
终端

如果你是在一台远程linux服务器上调试,那么可以使用VNC(Virtual Network Computing) viewer从本地机器连接到服务器上使用xterm。在此之前,需要在你的本地机器上安装VNC viewer,在服务器上安装并启动VNC server。大多数linux发行版都预装了vnc-server软件包,所以我们可以直接运行vncserver命令。注意,第一次运行vncserver时会提示输入密码,用作VNC viewer从客户端连接时的密码。可以在VNC server机器上使用vncpasswd命令修改密码。

[root@tivf09 root]# vncserver 

New 'tivf09:1 (root)' desktop is tivf09:1

Starting applications specified in /root/.vnc/xstartup
Log file is /root/.vnc/tivf09:1.log

[root@tivf09 root]#
[root@tivf09 root]# ps -ef|grep -i vnc
root     19609     1  0 Jun05 ?        00:08:46 Xvnc :1 -desktop tivf09:1 (root) 
  -httpd /usr/share/vnc/classes -auth /root/.Xauthority -geometry 1024x768 
  -depth 16 -rfbwait 30000 -rfbauth /root/.vnc/passwd -rfbport 5901 -pn
root     19627     1  0 Jun05 ?        00:00:00 vncconfig -iconic
root     12714 10599  0 01:23 pts/0    00:00:00 grep -i vnc
[root@tivf09 root]#

Vncserver是一个Perl脚本,用来启动Xvnc(X VNC server)。X client应用,比如xterm,VNC viewer都是和它通信的。如上所示,我们可以使用的DISPLAY值为tivf09:1。现在就可以从本地机器使用VNC viewer连接过去:

VNC viewer:输入服务器
VNC viewer:输入服务器

输入密码:

VNC viewer:输入密码
VNC viewer:输入密码

登录成功,界面和服务器本地桌面上一样:

VNC viewer
VNC viewer

下面我们来修改RIM_Oracle_Agent.9i脚本,使它看起来像下面这样:

#!/bin/sh
export DISPLAY=tivf09:1.0; xterm -e gdb RIM_Oracle_Agent.binary

如果你的程序在exec的时候还传入了参数,可以改成:

#!/bin/sh
export DISPLAY=tivf09:1.0; xterm -e gdb --args RIM_Oracle_Agent.binary $@

最后加上执行权限

[root@tivf09 bin]# chmod 755 RIM_Oracle_Agent.9i

现在就可以调试了。运行启动子进程的程序:

[root@tivf09 root]# wrimtest -l 9i_linux
Resource Type  : RIM
Resource Label : 9i_linux
Host Name      : tivf09
User Name      : mdstatus
Vendor         : Oracle
Database       : rim
Database Home  : /data/oracle9i/920
Server ID      : rim
Instance Home  : 
Instance Name  : 
Opening Regular Session...

程序停住了。从VNC viewer中可以看到,一个新的gdb xterm窗口在服务器端打开了

gdb xterm 窗口
gdb xterm窗口
[root@tivf09 root]# ps -ef|grep gdb
nobody   24312 24311  0 04:30 ?        00:00:00 xterm -e gdb RIM_Oracle_Agent.binary
nobody   24314 24312  0 04:30 pts/2    00:00:00 gdb RIM_Oracle_Agent.binary
root     24326 10599  0 04:30 pts/0    00:00:00 grep gdb

运行的正是要调试的程序。设置好断点,开始调试吧!

注意,下面的错误一般是权限的问题,使用 xhost 命令来修改权限:

xterm 错误
xterm 错误
[root@tivf09 bin]# export DISPLAY=tivf09:1.0
[root@tivf09 bin]# xhost +
access control disabled, clients can connect from any host

xhost + 禁止了访问控制,从任何机器都可以连接过来。考虑到安全问题,你也可以使用xhost + <你的机器名>。

小结

上述三种方法各有特点和优劣,因此适应于不同的场合和环境:

  • follow-fork-mode方法:方便易用,对系统内核和GDB版本有限制,适合于较为简单的多进程系统
  • attach子进程方法:灵活强大,但需要添加额外代码,适合于各种复杂情况,特别是守护进程
  • GDB wrapper方法:专用于fork+exec模式,不用添加额外代码,但需要X环境支持(xterm/VNC)。
//********************************************************************************************************************************

在 Linux 上实现基于 Socket 的多进程实时通信

套接口(Socket)为目前 Linux 上最为广泛使用的一种的进程间通信机制。但是它不能直接用来多进程之间的相互实时通信。本文提出一个基于 Socket 的多进程之间通信的实现方法,并给出样本程序的实现和说明。

周欣 (jones_zhou@yahoo.com.cn), 软件工程师

2005 年 3 月 01 日

  • +内容

套接口(Socket)为目前Linux上最为广泛使用的一种的进程间通信机制,与其他的Linux通信机制不同之处在于除了它可用于单机内的进程间通信以外,还可用于不同机器之间的进程间通信。但是由于Socket本身不支持同时等待和超时处理,所以它不能直接用来多进程之间的相互实时通信。

本文提出一个基于Socket的多进程之间通信的实现方法。原理是建立一个进程专门用来做为通信服务器(server)来中转各个进程之间的通信。它首先启动一个用来监视连接要求的listening Socket,并把它的描述(Descriptor)号加入到一个事先定义好的fd_set的集合中,这个fd_set的集合用来存放listening Socket和后来生成的通信Socket的描述号。Server运用system call select来实时检查是否有数据到达这个集合中的任何一个socket,如果有数据到达listening Socket,则这一定是客户端发起的连接请求,于是生成一个新的通信Socket与该客户端连接,将生成的Socket描述号加入到fd_set的集合中,将客户端的ID号和与之对应的Socket的描述号记录在ID登记表中。如果有数据到达某个通信Socket,则这一定是某个客户端发起的通信请求,读出数据并取出收信客户端ID号,在ID登记表中找到与之对应的Socket描述号,将数据通过对应Socket传送到收信客户端。

其他各进程作为客户端(client)。客户端的动作是首先建立通信Socket连接服务器端,然后通过通信Socket进行送信和收信。

下面给出具体的程序实现和说明,

首先给出Server端的程序,在这里假设有两个客户端要进行实时通信,ClientA向ClientB发送字符1,ClientB向ClientA发送字符2。

  #include  <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <sys/un.h>
#include <sys/time.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <netinet/in.h>
int main()
{
  int        rcd ;
  struct sockaddr_un  server_sockaddr ;
  int        backlog ;
  ushort        ci ;
  int        watch_fd_list[3] ;
  fd_set        catch_fd_set ;
  fd_set        watchset ;
  int    new_cli_fd ;
  int   maxfd;
  int    socklen ,server_len;
  struct sockaddr_un  cli_sockaddr ;
  struct {
    char  module_id ;  /* Module ID    */
    int  cli_sock_fd ;  /* Socket ID    */
  } cli_info_t[2] ;
  
  for (ci=0;ci<=1;ci++)
    cli_info_t[ci].cli_sock_fd=-1;
    
  for (ci=0;ci<=2;ci++)
    watch_fd_list[ci]=-1;  
    
  int server_sockfd,client_sockfd;
  
  server_sockfd = socket( AF_UNIX, SOCK_STREAM, 0 ) ;
  server_sockaddr.sun_family = AF_UNIX ;
  strcpy( server_sockaddr.sun_path, "server_socket" ) ;
  server_len=sizeof(server_sockaddr);
  rcd = bind( server_sockfd, ( struct sockaddr * )&server_sockaddr, server_len ) ;
  
  backlog = 5 ;
  rcd = listen( server_sockfd, backlog ) ;
  printf("SERVER::Server is  waitting on socket=%d \n",server_sockfd);
  watch_fd_list[0]=server_sockfd;
  FD_ZERO( &watchset ) ;
  FD_SET( server_sockfd, &watchset ) ;
  maxfd=watch_fd_list[0];

在上面的程序中,Server生成listening Socket(server_sockfd),初始化Socket监视集合(watchset),并将listening Socket放入Socket监视集合中。

			while (1){
		char ch;
		int fd;
		int nread;
		
		catch_fd_set=watchset;
		rcd = select( maxfd+1, &catch_fd_set, NULL, NULL, (struct timeval *)0 ) ;

在上面的程序中,Server运用系统调用函数 select来实时检查是否有数据到达Socket监视集合中的任何一个socket。

        if ( rcd < 0 ) {
      printf("SERVER::Server 5 \n");
      exit(1);
    }
    if ( FD_ISSET( server_sockfd, &catch_fd_set ) ) {
      socklen = sizeof( cli_sockaddr ) ;
      new_cli_fd = accept( server_sockfd, ( struct sockaddr * )
        &( cli_sockaddr ), &socklen ) ;
      printf(" SERVER::open communication with  Client %s on socket %d\n", 
           cli_sockaddr.sun_path,new_cli_fd);  
  
      for (ci=1;ci<=2;ci++){
        if(watch_fd_list[ci] != -1) continue;
        else{  
          watch_fd_list[ci] = new_cli_fd;
          break;
        }  
      }  
      FD_SET(new_cli_fd , &watchset ) ;
      if ( maxfd < new_cli_fd ) {
        maxfd = new_cli_fd ;
      }
      
      for ( ci=0;ci<=1;ci++){
        if(cli_info_t[ci].cli_sock_fd == -1) {
          cli_info_t[ci].module_id=cli_sockaddr.sun_path[0];
          cli_info_t[ci].cli_sock_fd=new_cli_fd;
          break;
        }    
      }
        
      continue;  
    }

在上面的程序中,Server运用系统调用函数FD_ISSET来检查是否有客户端的连接请求到达Listening Socket, 如果返回值大于0,Server生成一个新的通信Socket (new_cli_fd)与客户端连接。将新生成的通信Socket放入Socket监视集合中(FD_SET)。将客户端的信息(ID号和Socket描述号)保存在注册表cli_info_t中

    for ( ci = 1; ci<=2 ; ci++ ) {
      int      dst_fd = -1 ;
      char      dst_module_id;
      char       src_module_id;
      int    i;
      if (watch_fd_list[ ci ]==-1) continue;
      if ( !FD_ISSET( watch_fd_list[ ci ], &catch_fd_set ) ) {
        continue ;
      }
      ioctl(watch_fd_list[ ci ],FIONREAD,&nread);
      if (nread==0){
        continue;
      }  
      read( watch_fd_list[ ci ], &dst_module_id, 1 ) ;
      for (i=0;i<=1;i++){
        if(cli_info_t[i].module_id == dst_module_id) 
          dst_fd=  cli_info_t[i].cli_sock_fd;  
         if(cli_info_t[i].cli_sock_fd==watch_fd_list[ ci ]) 
        src_module_id=  cli_info_t[i].module_id;    
      }
      read( watch_fd_list[ ci ], &ch, 1 ) ;
      printf("SERVER::char=%c to  Client %c on socket%d\n",ch, dst_module_id,dst_fd);  
      write(dst_fd,&src_module_id, 1 ) ;
      write(dst_fd,&ch, 1 ) ;
    }
  }  
}

在上面的程序中,如果有数据到达某个通信Socket,Server则读出数据并取出收信客户端ID号。在ID登记表中找到收信客户端对应的Socket描述号。并将数据通过对应Socket传送到收信客户端

给出客户端 ClientA的程序

ClientB的程序只需将 char dst_module_id='B'; 改为char dst_module_id='A';char ch='1'; 改为char char ch='2';既可。

#include  <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <sys/un.h>
#include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h>
int main(){
	
	int client_sockfd;
	int len;
	struct sockaddr_un server_sockaddr,cli_sockaddr;
	int result;
	char dst_module_id='B';
	char ch='1';
	char src_module_id;
	
	
	client_sockfd= socket(AF_UNIX,SOCK_STREAM,0);
	
	cli_sockaddr.sun_family = AF_UNIX ;
	strcpy( cli_sockaddr.sun_path, "A" ) ;
	bind(client_sockfd,(struct sockaddr * )&cli_sockaddr, sizeof( cli_sockaddr ) ) ;
	server_sockaddr.sun_family=AF_UNIX;
	strcpy( server_sockaddr.sun_path, "server_socket" ) ;
	len=sizeof(server_sockaddr);
	
	result = connect(client_sockfd,( struct sockaddr * )&server_sockaddr,len);
	if (result <0){
		printf("ClientA::error on connecting \n");	
		exit(1);
	}
	
	printf("ClientA::succeed in connecting with server\n");
	sleep(10);
	write(client_sockfd,&dst_module_id,1);
	write(client_sockfd,&ch,1); 
	read (client_sockfd,&src_module_id,1); 
	read (client_sockfd,&ch,1); 
	printf("ClientA::char from  Client %c =%c\n", src_module_id,ch);	
	close (client_sockfd);
	
}

下面是样本程序的执行结果

[root@zhou test]# ./server &
[3] 4301
[root@zhou test]# SERVER::Server is  waitting on socket=3
./clientA & ./clientB &
[4] 4302
[5] 4303
ClientA::succeed in connecting with server
 SERVER::open communication with  Client A on socket 4
[root@zhou test]#  SERVER::open communication with  Client B on socket 5
ClientB::succeed in connecting with server
SERVER::char=1 to  Client B on socket5
ClientB::char from  Client A =1
SERVER::char=2 to  Client A on socket4
ClientA::char from  Client B =2

程序清单下载:server.c, clientA.c

#include  <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <sys/un.h>
#include <sys/time.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <netinet/in.h>


int main()
{
    int                rcd ;
    struct sockaddr_un    server_sockaddr ;
    int                backlog ;
    ushort                ci ;
    int                watch_fd_list[3] ;
    fd_set                catch_fd_set ;
    fd_set                watchset ;
    int    new_cli_fd ;
    int     maxfd;
    int    socklen,server_len;
    struct sockaddr_un    cli_sockaddr ;
    struct {
        char        module_id ;        /* Module ID            */
        int        cli_sock_fd ;    /* Socket ID        */
    } cli_info_t[2] ;
    
    for (ci=0;ci<=1;ci++)
        cli_info_t[ci].cli_sock_fd=-1;
        
    for (ci=0;ci<=2;ci++)
        watch_fd_list[ci]=-1;    
        
    int server_sockfd,client_sockfd;
    
    server_sockfd = socket( AF_UNIX, SOCK_STREAM, 0 ) ;
    server_sockaddr.sun_family = AF_UNIX ;
//    server_sockaddr.sin_addr.s_addr=htonl(INADDR_ANY);
//    server_sockaddr.sin_port=htons(9734);
    strcpy( server_sockaddr.sun_path, "server_socket" ) ;
    server_len=sizeof(server_sockaddr);
    rcd = bind( server_sockfd, ( struct sockaddr * )&server_sockaddr, server_len ) ;
    
    backlog = 5 ;
    rcd = listen( server_sockfd, backlog ) ;
    printf("SERVER::Server is  waitting on socket=%d \n",server_sockfd);

    watch_fd_list[0]=server_sockfd;
    FD_ZERO( &watchset ) ;
    FD_SET( server_sockfd, &watchset ) ;
    maxfd=watch_fd_list[0];
    
    while (1){
        char ch;
        int fd;
        int nread;
        
        catch_fd_set=watchset;
        rcd = select( maxfd+1, &catch_fd_set, NULL, NULL, (struct timeval *)0 ) ;
        if ( rcd < 0 ) {
            printf("SERVER::Server 5 \n");
            exit(1);
        }

        if ( FD_ISSET( server_sockfd, &catch_fd_set ) ) {
            socklen = sizeof( cli_sockaddr ) ;
            new_cli_fd = accept( server_sockfd, ( struct sockaddr * )&( cli_sockaddr ), &socklen ) ;
            printf(" SERVER::open communication with  Client %s on socket %d\n", cli_sockaddr.sun_path,new_cli_fd);    
    
            for (ci=1;ci<=2;ci++){
                if(watch_fd_list[ci] != -1) continue;
                else{    
                    watch_fd_list[ci] = new_cli_fd;
                    break;
                }    
            }    
            FD_SET(new_cli_fd , &watchset ) ;
            if ( maxfd < new_cli_fd ) {
                maxfd = new_cli_fd ;
            }
            
            for ( ci=0;ci<=1;ci++){
                if(cli_info_t[ci].cli_sock_fd == -1) {
                    cli_info_t[ci].module_id=cli_sockaddr.sun_path[0];
                    cli_info_t[ci].cli_sock_fd=new_cli_fd;
                    break;
                }        
            }
               


----------------------------------

#include  <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <sys/un.h>
#include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h>

int main(){
    
    int client_sockfd;
    int len;
    struct sockaddr_un server_sockaddr,cli_sockaddr;
    int result;
    char dst_module_id='B';
    char ch='1';
    char src_module_id;
    
    
    client_sockfd= socket(AF_UNIX,SOCK_STREAM,0);
    
//    memset( &cli_sockaddr, 0, sizeof( cli_sockaddr) ) ;
    cli_sockaddr.sun_family = AF_UNIX ;
    strcpy( cli_sockaddr.sun_path, "A" ) ;
    bind( client_sockfd, ( struct sockaddr * )&cli_sockaddr, sizeof( cli_sockaddr ) ) ;

    server_sockaddr.sun_family=AF_UNIX;
//    server_sockaddr.sin_addr.s_addr=htonl(INADDR_ANY);
//    server_sockaddr.sin_port=htons(9734);
    strcpy( server_sockaddr.sun_path, "server_socket" ) ;
    len=sizeof(server_sockaddr);
    
    result = connect(client_sockfd,( struct sockaddr * )&server_sockaddr,len);
    if (result <0){
        printf("ClientA::error on connecting \n");    
//        exit(1);
    }
    
    printf("ClientA::succeed in connecting with server\n");
    sleep(10);
    write(client_sockfd,&dst_module_id,1);
    write(client_sockfd,&ch,1);
    read (client_sockfd,&src_module_id,1);
    read (client_sockfd,&ch,1);
    printf("ClientA::char from  Client %c =%c\n", src_module_id,ch);    
    close (client_sockfd);
    
}



  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值