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

news2024/11/15 11:53:52

目录

前言:

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;
}

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

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

相关文章

使用 Pulumi 打造自己的多云管理平台

前言在公有云技术与产品飞速发展的时代&#xff0c;业务对于其自身的可用性提出了越来越高的要求&#xff0c;当跨区域容灾已经无法满足业务需求的情况下&#xff0c;我们通常会考虑多云部署我们的业务平台&#xff0c;以规避更大规模的风险。但在多云平台部署的架构下&#xf…

埃安自研版图扩至夸克电驱,动力研发团队已超1000人

埃安的三电自研版图正在扩大。3月3日&#xff0c;广汽集团旗下埃安发布了一项名为“夸克电驱”的技术产品&#xff0c;相比主流电驱体积减少一倍&#xff0c;同时电机功率密度比主流电驱增加了一倍。此前&#xff0c;比亚迪刚刚发布易四方动力系统&#xff0c;特斯拉也在投资者…

HTML常见标签

文章目录一、HTML基础标签注释标签标题标签段落标签换行标签格式化标签图片、音频、视频标签超链接标签列表标签表格标签布局标签表单标签表单标签概述form标签属性表单项标签综合案例一、HTML基础标签 基础标签就是和文字相关的标签 标签描述<h1> ~ <h6>定义标题…

【项目管理】晋升为领导后,如何开展工作?

兵随将转&#xff0c;作为管理者&#xff0c;你可以不知道下属的短处&#xff0c;却不能不知道下属的长处。晋升为领导后&#xff0c;如何开展工作呢&#xff1f; 金九银十&#xff0c;此期间换工作的人不在少数。有几位朋友最近都换了公司&#xff0c;职位得到晋升&#xff0c…

前端——1.相关概念

这篇文章主要介绍前端入门的相关概念 1.网页 1.1什么是网页&#xff1f; 网站&#xff1a;是指在因特网上根据一定的规则&#xff0c;使用HTML等制作的用于展示特定内容相关的网页集合 网页&#xff1a;是网站中的一“页”&#xff0c;通常是HTML格式的文件&#xff0c;它要…

JAVA后端部署项目三步走

1. JAVA部署项目三步走 1.1 查看 运行的端口 lsof -i:8804 &#xff08;8804 为端口&#xff09; 发现端口25111被监听 1.2 杀死进程,终止程序 pid 为进程号 kill -9 pid 1.3 后台运行jar包 nohup java -jar -Xms128M -Xmx256M -XX:MetaspaceSize128M -XX:MaxM…

C++笔记之lambda表达式

引言 Lambda表达式是从C 11版本引入的特性&#xff0c;利用它可以很方便的定义匿名函数对象&#xff0c;通常作为回调函数来使用。大家会经常拿它和函数指针&#xff0c;函数符放在一起比较&#xff0c;很多场合下&#xff0c;它们三者都可以替换着用。 语法 [ captures ] (…

javaScript基础面试题 ---宏任务微任务

宏任务微任务一、为什么JS是单线程语言&#xff1f;二、JS是单线程&#xff0c;怎样执行异步代码&#xff1f;1、JS是单线程语言 2、JS代码执行流程&#xff0c;同步执行完&#xff0c;再进行事件循环&#xff08;微任务、宏任务&#xff09; 3、清空所有的微任务&#xff0c;再…

机器学习100天(四十):040 线性支持向量机-公式推导

《机器学习100天》完整目录:目录 机器学习 100 天,今天讲的是:线性支持向量机-公式推导! 首先来看这样一个问题,在二维平面上需要找到一条直线划分正类和负类。 我们找到了 A、B、C 三条直线。这三条直线都能正确分类所有训练样本。但是,哪条直线最好呢?直观上来看,我…

代码随想录算法训练营第六天|242.有效的字母异位词 、349. 两个数组的交集 、 202. 快乐数、1. 两数之和

当我们遇到了要快速判断一个元素是否出现集合里的时候&#xff0c;就要考虑哈希法。哈希法是牺牲了空间换取了时间&#xff0c;要使用额外的数组&#xff0c;set或者是map来存放数据&#xff0c;才能实现快速的查找。当我们要使用集合来解决哈希问题的时候&#xff0c;优先使用…

【SpringCloud】SpringCloud教程之Nacos实战(1)

目录Nacos是什么&#xff1f;一.Nacos下载二.安装Nacos三.Nacos原理四.Nacos快速入门五.Nacos服务多级存储模式六.Nacos根据集群设置负载均衡1.根据同集群优先访问2.根据权重配置负载均衡七.Nacos的环境隔离八.Nacos和Eureka的区别前提&#xff1a;以订单服务和用户服务为例&am…

【C++基础入门】数组、函数

一&#xff1a;数组 1.1 概述 所谓数组&#xff0c;就是一个集合&#xff0c;里面存放了相同类型的数据元素 特点1&#xff1a; 数组中的每个数据元素都是相同的数据类型 特点2&#xff1a; 数组是由连续的内存位置组成的 1.2 一维数组 1.2.1 一维数组定义方式 一维数组…

世界那么大,你哪都别去了,来我带你了解CSS3 (二)

文章目录‍❤️‍&#x1f525;CSS文档流‍❤️‍&#x1f525;CSS浮动‍❤️‍&#x1f525;CSS定位‍❤️‍&#x1f525;CSS媒体查询‍❤️‍&#x1f525;CSS文档流 文档流是文档中可显示对象在排列时所占用的位置/空间。 例如&#xff1a;块元素自上而下摆放&#xff0c;内…

第一章---Pytorch快速入门---第一节---张量及运算

目录 1.1张量的数据类型 1.2 张量的生成 1.3 张量操作 1.4 张量的计算 一、张量 在高等数学中&#xff0c;单独的一个数是标量&#xff0c;而有序排列的一组数字是一个向量&#xff08;例如一个数组&#xff09;&#xff0c;向量组可以构成矩阵。向量是一维的&#xff0c;而…

Java——腐烂的橘子

题目链接 leetcode在线oj题——腐烂的橘子 题目描述 在给定的 m x n 网格 grid 中&#xff0c;每个单元格可以有以下三个值之一&#xff1a; 值 0 代表空单元格&#xff1b;值 1 代表新鲜橘子&#xff1b;值 2 代表腐烂的橘子。 每分钟&#xff0c;腐烂的橘子 周围 4 个方…

Python数据分析案例19——上市银行财务指标对比

我代码栏目都是针对基础的python数据分析人群&#xff0c;比如想写个本科毕业论文&#xff0c;课程论文&#xff0c;做个简单的案例分析等。过去写的案例可能使用了过多的机器学习和深度学习方法&#xff0c;文科的同学看不懂&#xff0c;可能他们仅仅只想用python做个回归或者…

Method breakpoints may dramatically slow down debugging 解决方案

项目无法启动了 简单介绍一下事情的过程&#xff1a;昨天在进行代码调试的时候&#xff0c;代码部分处理完成之后&#xff0c;启动debug模式的热部署准备测试一下逻辑&#xff0c;结果左下角提示我热部署失败&#xff0c;需要重新启动Tomcat才能再次调试&#xff0c;所以只得重…

spring之refresh流程-Java八股面试(六)

系列文章目录 第一章 ArrayList-Java八股面试(一) 第二章 HashMap-Java八股面试(二) 第三章 单例模式-Java八股面试(三) 第四章 线程池和Volatile关键字-Java八股面试(四) 第五章ConcurrentHashMap-Java八股面试(五) 动态每日更新算法题&#xff0c;想要学习的可以关注一下…

css中重难点整理(vertical-align)

一、vertical-align 在学习vertical-align的时候&#xff0c;可能会很困惑。即使网上有一大推文章讲veitical-align,感觉看完好像懂了&#xff0c;等自己布局的时候用到vertical-align的时候好像对它又很陌生。这就是我在布局的时候遇到的问题。 本来vertical-align就很不好理…

Spring——配置文件实现IOC和DI入门案例

现在先如图创建如下的Maven项目&#xff0c;在业务层和数据层分别写上对应的接口和实现类 在BookServiceImpl中创建一个BookDaoImpl对象&#xff0c;并调用里面的save()方法。 在测试类里面new一个bookservice的实现类&#xff0c;调用save()方法 输出如下图所示 要使用IOC容…