本地进程间通信——Unix域套接字

本地进程间通信机制

管道

对于本地进程间通信,普通管道提供了相关进程(都有相同的祖先)之间通信的一种方法,但是普通管道存在两种局限性:1)它只能在“相关进程间”(即父子进程)使用; 2)它是半双工的(半双工也就是收发不能同时进行)。

命名管道解决了第一个局限性,他能在不相关的进程之间进行数据通信,但是同样具有第二个局限性,即通常他们是半双工的。

域套接字

消息队列和unix域套接字没有上面所说的局限性的同性方法。

使用套接字除了可以实现网络间不同主机间的通信外,还可以实现同一主机的不同进程间的通信,且建立的通信是双向的通信。这里所指的使用套接字实现进程间通信,是将通信域指定为PF_UNIX来实现的;该函数的形式如下:

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

socket函数中的domain参数用于指定通信域,domain参数取PF_UNIX时,表示创建UNIX域的套接字。使用PF_UNIX域的套接字可以实现同一机器上的不同进程间的通信。

调用bind函数实现了套接字与地址(这里是文件名)的绑定。bind函数的具体信息如下:

int bind(int sockfd, const struct sockaddr *my_addr, socklen_t addrlen);    其参数my_addr为指向结构体sockaddr_un的指针,该结构体的定义如下:

#define UNIX_PATH_MAX 255
struct sockaddr_un {
    sa_family_t sun_family;    /*PF_UNIX或AF_UNIX */
    char sun_path[UNIX_PATH_MAX];    /* 路径名 */
};
在该结构体中,sun_family为AF_UNIX。sun_path是套接字在文件系统中的路径名。

 Unix域套接字是通过套接字API实现的简单的协议族。实际上它并不代表一个网络协议;它只能连接到同一台机器上的套接字。它提供了灵活的IPC机制。它的地址是它所在的文件系统的路径名,创建之后套接字就和路径名绑定在一起。用来表示Unix域地址的套接字文件能够使用stat()但是不能通过open()打开,而且应该使用套接字API对它进行操作。
    Unix域套接字是面向连接的,每个套接字的连接都建立了一个新的通讯信道。服务器可能同时处理许多连接,但对于每个连接都有不同的文件描述符。这个属性使Unix域套接字能够比命名管道更好的适应IPC任务。

 

域套接字实例

在一个终端运行服务器,然后在另一个终端(在相同目录下)运行客户端。当从客户端输入一行时,数据将通过套接字送到服务器;当退出客户端,服务器将等待另外一个连接。

还可以通过客户端程序的重定向输入来传送文件,cat uclient.c | ./uclient  或  ./uclient < uclient.c。

 

socket 服务器、客户端进程间通信—使用库函数

1、编写功能函数:

编写copyData 函数(从from获取输入,打印输出到to 上),并制作一个动态库文件,供其他模块调用;

/* sockutil.c - Utility functions used in socket example programs */
#include <stdio.h>
#include <string.h>
#include <unistd.h>

#include "test_common.h"

/* issue an error message via perror() and terminate the program */
void PrintfError(char * message)
{
    perror(message);
    // exit(1);
}

/* 
   Copies data from file descriptor 'from' to file descriptor
   'to' until nothing is left to be copied. Exits if an error
   occurs. This assumes both from and to are set for blocking
   reads and writes. 
*/
void copyData(int from, int to)
{
    char buf[1024];
    int amount;
   
    while ((amount = read(from, buf, sizeof(buf))) > 0) {
        if (write(to, buf, amount) != amount) {
            PrintfError("write");
            return;
        }
    }

    if (amount < 0) {
        PrintfError("read");
	}
}

 test_common.h 的头文件:

[root@llz lib_so]# cat test_common.h 
#ifndef TEST_COMMON_H
#define TEST_COMMON_H

void PrintfError(char * message);
void copyData(int from, int to);

#endif

将功能函数生成动态库文件:

将test_common.c编译为动态库,编译命令:

   

【如上制作动态库文件的命令,不正确,虽然也制作了库文件,但是最后不能使用】

【如下:是正确的命令】(-c 表示生成可执行文件,-fPIC 表示生成的库文件与路径无关)

至此 libtest_common.so 动态库便生成了

 

2、实现socket 服务器、客户端

一个终端运行服务器,然后在另一个终端(在相同目录下)运行客户端。当从客户端输入一行时,数据将通过套接字送到服务器。当退出客户端,服务器将等待另外一个连接。

还可以通过客户端程序的重定向输入来传送文件,cat uclient.c | ./uclient 或 ./uclient < uclient.c。

2.1、编写功能函数:socket 服务器

zll@zll-Lenovo:~/Pro_Soft$ cat 06_linux_uinx_server.c
/* userver.c - Simple Unix Domain Socket server */

/* Waits for a connection on the ./sample-socket Unix domain
   socket. Once a connection has been established, copy data
   from the socket to stdout until the other end closes the
   connection, and then wait for another connection to the
   socket.
*/

#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>

#include "test_common.h"          /* some public functions */

int main(void) {
    struct sockaddr_un address;	// #include <sys/un.h>
    int sock, conn;
    socklen_t addrLength;

	// AF 表示ADDRESS FAMILY 地址族 
	// PF 表示PROTOCL FAMILY 协议族
    if ((sock = socket(PF_UNIX, SOCK_STREAM, 0)) < 0) {
        PrintfError("socket");
	}

    /* Remove any preexisting socket (or other file) */
    unlink("./sample-socket");

    address.sun_family = AF_UNIX;       /* Unix domain socket */
    strcpy(address.sun_path, "./sample-socket");

    /* The total length of the address includes the sun_family element */
    addrLength = sizeof(address.sun_family) + strlen(address.sun_path);

    if (bind(sock, (struct sockaddr *)&address, addrLength)) {
        PrintfError("bind");
	}

    if (listen(sock, 5)) {
        PrintfError("listen");
	}

	/// 如何监听多个客户端的链接???
    while ((conn = accept(sock, (struct sockaddr *)&address, &addrLength)) >= 0) {
        printf("---- getting data, conn:%d\n", conn);
        copyData(conn, 1);
        printf("---- done\n");
        close(conn);
    }

    if (conn < 0) {
        PrintfError("accept");
	}
   
    close(sock);
    return 0;
}
zll@zll-Lenovo:~/Pro_Soft$ 

编译服务器程序:

注意:编译时需要指定编译路径(-L),以及需要的库名称(-l)。

如上所示:没有找到 lib,gcc 编译命令不正确:

正确的是:gcc  xxx.c  -o  xxx  -L  .  -l  动态库名称

如下图:提示PrintfError、copyData 没有定义,即 libtest_common.so 库文件制作的不对

重新制作动态库文件后,服务器可执行文件编译OK 了。

 

2.2、编写功能函数:socket 客户端

zll@zll-Lenovo:~/Pro_Soft$ cat 06_linux_uinx_client.c
/* uclient.c - Simple Unix Domain Socket client */

/* 
	Connect to the ./sample-socket Unix domain socket, copy stdin
	into the socket, and then exit.
*/

#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>

#include "test_common.h" /* some public functions */

int main(void)
{
    struct sockaddr_un address;
    int sock;
    size_t addrLength;

    if ((sock = socket(PF_UNIX, SOCK_STREAM, 0)) < 0) {
        PrintfError("socket");
	}

    address.sun_family = AF_UNIX;	/* Unix domain socket */
    strcpy(address.sun_path, "./sample-socket");

    /* The total length of the address includes the sun_family element */
    addrLength = sizeof(address.sun_family) + strlen(address.sun_path);

    if (connect(sock, (struct sockaddr *) &address, addrLength)) {
        PrintfError("connect");
	}
	// 0 ==> std_input
    copyData(0, sock);
    close(sock);
    return 0;
}
zll@zll-Lenovo:~/Pro_Soft$ 

编译客户端程序:

2.3、socket 服务器、客户端执行结果:

客户端重定向发送数据给服务器: cat test_common.h | ./06_linux_uinx_client

 

不能同时启动多个客户端,待进一步优化。

 

 

  • 3
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是一个简单的使用本地套接字实现进程间通信的代码样例: **server.py** ```python import socket # 创建套接字对象 server_socket = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) # 定义套接字文件路径 socket_file = "/tmp/my_socket" # 绑定套接字文件路径 server_socket.bind(socket_file) # 监听连接请求 server_socket.listen() while True: # 接受连接请求 client_socket, client_address = server_socket.accept() print(f"Connection from {client_address}") # 接收消息 message = client_socket.recv(1024) print(f"Received message: {message.decode()}") # 发送响应 response = "Hello from server" client_socket.send(response.encode()) # 关闭连接 client_socket.close() ``` **client.py** ```python import socket # 创建套接字对象 client_socket = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) # 定义套接字文件路径 socket_file = "/tmp/my_socket" # 连接服务器 client_socket.connect(socket_file) # 发送消息 message = "Hello from client" client_socket.send(message.encode()) # 接收响应 response = client_socket.recv(1024) print(f"Received response: {response.decode()}") # 关闭连接 client_socket.close() ``` 在上述代码中,server.py 作为服务器端,首先创建一个套接字对象,并指定地址族为 UNIX 套接字,类型为 SOCK_STREAM。然后,将套接字文件路径绑定到该套接字上,并开始监听连接请求。当有客户端连接请求时,接受连接请求,并接收客户端发送的消息。接收到消息后,向客户端发送响应,然后关闭连接。 而 client.py 作为客户端,同样创建一个套接字对象,并指定地址族为 UNIX 套接字,类型为 SOCK_STREAM。然后,连接服务器,并发送消息。接收到服务器的响应后,打印响应内容,并关闭连接。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值