Linux进程间通信方式--本地socket

先上一个代码

服务端:


[cpp] view plaincopy


  1. //s_unix.c  
  2. #include <stdio.h>  
  3. #include <sys/types.h>  
  4. #include <sys/socket.h>  
  5. #include <sys/un.h>   
  6. #define UNIX_DOMAIN "/tmp/UNIX.domain"  
  7. int main(void)  
  8. {  
  9.     socklen_t clt_addr_len;  
  10.     int listen_fd;  
  11.     int com_fd;  
  12.     int ret;  
  13.     int i;  
  14.     static char recv_buf[1024];   
  15.     int len;  
  16.     struct sockaddr_un clt_addr;  
  17.     struct sockaddr_un srv_addr;  
  18.     listen_fd=socket(PF_UNIX,SOCK_STREAM,0);  
  19.     if(listen_fd<0)  
  20.     {  
  21.         perror("cannot create communication socket");  
  22.         return 1;  
  23.     }    
  24.      
  25.     //set server addr_param  
  26.     srv_addr.sun_family=AF_UNIX;  
  27.     strncpy(srv_addr.sun_path,UNIX_DOMAIN,sizeof(srv_addr.sun_path)-1);  
  28.     unlink(UNIX_DOMAIN);  
  29.     //bind sockfd & addr  
  30.     ret=bind(listen_fd,(struct sockaddr*)&srv_addr,sizeof(srv_addr));  
  31.     if(ret==-1)  
  32.     {  
  33.         perror("cannot bind server socket");  
  34.         close(listen_fd);  
  35.         unlink(UNIX_DOMAIN);  
  36.         return 1;  
  37.     }  
  38.     //listen sockfd   
  39.     ret=listen(listen_fd,1);  
  40.     if(ret==-1)  
  41.     {  
  42.         perror("cannot listen the client connect request");  
  43.         close(listen_fd);  
  44.         unlink(UNIX_DOMAIN);  
  45.         return 1;  
  46.     }  
  47.     //have connect request use accept  
  48.     len=sizeof(clt_addr);  
  49.     com_fd=accept(listen_fd,(struct sockaddr*)&clt_addr,&len);  
  50.     if(com_fd<0)  
  51.     {  
  52.         perror("cannot accept client connect request");  
  53.         close(listen_fd);  
  54.         unlink(UNIX_DOMAIN);  
  55.         return 1;  
  56.     }  
  57.     //read and printf sent client info  
  58.     printf("/n=====info=====/n");  
  59.     for(i=0;i<4;i++)  
  60.     {  
  61.         memset(recv_buf,0,1024);  
  62.         int num=read(com_fd,recv_buf,sizeof(recv_buf));  
  63.         printf("Message from client (%d)) :%s/n",num,recv_buf);    
  64.     }  
  65.     close(com_fd);  
  66.     close(listen_fd);  
  67.     unlink(UNIX_DOMAIN);  
  68.     return 0;  
  69. }  


客户端:



[cpp] view plaincopy

CODE_ico.png


  1. //c_unix.c  
  2. #include <stdio.h>  
  3. #include <sys/types.h>  
  4. #include <sys/socket.h>  
  5. #include <sys/un.h>  
  6. #define UNIX_DOMAIN "/tmp/UNIX.domain"  
  7. int main(void)  
  8. {  
  9.     int connect_fd;  
  10.     int ret;  
  11.     char snd_buf[1024];  
  12.     int i;  
  13.     static struct sockaddr_un srv_addr;  
  14. //creat unix socket  
  15.     connect_fd=socket(PF_UNIX,SOCK_STREAM,0);  
  16.     if(connect_fd<0)  
  17.     {  
  18.         perror("cannot create communication socket");  
  19.         return 1;  
  20.     }     
  21.     srv_addr.sun_family=AF_UNIX;  
  22.     strcpy(srv_addr.sun_path,UNIX_DOMAIN);  
  23. //connect server  
  24.     ret=connect(connect_fd,(struct sockaddr*)&srv_addr,sizeof(srv_addr));  
  25.     if(ret==-1)  
  26.     {  
  27.         perror("cannot connect to the server");  
  28.         close(connect_fd);  
  29.         return 1;  
  30.     }  
  31.     memset(snd_buf,0,1024);  
  32.     strcpy(snd_buf,"message from client");  
  33. //send info server  
  34.     for(i=0;i<4;i++)  
  35.         write(connect_fd,snd_buf,sizeof(snd_buf));  
  36.     close(connect_fd);  
  37.     return 0;  
  38. }  


使用套接字除了可以实现网络间不同主机间的通信外,还可以实现同一主机的不同进程间的通信,且建立的通信是双向的通信。socket进程通信与网络通信使用的是统一套接口,只是地址结构与某些参数不同。

一、创建socket流程

1)创建socket,类型为AF_LOCALAF_UNIX,表示用于进程通信:

创建套接字需要使用 socket 系统调用,其原型如下:


int socket(int domain, int type, int protocol);


其中,domain 参数指定协议族,对于本地套接字来说,其值须被置为 AF_UNIX 枚举值;type 参数指定套接字类型,protocol 参数指定具体协议;type 参数可被设置为 SOCK_STREAM(流式套接字)或 SOCK_DGRAM(数据报式套接字),protocol 字段应被设置为 0;其返回值为生成的套接字描述符。


对于本地套接字来说,流式套接字(SOCK_STREAM)是一个有顺序的、可靠的双向字节流,相当于在本地进程之间建立起一条数据通道;数据报式套接字(SOCK_DGRAM)相当于单纯的发送消息,在进程通信过程中,理论上可能会有信息丢失、复制或者不按先后次序到达的情况,但由于其在本地通信,不通过外界网络,这些情况出现的概率很小。


二、命名socket

SOCK_STREAM 式本地套接字的通信双方均需要具有本地地址,其中服务器端的本地地址需要明确指定,指定方法是使用 struct sockaddr_un 类型的变量。

struct sockaddr_un {
    sa_family_t     sun_family;     /* AF_UNIX */
    char    sun_path[UNIX_PATH_MAX];        /* 路径名 */
};

这里面有一个很关键的东西,socket进程通信命名方式有两种。一是普通的命名,socket会根据此命名创建一个同名的socket文件,客户端连接的时候通过读取该socket文件连接到socket服务端。这种方式的弊端是服务端必须对socket文件的路径具备写权限,客户端必须知道socket文件路径,且必须对该路径有读权限。

另外一种命名方式是抽象命名空间,这种方式不需要创建socket文件,只需要命名一个全局名字,即可让客户端根据此名字进行连接。后者的实现过程与前者的差别是,后者在对地址结构成员sun_path数组赋值的时候,必须把第一个字节置0,即sun_path[0] = 0,下面用代码说明:


第一种方式:

[cpp] view plaincopy

CODE_ico.png


  1. //name the server socket   
  2.     server_addr.sun_family = AF_UNIX;  
  3.     strcpy(server_addr.sun_path,"/tmp/UNIX.domain");  
  4.     server_len = sizeof(struct sockaddr_un);  
  5.     client_len = server_len;  




第二种方式:



[cpp] view plaincopy

CODE_ico.png


  1. #define SERVER_NAME @socket_server  


[cpp] view plaincopy

CODE_ico.png


  1. //name the socket  
  2.   server_addr.sun_family = AF_UNIX;  
  3.   strcpy(server_addr.sun_path, SERVER_NAME);  
  4.   server_addr.sun_path[0]=0;  
  5.   //server_len = sizeof(server_addr);  
  6.   server_len = strlen(SERVER_NAME)  + offsetof(struct sockaddr_un, sun_path);  




其中,offsetof函数在#include <stddef.h>头文件中定义。因第二种方式的首字节置0,我们可以在命名字符串SERVER_NAME前添加一个占位字符串,例如:

[cpp] view plaincopy

CODE_ico.png


  1. #define SERVER_NAME @socket_server    


前面的@符号就表示占位符,不算为实际名称。


提示:客户端连接服务器的时候,必须与服务端的命名方式相同,即如果服务端是普通命名方式,客户端的地址也必须是普通命名方式;如果服务端是抽象命名方式,客户端的地址也必须是抽象命名方式。


三、绑定

SOCK_STREAM 式本地套接字的通信双方均需要具有本地地址,其中服务器端的本地地址需要明确指定,指定方法是使用 struct sockaddr_un 类型的变量,将相应字段赋值,再将其绑定在创建的服务器套接字上,绑定要使用 bind 系统调用,其原形如下:


int bind(int socket, const struct sockaddr *address, size_t address_len);


其中 socket表示服务器端的套接字描述符,address 表示需要绑定的本地地址,是一个 struct sockaddr_un 类型的变量,address_len 表示该本地地址的字节长度。实现服务器端地址指定功能的代码如下(假设服务器端已经通过上文所述的 socket 系统调用创建了套接字,server_sockfd 为其套接字描述符):
struct sockaddr_un server_address;
server_address.sun_family = AF_UNIX;
strcpy(server_address.sun_path, "Server Socket");
bind(server_sockfd, (struct sockaddr*)&server_address, sizeof(server_address));


客户端的本地地址不用显式指定,只需能连接到服务器端即可,因此,客户端的 struct sockaddr_un 类型变量需要根据服务器的设置情况来设置,代码如下(假设客户端已经通过上文所述的 socket 系统调用创建了套接字,client_sockfd 为其套接字描述符):
struct sockaddr_un client_address;
client_address.sun_family = AF_UNIX;
strcpy(client_address.sun_path, "Server Socket");

四、监听
服务器端套接字创建完毕并赋予本地地址值(名称,本例中为Server Socket)后,需要进行监听,等待客户端连接并处理请求,监听使用 listen 系统调用,接受客户端连接使用accept系统调用,它们的原形如下:
int listen(int socket, int backlog);
int accept(int socket, struct sockaddr *address, size_t *address_len);
其中 socket 表示服务器端的套接字描述符;backlog 表示排队连接队列的长度(若有多个客户端同时连接,则需要进行排队);address 表示当前连接客户端的本地地址,该参数为输出参数,是客户端传递过来的关于自身的信息;address_len 表示当前连接客户端本地地址的字节长度,这个参数既是输入参数,又是输出参数。实现监听、接受和处理的代码如下:
#define MAX_CONNECTION_NUMBER 10
int server_client_length, server_client_sockfd;
struct sockaddr_un server_client_address;
listen(server_sockfd, MAX_CONNECTION_NUMBER);
while(1)
{
    // ...... (some process code)
    server_client_length = sizeof(server_client_address);
    server_client_sockfd = accept(server_sockfd, (struct sockaddr*)server_client_address, &server_client_length);
    // ...... (some process code)
}
这里使用死循环的原因是服务器是一个不断提供服务的实体,它需要不间断的进行监听、接受并处理连接,本例中,每个连接只能进行串行处理,即一个连接处理完后,才能进行后续连接的处理。如果想要多个连接并发处理,则需要创建线程,将每个连接交给相应的线程并发处理。
客户端套接字创建完毕并赋予本地地址值后,需要连接到服务器端进行通信,让服务器端为其提供处理服务。对于 SOCK_STREAM 类型的流式套接字,需要客户端与服务器之间进行连接方可使用。连接要使用 connect 系统调用,其原形为


int connect(int socket, const struct sockaddr *address, size_t address_len);


其中socket为客户端的套接字描述符,address表示当前客户端的本地地址,是一个 struct sockaddr_un 类型的变量,address_len 表示本地地址的字节长度。实现连接的代码如下:
connect(client_sockfd, (struct sockaddr*)&client_address, sizeof(client_address));
无论客户端还是服务器,都要和对方进行数据上的交互,这种交互也正是我们进程通信的主题。一个进程扮演客户端的角色,另外一个进程扮演服务器的角色,两个进程之间相互发送接收数据,这就是基于本地套接字的进程通信。发送和接收数据要使用 write read 系统调用,它们的原形为:
int read(int socket, char *buffer, size_t len);
int write(int socket, char *buffer, size_t len);
其中 socket 为套接字描述符;len 为需要发送或需要接收的数据长度;对于 read 系统调用,buffer 是用来存放接收数据的缓冲区,即接收来的数据存入其中,是一个输出参数;对于 write 系统调用,buffer 用来存放需要发送出去的数据,即 buffer 内的数据被发送出去,是一个输入参数;返回值为已经发送或接收的数据长度。例如客户端要发送一个 "Hello" 字符串给服务器,则代码如下:
char buffer[10] = "Hello";
write(client_sockfd, buffer, strlen(buffer));
交互完成后,需要将连接断开以节省资源,使用close系统调用,其原形为:
int close(int socket);
不多说了,直接使用,大家一定都会,呵呵!
上面所述的每个系统调用都有 -1 返回值,在调用不成功时,它们均会返回 -1,这个特性可以使得我们用 if - else 或异常处理语句来处理错误,为我们提供了很大的方便。
SOCK_DGRAM
数据报式本地套接字的应用场合很少,因为流式套接字在本地的连接时间可以忽略,所以效率并没有提高,而且发送接收都需要携带对方的本地地址,因此很少甚至几乎不使用。
与本地套接字相对应的是网络套接字,可以用于在网络上传送数据,换言之,可实现不同机器上的进程通信过程。在 TCP/IP 协议中,IP 地址的首字节为 127 即代表本地,因此本地套接字通信可以使用 IP 地址为 127.x.x.x 的网络套接字来实现。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/383357.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

extern和static的区别

c语言中的 static&#xff1a; 修饰局部变量&#xff1a;存放在静态数据区&#xff0c;生命周期位整个程序结束&#xff0c;但作用于仍为函数局部。 修饰全局变量&#xff1a;无法被同一工程其他源文件访问。 修饰函数&#xff1a;与全局变量类似。 extern&#xff1a; 可被…

RT5350原厂SDK及AP移植步骤详解

最近想搞一下rt5350&#xff0c;所以找了个原厂的SDK包进行了编译&#xff0c;很快路由器就可以用了&#xff0c;把我的编译操作步骤写了下分享给更多的爱好者&#xff0c;供大家参靠&#xff0c;下一步准备移植摄像头玩玩。有兴趣的可以一起交流。 RT5350移植Toolchain工具的安…

linux系统编程之进程概念(操作系统---管理,进程创建,进程状态,进程优先级, 环境变量,程序地址空间,进程O(1)调度方法)

系统编程&#xff1a; 进程概念->进程控制->基础IO->进程间通信->进程信号->多线程进程概念 冯诺依曼体系结构----现代计算机硬件体系结构 冯诺依曼体系结构----现代计算机硬件体系结构 计算机五大硬件单元&#xff1a;输入设备&#xff1a;键盘输出设备&#…

Make Menuconfig详解 (配置内核选择)

Make Menuconfig简介 make menuconfig 图形化的内核配置make mrproper -----删除不必要的文件和目录. #make config&#xff08;基于文本的最为传统的配置界面&#xff0c;不推荐使用&#xff09; #make menuconfig&#xff08;基于文本选单的配置界面&#xff0c;字符终端下…

Linux系统编程之进程控制(进程创建,fork函数,进程中止,进程等待,程序替换)

进程创建 fork()------复制&#xff0c;返回值&#xff0c;写时复制 vfork()创建子进程—子进程与父进程共用同一块虚拟地址空间&#xff0c; 为了防止调用栈混乱&#xff0c;因此阻塞父进程直到子进程调用exit&#xff08;&#xff09;退出或者进行程序替换 vfork创建的子…

Linux内核配置系统浅析

随着 Linux 操作系统的广泛应用&#xff0c;特别是 Linux 在嵌入式领域的发展&#xff0c;越来越多的人开始投身到 Linux 内核级的开发中。面对日益庞大的 Linux 内核源代码&#xff0c;开发者在完成自己的内核代码后&#xff0c;都将面临着同样的问题&#xff0c;即如何将源代…

Linux系统编程下做一个简易的shell

自主实现一个shell--------minshell shell&#xff1a;命令行解释器-------解释执行用户的输入&#xff08;完成相对应的功能&#xff09; 步骤 1. 获取标准输入中的字符串 2. 对字符串进行解析[ls -l -a][ls ] [-l ] [-a] 3. 创建子进程 4. 子进程中进行程序替换 5. 父进程…

C++起始(内联函数,宏的优缺点,const关键字,auto关键字(C++11)基于范围的for循环(C++11). 指针空值nullptr(C++11))

内联函数 概念 以inline修饰的函数叫做内联函数&#xff0c;编译时C编译器会在调用内联函数的地方展开&#xff0c;没有函数压栈的开销&#xff0c; 内联函数提升程序运行的效率 函数前增加inline关键字将其改成内联函数&#xff0c;在编译期间编译器会用函数体替换函数的调用…

linux内核中的汇编语言

在Linux内核代码中&#xff0c;有一部分是用汇编语言编写的。其大部分是关于中断与异常处理的底层程序&#xff0c;还有就是与初始化有关的程序&#xff0c;以及一些核心代码中调用的公用子程序。 用汇编语言编写内核代码中的部分代码&#xff0c;大体上是出于如下几个方面考虑…

数据结构课程设计---c语言实现通讯录(动态扩容+文件存储)

1 题目一 &#xff1a; 通讯录 1.1问题描述 编写一个通讯录管理系统&#xff0c;以把所学数据结构知识应用到实际软件开发中去。每条信息至包含 &#xff1a;姓名&#xff08;NAME &#xff09;街道&#xff08;STREET&#xff09;城市&#xff08;CITY&#xff09;邮编&#…

linux内核panic

1. Linux Kernel Panic的产生的原因 panic是英文中是惊慌的意思&#xff0c;Linux Kernel panic正如其名&#xff0c;linux kernel不知道如何走了&#xff0c;它会尽可能把它此时能获取的全部信息都打印出来。 有两种主要类型kernel panic&#xff0c;后面会对这两类panic做详细…

数据结构课程设计------c实现散列表(二次探测再哈希)电话簿(文件存储)

题目二 &#xff1a;散列表的设计与实现 2.1问题描述 设计散列表实现电话号码查找系统&#xff0c;使得平均查找长度不超过2基本要求 &#xff08;1&#xff09;设每个记录有下列数据项&#xff1a;电话号码、用户名、地址&#xff1b; &#xff08;2&#xff09;从键盘输入各…

科技论文----论搜索引擎现状及发展趋势

搜索引擎现状及发展趋势 【摘要】 随着最近10年中国互联网的快速发展菜互联网已经彻底改变了人们的生活方式&#xff0c;而在互联网的发展过程中。搜索引擎发挥了巨大的推动作用。本文对搜索引擎的发展历史采用的技术&#xff0c;发展现状出现的问题以及未来发展方向进行了综述…

inittab文件格式

/etc/inittab文件是Linux系统第一个进程init的配置文件。其每个记录占一行&#xff0c;每行最多512个字符。该文件的每个记录的格式为&#xff1a; id:runlevel:action:process 其中&#xff0c;id是一个不超过4个字符的标识&#xff0c;用来唯一标识一条记录。runlevel表明该条…

数据结构课程设计------扫雷游戏(升级版,可展开)

本程序由团队中的一个人所写&#xff0c;本人看懂并写下此文章 题目&#xff1a;扫雷 3.1问题描述 扫雷游戏 [基本要求] &#xff08;1&#xff09;完成棋盘的初始化并在标准显示器中显示 &#xff08;2&#xff09;通过输入行列值确定用户输入 &#xff08;3&#xff09;游…

C语言的编译链接过程的介绍

发布时间: 2012-11-08 10:17 作者: 未知 来源: 51Testing软件测试网采编 字体: 小 中 大 | 上一篇 下一篇 | 打印 | 我要投稿 | 推荐标签&#xff1a; DotNet 软件开发 | 感言十年 C语言的编译链接过程要把我们编写的一个c程序&#xff08;源代码&#x…

vs2013链接Mysql时出现 (由于找不到libmysql.dll,无法继续执行代码。重新安装程序可能会解决此问题)

将MySQL安装目录下的lib文件夹中 的libmysql.dll文件拷贝到C:\Windows\System32目录下即可

gcc 优化选项 -O1 -O2 -O3 -Os 优先级,-fomit-frame-pointer

少优化->多优化&#xff1a; O0 -->> O1 -->> O2 -->> O3 -O0表示没有优化,-O1为缺省值&#xff0c;-O3优化级别最高 英文解析&#xff1a; -O -O1 Optimize. Optimizing compilation takes somewhat more time, an…

const 和 #define 区别总结

const有类型&#xff0c;可进行编译器安全检查&#xff0c;#define 无类型&#xff0c;不可进行类型检查const 有作用域&#xff0c;而#define 不重视作用域&#xff0c;默认定义在指定作用域下有效的常量&#xff0c;那么#define 就不能用&#xff08;可以用#undef结束宏定义生…

Eclipse : Unresolved inclusion

Eclipse 中新建C 或C 到项目时&#xff0c;头文件报警&#xff0c;显示“Unresolved inclusion:<stdio.h>” 虽然不影响项目到编译和运行&#xff0c;确也无法查看头文件&#xff0c;让人感觉实在不爽。下面是在国外到网站上看到解决方案&#xff0c;自己整理了一下拿来分…