socket本地多进程通信基本使用方法和示例

目录

前言:

socket是什么

 socket基本原理框图

socket基本函数

1 socket() 函数

2 bind()函数

3 connect()函数

4 listen() 函数

5 accept() 函数

6 read() write() send() recv()函数

7 close()函数

8 字节序转换(hton)

示例代码


前言:

进程间通信(IPC,Interprocess communication)是一组编程接口,让程序员能够协调不同的进程,使之能在一个操作系统里同时运行,并相互传递、交换信息。这使得一个程序能够在同一时间里处理许多用户的要求。因为即使只有一个用户发出要求,也可能导致一个操作系统中多个进程的运行,进程之间必须互相通话。IPC接口就提供了这种可能性。每个IPC方法均有它自己的优点和局限性,一般,对于单个程序而言使用所有的IPC方法是不常见的。

进程间通信的8种方法:

1、无名管道通信

无名管道( pipe ):管道是一种半双工的通信方式,数据只能单向流动,而且只能在具有亲缘关系的进程间使用。进程的亲缘关系通常是指父子进程关系。

2、高级管道通信

高级管道(popen):将另一个程序当做一个新的进程在当前程序进程中启动,则它算是当前程序的子进程,这种方式我们成为高级管道方式。

3、有名管道通信

有名管道 (named pipe) : 有名管道也是半双工的通信方式,但是它允许无亲缘关系进程间的通信。

4、消息队列通信

消息队列( message queue ) : 消息队列是由消息的链表,存放在内核中并由消息队列标识符标识。消息队列克服了信号传递信息少、管道只能承载无格式字节流以及缓冲区大小受限等缺点。

5、信号量通信

信号量( semophore ) : 信号量是一个计数器,可以用来控制多个进程对共享资源的访问。它常作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源。因此,主要作为进程间以及同一进程内不同线程之间的同步手段。

6、信号

信号 ( sinal ) : 信号是一种比较复杂的通信方式,用于通知接收进程某个事件已经发生。

7、共享内存通信

共享内存( shared memory ) :共享内存就是映射一段能被其他进程所访问的内存,这段共享内存由一个进程创建,但多个进程都可以访问。共享内存是最快的 IPC 方式,它是针对其他进程间通信方式运行效率低而专门设计的。它往往与其他通信机制,如信号两,配合使用,来实现进程间的同步和通信。

8、套接字通信

套接字( socket ) : 套接口也是一种进程间通信机制,与其他通信机制不同的是,它可用于不同机器间的进程通信。

socket是什么

你也许听很多高手说过,UNIX/Linux 中的一切都是文件!那个家伙说的没错。
为了表示和区分已经打开的文件,UNIX/Linux 会给每个文件分配一个 ID,这个 ID 就是一个整数,被称为文件描述符(File Descriptor)。例如:

  • 通常用 0 来表示标准输入文件(stdin),它对应的硬件设备就是键盘;
  • 通常用 1 来表示标准输出文件(stdout),它对应的硬件设备就是显示器。

UNIX/Linux 程序在执行任何形式的 I/O 操作时,都是在读取或者写入一个文件描述符。一个文件描述符只是一个和打开的文件相关联的整数,它的背后可能是一个硬盘上的普通文件、FIFO、管道、终端、键盘、显示器,甚至是一个网络连接。

请注意,网络连接也是一个文件,它也有文件描述符!你必须理解这句话。

我们可以通过 socket() 函数来创建一个网络连接,或者说打开一个网络文件,socket() 的返回值就是文件描述符。有了文件描述符,我们就可以使用普通的文件操作函数来传输数据了,例如:

  • 用 read() 读取从远程计算机传来的数据;
  • 用 write() 向远程计算机写入数据。


你看,只要用 socket() 创建了连接,剩下的就是文件操作了,socket通信原来就是如此简单!


 
socket基本原理框图

1.服务端首先初始化Socket(),然后和接口进行绑定bind()和监听listen(),然后调用accept()进行阻塞。
2.客户端初始化socket(),然后调用connect()与服务端进行连接,然后客户端负责发送请求,服务端接收并且处理请求。write(),read().在 UNIX/Linux 系统中,为了统一对各种硬件的操作,简化接口,不同的硬件设备也都被看成一个文件。对这些文件的操作,等同于对磁盘上普通文件的操作。

socket基本函数

1 socket() 函数

#include <sys/types.h>
#include <sys/socket.h>
int socket (int af, int type, int protocal );

1) af 为地址族(Address Family),也就是 IP 地址类型,常用的有 AF_INET 和 AF_INET6。AF 是“Address Family”的简写,INET是“Inetnet”的简写。AF_INET 表示 IPv4 地址,例如 127.0.0.1;AF_INET6 表示 IPv6 地址,例如 1030::C9B4:FF12:48AA:1A2B。

大家需要记住127.0.0.1,它是一个特殊IP地址,表示本机地址。

2) type 为数据传输方式/套接字类型,常用的有 SOCK_STREAM(流格式套接字/面向连接的套接字) 和 SOCK_DGRAM(数据报套接字/无连接的套接字)

3) protocol 表示传输协议,常用的有 IPPROTO_TCP 和 IPPTOTO_UDP,分别表示 TCP 传输协议和 UDP 传输协议。

2 bind()函数

socket()函数用来创建套接字,确定套接字的各种属性,然后服务器端要用 bind() 函数将套接字与特定的 IP 地址和端口绑定起来,只有这样,流经该 IP 地址和端口的数据才能交给套接字处理。类似地,客户端也要用 connect() 函数建立连接。

#include <sys/types.h>
#include <sys/socket.h>
int bind (int sockfd, const struct sockaddr* my_addr, socklen_t addlen );
//前边的专用地址的指针强制转换成 struct sockaddr* 就行

1)sock 为 socket 文件描述符,

2)addr 为 sockaddr 结构体变量的指针,

3)addrlen 为 addr 变量的大小,可由 sizeof() 计算得出。

下面的代码,将创建的套接字与IP地址 127.0.0.1、端口 1234 绑定:

    //创建套接字
    int serv_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    //创建sockaddr_in结构体变量
    struct sockaddr_in serv_addr;
    memset(&serv_addr, 0, sizeof(serv_addr));  //每个字节都用0填充
    serv_addr.sin_family = AF_INET;  //使用IPv4地址
    serv_addr.sin_addr.s_addr = inet_addr("127.0.0.1");  //具体的IP地址
    serv_addr.sin_port = htons(1234);  //端口
    //将套接字和IP、端口绑定
    bind(serv_sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr));

这里我们使用 sockaddr_in 结构体,然后再强制转换为 sockaddr 类型,后边会讲解为什么这样做。

sockaddr_in 结构体

接下来不妨先看一下 sockaddr_in 结构体,它的成员变量如下:

    struct sockaddr_in{
        sa_family_t     sin_family;   //地址族(Address Family),也就是地址类型
        uint16_t        sin_port;     //16位的端口号
        struct in_addr  sin_addr;     //32位IP地址
        char            sin_zero[8];  //不使用,一般用0填充
    };

1) sin_family 和 socket() 的第一个参数的含义相同,取值也要保持一致。

2) sin_prot 为端口号。uint16_t 的长度为两个字节,理论上端口号的取值范围为 0~65536,但 0~1023 的端口一般由系统分配给特定的服务程序,例如 Web 服务的端口号为 80,FTP 服务的端口号为 21,所以我们的程序要尽量在 1024~65536 之间分配端口号。

端口号需要用 htons() 函数转换,后面会讲解为什么。

3) sin_addr 是 struct in_addr 结构体类型的变量,下面会详细讲解。

4) sin_zero[8] 是多余的8个字节,没有用,一般使用 memset() 函数填充为 0。上面的代码中,先用 memset() 将结构体的全部字节填充为 0,再给前3个成员赋值,剩下的 sin_zero 自然就是 0 了。

in_addr 结构体

sockaddr_in 的第3个成员是 in_addr 类型的结构体,该结构体只包含一个成员,如下所示:

    struct in_addr{
        in_addr_t  s_addr;  //32位的IP地址
    };

in_addr_t 在头文件 <netinet/in.h> 中定义,等价于 unsigned long,长度为4个字节。也就是说,s_addr 是一个整数,而IP地址是一个字符串,所以需要 inet_addr() 函数进行转换,例如:

    unsigned long ip = inet_addr("127.0.0.1");
    printf("%ld\n", ip);

运行结果:
16777343

为什么要搞这么复杂,结构体中嵌套结构体,而不用 sockaddr_in 的一个成员变量来指明IP地址呢?socket() 函数的第一个参数已经指明了地址类型,为什么在 sockaddr_in 结构体中还要再说明一次呢,这不是啰嗦吗?

这些繁琐的细节确实给初学者带来了一定的障碍,我想,这或许是历史原因吧,后面的接口总要兼容前面的代码。各位读者一定要有耐心,暂时不理解没有关系,根据教程中的代码“照猫画虎”即可,时间久了自然会接受。

为什么使用 sockaddr_in 而不使用 sockaddr

bind() 第二个参数的类型为 sockaddr,而代码中却使用 sockaddr_in,然后再强制转换为 sockaddr,这是为什么呢?

sockaddr 结构体的定义如下:

    struct sockaddr{
        sa_family_t  sin_family;   //地址族(Address Family),也就是地址类型
        char         sa_data[14];  //IP地址和端口号
    };

下图是 sockaddr 与 sockaddr_in 的对比(括号中的数字表示所占用的字节数):

 sockaddr 和 sockaddr_in 的长度相同,都是16字节,只是将IP地址和端口号合并到一起,用一个成员 sa_data 表示。要想给 sa_data 赋值,必须同时指明IP地址和端口号,例如”127.0.0.1:80“,遗憾的是,没有相关函数将这个字符串转换成需要的形式,也就很难给 sockaddr 类型的变量赋值,所以使用 sockaddr_in 来代替。这两个结构体的长度相同,强制转换类型时不会丢失字节,也没有多余的字节。

可以认为,sockaddr 是一种通用的结构体,可以用来保存多种类型的IP地址和端口号,而 sockaddr_in 是专门用来保存 IPv4 地址的结构体。另外还有 sockaddr_in6,用来保存 IPv6 地址,它的定义如下:

    struct sockaddr_in6 { 
        sa_family_t sin6_family;  //(2)地址类型,取值为AF_INET6
        in_port_t sin6_port;  //(2)16位端口号
        uint32_t sin6_flowinfo;  //(4)IPv6流信息
        struct in6_addr sin6_addr;  //(4)具体的IPv6地址
        uint32_t sin6_scope_id;  //(4)接口范围ID
    };

正是由于通用结构体 sockaddr 使用不便,才针对不同的地址类型定义了不同的结构体。

3 connect()函数

#include <sys/types.h>
#include <sys/socket.h>
int connect(int sockfd, const struct sockaddr *serv_addr, socklen_t addrlen ); //成功返回服务端的文件描述符

各个参数的说明和 bind() 相同,不再赘述。

4 listen() 函数

#include <sys/socket.h>
int listen( int sockfd, int backlog ); //backlog是指完全连接(ESTABLISHED)的客户端的上限

对于服务器端程序,使用 bind() 绑定套接字后,还需要使用 listen() 函数让套接字进入被动监听状态,再调用 accept() 函数,就可以随时响应客户端的请求了。

sock 为需要进入监听状态的套接字,backlog 为请求队列的最大长度。

所谓被动监听,是指当没有客户端请求时,套接字处于“睡眠”状态,只有当接收到客户端请求时,套接字才会被“唤醒”来响应请求。

请求队列

当套接字正在处理客户端请求时,如果有新的请求进来,套接字是没法处理的,只能把它放进缓冲区,待当前请求处理完毕后,再从缓冲区中读取出来处理。如果不断有新的请求进来,它们就按照先后顺序在缓冲区中排队,直到缓冲区满。这个缓冲区,就称为请求队列(Request Queue)。

缓冲区的长度(能存放多少个客户端请求)可以通过 listen() 函数的 backlog 参数指定,但究竟为多少并没有什么标准,可以根据你的需求来定,并发量小的话可以是10或者20。

如果将 backlog 的值设置为 SOMAXCONN,就由系统来决定请求队列长度,这个值一般比较大,可能是几百,或者更多。

当请求队列满时,就不再接收新的请求,对于 Linux,客户端会收到 ECONNREFUSED 错误,对于 Windows,客户端会收到 WSAECONNREFUSED 错误。

注意:listen() 只是让套接字处于监听状态,并没有接收请求。接收请求需要使用 accept() 函数

5 accept() 函数

#include <sys/types.h>
#include <sys/socket.h>
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen ); //成功返回客户端的文件描述符

当套接字处于监听状态时,可以通过 accept() 函数来接收客户端请求

它的参数与 listen() 和 connect() 是相同的:sock 为服务器端套接字,addr 为 sockaddr_in 结构体变量,addrlen 为参数 addr 的长度,可由 sizeof() 求得。
accept() 返回一个新的套接字来和客户端通信,addr 保存了客户端的IP地址和端口号,而 sock 是服务器端的套接字,大家注意区分。后面和客户端通信时,要使用这个新生成的套接字,而不是原来服务器端的套接字。
最后需要说明的是:listen() 只是让套接字进入监听状态,并没有真正接收客户端请求,listen() 后面的代码会继续执行,直到遇到 accept()。accept() 会阻塞程序执行(后面代码不能被执行),直到有新的请求到来。

6 read() write() send() recv()函数

write() read() 通用读写
ssize_t write(int fd, const void *buf, size_t nbytes);
ssize_t read(int fd, void *buf, size_t nbytes);

前面我们说过,两台计算机之间的通信相当于两个套接字之间的通信,在服务器端用 write() 向套接字写入数据,客户端就能收到,然后再使用 read() 从套接字中读取出来,就完成了一次通信。

除此之外还有其他方法进行读写套接字如下:


TCP数据读写
#include <sys/types.h>
#include <sys/socket.h>
ssize_t recv(int sockfd, void *buf, size_t len, int flags );
ssize_t send(int sockfd, void *buf, size_t len, int flags );

UDP读写
#include <sys/types.h>
#include <sys/socket.h>
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addlen);
ssize_t sendto(int sockfd, void *buf, size_t len, int flags, struct sockaddr *dest_addr, socklen_t *addlen);

socket通用读写
#include <sys/socket.h>
ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags );
ssize_t sendmsg(int sockfd, struct msghdr *msg, int flags );
struct msghdr
{
    void *msg_name;			//socket地址
    socklen_t msg_namelen	//socket地址长度
    struct iovec *msg_iov;	//分散的内存块数组
    int msg_iovlen;			//内存块数量
    void *msg_control;		//指向辅助数据的起始位置
    socklen_t msg_controllen;//辅助数据长度
    int msg_flags;			//复制函数中的flags
};
struct iovec
{
  	void *iov_base;	//内存起始地址
    size_t iov_len;	//这块内存长度
};

7 close()函数

#include <unistd.h>
int close(int fd); //通用文件描述符关闭,将fd的引用计数减一

#include <sys/socket.h>
int shutdown(int sockfd, int howto ); //网络专用,howto取值SHUT_RD, SHUT_WR, SHUT_RDWR。

8 字节序转换(hton)

#include <netinet/in.h>
unsigned long int htonl(unsigned long int hostlong);
unsigned short int htons(unsigned short int hostshort);
unsigned long int ntohl(unsigned long int netlong);
unsigned short int ntohs(unsigned short int netshort);

htons是将整型变量从主机字节顺序转变成网络字节顺序, 就是整数在地址空间存储方式变为高位字节存放在内存的低地址处。
网络字节顺序是TCP/IP中规定好的一种数据表示格式,它与具体的CPU类型、操作系统等无关,从而可以保证数据在不同主机之间传输时能够被正确解释,网络字节顺序采用big-endian排序方式。
htons的功能:将一个无符号短整型的主机数值转换为网络字节顺序,即大尾顺序(big-endian)

htonl,其实是host to network, l 的意思是返回类型是long. 将主机数转换成无符号长整型的网络字节顺序。本函数将一个32位数从主机字节顺序转换成网络字节顺序。

示例代码

本示例是本地多进程间进行通信,完成进程1和进程2分别和service进程建立socket,进而使本地进程1和进程2进行通信

client1 代码

/*************************************************************************
	> file Name: client.c
	> author: C G
	> mail: - 
	> created Time: 2023年03月01日 星期三 18时19分46秒
	> version: 1.0
 ************************************************************************/
#include<stdio.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<sys/time.h>
#include<sys/ioctl.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/time.h>
#include<string.h>
#include"../common.h"


int init(void)
{
	int len = 0;
	int clientSockfd = 0;
	int clientLen= 0;
	int result = 0;
	struct sockaddr_in clientAddress;
	struct sockaddr_in serverAddress;

	clientSockfd = socket(AF_INET, SOCK_STREAM, 0);

	memset(&serverAddress, 0x00, sizeof(serverAddress));
	serverAddress.sin_family = AF_INET;
	serverAddress.sin_addr.s_addr = inet_addr(SERVICE_STRING_IP);
	serverAddress.sin_port = htons(SERVICE_PORT);
	len = sizeof(serverAddress);

	memset(&clientAddress, 0x00, sizeof(clientAddress));
	clientAddress.sin_family = AF_INET;
	clientAddress.sin_addr.s_addr = htonl(INADDR_ANY);
	clientAddress.sin_port = htons(BT_PORT_ID);
	clientLen = sizeof(clientAddress);

	bind(clientSockfd, (struct sockaddr*)&clientAddress, clientLen);

	result = connect(clientSockfd, (struct sockaddr *)&serverAddress, len);

	if(result == -1)
	{
		printf("connect result is error\n");
		exit(1);
	}

	return clientSockfd;
}


int main()
{
	int clientSockfd = 0;
	struct Data myData;

	struct timeval start;
	struct timeval end;

	clientSockfd = init();

	printf("i am is BT\n");

	memset(&myData, 0x00, sizeof(myData));
	myData.src = BT_PORT_ID;
	myData.der = HMI_PORT_ID;
	myData.evt = 1;

	write(clientSockfd, &myData, sizeof(myData));

	gettimeofday(&start,NULL);
	printf("BT write time is %ld.%06ld\n",start.tv_sec, start.tv_usec);
	printf("BT write src = %d , der = %d , evt = %d \n",myData.src, myData.der, myData.evt);


	memset(&myData, 0x00, sizeof(myData));
	read(clientSockfd, &myData, sizeof(myData));

	gettimeofday(&end,NULL);
	printf("BT read time is %ld.%06ld\n",end.tv_sec, end.tv_usec);
	printf("BT read src = %d , der = %d , evt = %d \n",myData.src, myData.der, myData.evt);


	close(clientSockfd);

	return 0;
}

client 2 代码

/*************************************************************************
	> file Name: client.c
	> author: C G
	> mail: - 
	> created Time: 2023年03月01日 星期三 18时19分46秒
	> version: 1.0
 ************************************************************************/
#include<stdio.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<sys/time.h>
#include<sys/ioctl.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/time.h>
#include<string.h>
#include"../common.h"


int init(void)
{
	int len = 0;
	int clientSockfd = 0;
	int clientLen= 0;
	int result = 0;
	struct sockaddr_in clientAddress;
	struct sockaddr_in serverAddress;

	clientSockfd = socket(AF_INET, SOCK_STREAM, 0);

	memset(&serverAddress, 0x00, sizeof(serverAddress));
	serverAddress.sin_family = AF_INET;
	serverAddress.sin_addr.s_addr = inet_addr(SERVICE_STRING_IP);
	serverAddress.sin_port = htons(SERVICE_PORT);
	len = sizeof(serverAddress);

	memset(&clientAddress, 0x00, sizeof(clientAddress));
	clientAddress.sin_family = AF_INET;
	clientAddress.sin_addr.s_addr = htonl(INADDR_ANY);
	clientAddress.sin_port = htons(HMI_PORT_ID);
	clientLen = sizeof(clientAddress);

	bind(clientSockfd, (struct sockaddr*)&clientAddress, clientLen);

	result = connect(clientSockfd, (struct sockaddr *)&serverAddress, len);

	if(result == -1)
	{
		printf("connect result is error\n");
		exit(1);
	}

	return clientSockfd;
}


int main()
{
	int clientSockfd = 0;
	struct Data myData;

	struct timeval start;
	struct timeval end;

	clientSockfd = init();
	printf("i am HMI\n");

	memset(&myData, 0x00, sizeof(myData));

	read(clientSockfd, &myData, sizeof(myData));
	gettimeofday(&end,NULL);
	printf("HMI read time is %ld.%06ld\n",end.tv_sec, end.tv_usec);
	printf("HMI read src = %d , der = %d , evt = %d \n",myData.src, myData.der, myData.evt);

	memset(&myData, 0x00, sizeof(myData));
	myData.src = HMI_PORT_ID;
	myData.der = BT_PORT_ID;
	myData.evt = 2;

	write(clientSockfd, &myData, sizeof(myData));

	gettimeofday(&start,NULL);
	printf("HMI write time is %ld.%06ld\n",start.tv_sec, start.tv_usec);
	printf("HMI write src = %d , der = %d , evt = %d \n",myData.src, myData.der, myData.evt);

	close(clientSockfd);

	return 0;

}

service 代码


/*************************************************************************
	> file Name: service.c
	> author: C G
	> mail: - 
	> created Time: 2023年03月01日 星期三 18时15分49秒
	> version: 1.0
 ************************************************************************/

extern "C"
{
#include<stdio.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<sys/time.h>
#include<sys/ioctl.h>
#include<unistd.h>
#include<stdlib.h>
#include<string.h>

#include"../common.h"
}


#include<map>

int main()
{
	std::map<int, int> myMap;

	int server_sockfd,client_sockfd;
	unsigned int server_len, client_len;
	struct sockaddr_in server_address;
	struct sockaddr_in client_address;
	int result;
	fd_set readfds, testfds;

	struct Data myData;
	memset(&myData, 0x00, sizeof(myData));

	server_sockfd = socket(AF_INET, SOCK_STREAM, 0);
	server_address.sin_family = AF_INET;
	server_address.sin_addr.s_addr = htonl(INADDR_ANY);
	server_address.sin_port = htons(SERVICE_PORT);
	server_len = sizeof(server_address);

	bind(server_sockfd, (struct sockaddr*)&server_address, server_len);
	listen(server_sockfd, LISTEN_MAX);

	FD_ZERO(&readfds);
	FD_SET(server_sockfd,&readfds);

	while(1)
	{
		char ch;
		int fd;
		int nread;
		ssize_t readSize = 0;
		
		FD_ZERO(&testfds);
		testfds = readfds;
		result = select(FD_SETSIZE, &testfds, (fd_set*)0, (fd_set*)0, NULL);
		if(result  < 1)
		{
			printf("is error\n");
			exit(1);
		}

		for(fd = 0; fd < FD_SETSIZE; fd++)
		{
			if(FD_ISSET(fd, &testfds))
			{
				if(fd == server_sockfd)
				{
					client_len = sizeof(client_address);
					client_sockfd = accept(server_sockfd, (struct sockaddr*)&client_address, &client_len);
					FD_SET(client_sockfd, &readfds);
					printf("adding client on fd %d  port %d ID:%s\n", client_sockfd, ntohs(client_address.sin_port), 
							HMI_PORT_ID==ntohs(client_address.sin_port)?"HMI_PORT_ID":BT_PORT_ID==ntohs(client_address.sin_port)?"BT_PORT_ID":"other_port_ID");
					myMap[ntohs(client_address.sin_port)] = client_sockfd;
				}
				else
				{
					readSize = read(fd, &myData, sizeof(myData));

					if(readSize > 0)
					{
						struct timeval start;
						gettimeofday(&start,NULL);
						printf("read time is %ld.%06ld\n",start.tv_sec, start.tv_usec);

						printf("read client on fd %d ID %s,src = %d , der = %d  => %d, evn = %d \n",fd,
								HMI_PORT_ID==myData.der?"HMI_PORT_ID":BT_PORT_ID==ntohs(client_address.sin_port)?"BT_PORT_ID":"other_port_ID",
								myData.src, myData.der, myMap[myData.der],myData.evt);

						write(myMap[myData.der], &myData, sizeof(myData));

						printf("write client on fd %d ID %s,src = %d , der = %d  => %d, evn = %d \n\n",fd,
								HMI_PORT_ID==myData.der?"HMI_PORT_ID":BT_PORT_ID==ntohs(client_address.sin_port)?"BT_PORT_ID":"other_port_ID",
								myData.src, myData.der, myMap[myData.der],myData.evt);
					}
					else
					{
						FD_CLR(fd, &readfds);
						printf("removing client on fd %d\n",fd);
						std::map<int,int>::iterator key = myMap.find(fd);
						if(key != myMap.end())
						{
							myMap.erase(key);
						}
						nread++;
						write(fd, &nread, 1);
					}
				}
			}
		}
	}

	return 0;
}

  • 8
    点赞
  • 38
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
要实现Linux多进程本地socket通信接口,可以使用以下步骤: 1. 创建一个socket文件描述符:使用`socket()`函数创建一个本地socket文件描述符,指定协议族为`AF_UNIX`。 2. 绑定socket地址:使用`bind()`函数将socket绑定到一个本地文件路径上。 3. 监听socket:如果需要让socket接受连接请求,可以使用`listen()`函数将socket设置为监听状态。 4. 接受连接请求:如果socket是处于监听状态,可以使用`accept()`函数接受连接请求,并返回一个新的socket文件描述符。 5. 发送和接收数据:使用`send()`和`recv()`函数向连接的另一端发送和接收数据。 下面是一个简单的示例代码: ```c #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <sys/socket.h> #include <sys/un.h> #define SOCK_PATH "/tmp/mysocket" int main(void) { int s, s2, len; socklen_t t; struct sockaddr_un local, remote; char str[100]; // 创建socket if ((s = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) { perror("socket"); exit(1); } // 绑定socket到本地文件路径 local.sun_family = AF_UNIX; strcpy(local.sun_path, SOCK_PATH); unlink(local.sun_path); len = strlen(local.sun_path) + sizeof(local.sun_family); if (bind(s, (struct sockaddr *)&local, len) == -1) { perror("bind"); exit(1); } // 监听socket,设置最大连接数为5 if (listen(s, 5) == -1) { perror("listen"); exit(1); } printf("Waiting for a connection...\n"); // 接受连接请求 t = sizeof(remote); if ((s2 = accept(s, (struct sockaddr *)&remote, &t)) == -1) { perror("accept"); exit(1); } printf("Connected.\n"); // 发送数据 if (send(s2, "Hello, world!", 13, 0) == -1) { perror("send"); exit(1); } // 接收数据 if ((len = recv(s2, str, 100, 0)) == -1) { perror("recv"); exit(1); } str[len] = '\0'; printf("Received: %s\n", str); // 关闭socket close(s2); close(s); return 0; } ``` 这个示例代码可以在终端中运行,将会监听本地文件路径`/tmp/mysocket`,等待连接请求,一旦有连接请求到来,就会发送一条消息,然后等待接收对方发送的消息。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值