进程间通信机制(IPC)--命名socket


  在前面所介绍的几种进程间通信(信号、管道)中所通信的进程都需要在一台计算机上,而socket域进行的通信方式,不仅可以在一台计算机,而且可以是通过网络所连接的不同计算机,这种方式称为 命名socket或者UNIX域socket,这也是一种进程间通信的方式,UNIX域数据报服务是可靠的,不会传递出错也不会丢失信息。其提供两类套接字: 字节流套接字(stream)和数据报套接字(datagram)
  socket起源于UNIX,在UNIX下一切皆文件,而socket是一种“打开——读/写——关闭”的模式,服务器端和客户端各自维护一个文件,在建立连接后,通过对文件描述符的操作可以向自己文件写入内容供对方读取或者读取对方内容,将文件描述符关闭意味着通讯结束。
  在介绍socket编程前首先我们要了解下 TCP/IP协议集:

1、TCP/IP协议集

TCP/IP(Transmission Control Protocol/Internet Protocol)即传输控制协议/网间协议,定义了主机间如何连入因特网及数据在主机间传输的标准。
  TCP/IP协议不单单是指TCP和IP的协议而是指整个因特网上TCP/IP协议族。不同于ISO的七层网络模型,TCP/IP协议参考模型是将TCP/IP系列协议抽象到四个层中:如下图所示:
在这里插入图片描述
  我们需要了解socket在TCP/IP协议参考模型中对应那一层,能够更好地了解到其特点:
在这里插入图片描述
  通过上图我们可了解到socket对应在模型中的应用层和传输层之间的一个抽象层,他把两层之间的复杂操作抽象为几个简单的接口,从而实现进程间在网络上的通信。

2、socket套接字

  用户认为两台主机间的信息传递只是建立在应用程序上,但从计网的角度而言,实际上在TCP连接中是靠套接字来传递信息。
在这里插入图片描述
  对于TCP而言,用主机的IP地址加上应用程序对应的端口号作为TCP连接的端点,这种端点叫做socket套接字或插口,用(IP地址:端口号)来表示,区分不同应用程序进程间的网络通信和连接,主要通过三个参数:通信的目的IP、使用传输层的协议(TCP或UDP)和使用的端口号

3、socket流程

在这里插入图片描述
  从上述流程图可知:

  • 服务器先根据地址类型(ipv4、ipv6),socket类型和选择协议创建socket(文件描述符)。
  • 服务器端绑定客户端的IP地址及端口号。
  • 服务器端监听端口号,等待客户端发送连接请求。服务器端socket此时未打开。
  • 客户端创建socket后打开,根据服务器的ip地址和端口号尝试连接服务器socket。
  • 服务器socket接收到客户端socket请求,直到客户端返回连接信息后,服务器端socket进入阻塞状态,即accept等待客户端发送信息后返回。
  • 客户端连接成功后向服务器发送连接状态信息。
  • 服务器accept返回连接成功。
  • 客户端写入数据后服务器端进行读取信息。
  • 客户端关闭套接字,服务器端也关闭。

4、socket编程API

  首先根据服务器端的从上至下的顺序来介绍:

4.1、socket()创建描述符

#include <sys/socket.h>			//头文件
int socket(int domain, int type, int protocol);		//原型
//成功时返回非负数

  socket函数对应于普通文件的打开操作,普通文件的打开操作返回一个文件描述符,而socket()为了创建一个socket描述符,唯一标识一个socket,后续通过对其创建为socket描述符进行读写操作,从上述流程可看出客户端或者服务器端都需要socket()来创建socket描述符
参数介绍:

  • domain :协议域,又可称为协议族(family)。
      常用的协议族有AF_INET、AF_INET6、AF_LOCAL(或称AF_UNIX,Unix域socket)、AF_ROUTE等等(AF是address family简称)。协议族决定了socket的地址类型通常AF_INET(IPv4)、AF_INET6(IPv6).
  • type:指定socket类型。
      常用的socket类型有,SOCK_STREAM、SOCK_DGRAM、SOCK_RAW、SOCK_PACKET、SOCK_SEQPACKET等等。主要SOCK_STREAM(TCP协议)、SOCK_DGRAM(UDP协议)
  • protocol:指定协议。
      常用的协议有,IPPROTO_TCP、IPPTOTO_UDP、IPPROTO_SCTP、IPPROTO_TIPC等,它们分别对应TCP传输协议、UDP传输协议、STCP传输协议、TIPC传输协议。 当protocol为0时,会自动选择type对应的默认协议,一般为 0

4.2、bind()绑定描述符

  当我们使用socket()来创建socket描述符时,返回的描述符信息存在于协议族中(address family, AF_XXX)空间中,没有一个具体的地址,因此我们需要借助bind()函数将地址族中特定的地址赋给socket,服务器启动时会绑定一个特有地址(ip地址+端口号),这样客户端可通过该地址访问服务器,而客户端就可以由系统内分配一个端口和其自身的ip地址组合(connect()时分配)。

int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

参数介绍:

  • sockfd:sockfd描述字,通过socket()创建的唯一标识。
  • addrlen:对应的地址长度。
  • addr:是一个const struct sockaddr *类型的指针,指向要绑定给sockfd的协议地址,该地址结构会根据socket创建时的地址协议族的不同而不同,但最后会统一强转赋值给sockaddr类型的指针传给内核:

  通用套接字sockaddr结构定义:

struct sockaddr {
   
  unsigned short sa_family;	 /* address family, AF_xxx */
  char sa_data[14];			 /* 14 bytes of protocol address */
  };

  ipv4对应的是sockaddr_in类型定义:

struct in_addr {
       
	uint32_t       s_addr;     
};
struct sockaddr_in {
       
	unsigned short      sin_family;  
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
进程通信 Linux进程间通信 一、进程间通信概述 进程通信有如下一些目的: A、数据传输:一个进程需要将它的数据发送给另一个进程,发送的数据量在一个字节到几M字节之间 B、共享数据:多个进程想要操作共享数据,一个进程对共享数据的修改,别的进程应该立刻看到。 C、通知事件:一个进程需要向另一个或一组进程发送消息,通知它(它们)发生了某种事件(如进程终止时要通知父进程)。 D、资源共享:多个进程之间共享同样的资源。为了作到这一点,需要内核提供锁和同步机制。 E、进程控制:有些进程希望完全控制另一个进程的执行(如Debug进程),此时控制进程希望能够拦截另一个进程的所有陷入和异常,并能够及时知道它的状态改变。 Linux 进程间通信IPC)以下以几部分发展而来: 早期UNIX进程间通信、基于System V进程间通信、基于Socket进程间通信和POSIX进程间通信。 UNIX进程间通信方式包括:管道、FIFO、信号。 System V进程间通信方式包括:System V消息队列、System V信号灯、System V共享内存、 POSIX进程间通信包括:posix消息队列、posix信号灯、posix共享内存。 现在linux使用的进程间通信方式: (1)管道(pipe)和有名管道(FIFO) (2)信号(signal) (3)消息队列 (4)共享内存 (5)信号量 (6)套接字(socket) 二、管道通信 普通的Linux shell都允许重定向,而重定向使用的就是管道。例如: ps | grep vsftpd .管道是单向的、先进先出的、无结构的、固定大小的字节流,它把一个进程的标准输出和另一个进程的标准输入连接在一起。写进程在管道的尾端写入数据,读进程在管道的道端读出数据。数据读出后将从管道中移走,其它读进程都不能再读到这些数据。管道提供了简单的流控制机制。进程试图读空管道时,在有数据写入管道前,进程将一直阻塞。同样,管道已经满时,进程再试图写管道,在其它进程从管道中移走数据之前,写进程将一直阻塞。管道主要用于不同进程间通信。 管道创建与关闭 创建一个简单的管道,可以使用系统调用pipe()。它接受一个参数,也就是一个包括两个整数的数组。如果系统调用成功,此数组将包括管道使用的两个文件描述符。创建一个管道之后,一般情况下进程将产生一个新的进程。 系统调用:pipe(); 原型:int pipe(int fd[2]); 返回值:如果系统调用成功,返回0。如果系统调用失败返回-1: errno=EMFILE(没有空亲的文件描述符) EMFILE(系统文件表已满) EFAULT(fd数组无效) 注意:fd[0]用于读取管道,fd[1]用于写入管道。 图见附件 管道的创建 #include<unistd.h> #include<errno.h> #include<stdio.h> #include<stdlib.h> int main() { int pipe_fd[2]; if(pipe(pipe_fd)<0){ printf("pipe create error\n"); return -1; } else printf("pipe create success\n"); close(pipe_fd[0]); close(pipe_fd[1]); } 管道的读写 管道主要用于不同进程间通信。实际上,通常先创建一个管道,再通过fork函数创建一个子进程。图见附件。 子进程写入和父进程读的命名管道:图见附件 管道读写注意事项: 可以通过打开两个管道来创建一个双向的管道。但需要在子理程中正确地设置文件描述符。必须在系统调用fork()中调用pipe(),否则子进程将不会继承文件描述符。当使用半双工管道时,任何关联的进程都必须共享一个相关的祖先进程。因为管道存在于系统内核之中,所以任何不在创建管道的进程的祖先进程之中的进程都将无法寻址它。而在命名管道中却不是这样。管道实例见:pipe_rw.c #include<unistd.h> #include<memory.h> #include<errno.h> #include<stdio.h> #include<stdlib.h> int main() { int pipe_fd[2]; pid_t pid; char buf_r[100]; char* p_wbuf; int r_num; memset(buf_r,0,sizeof(buf_r));数组中的数据清0; if(pipe(pipe_fd)<0){ printf("pipe create error\n"); return -1; } if((pid=fork())==0){ printf("\n"); close(pipe_fd[1]); sleep(2); if((r_num=read(pipe_fd[0],buf_r,100))>0){ printf("%d numbers read from be pipe is %s\n",r_num,buf_r); } close(pipe_fd[0]); exit(0); }else if(pid>0){ close(pipe_fd[0]); if(write(pipe_fd[1],"Hello",5)!=-1) printf("parent write success!\n"); if(write(pipe_fd[1]," Pipe",5)!=-1) printf("parent wirte2 succes!\n"); close(pipe_fd[1]); sleep(3); waitpid(pid,NULL,0); exit(0); } }

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值