Linux(进程间通信)
目录
一、通信概念
二、进程间通信机制
1、管道
1.1 匿名管道(Anonymous Pipe)
1.2 命名管道(Named Pipe)
2、信号量
2.1 概念
2.2 API详解
2.3 使用示例
3、消息队列
3.1 概念
3.2 API函数
3.3 应用代码
4、共享内存
4.1 概念
4.2 API
4.3 示例代码
5、套接字
5.1 概念
5.2 API
5.3 示例代码
一、通信概念
进程间通信(Inter-Process Communication,IPC)是指在操作系统中,不同进程之间进行数据和信息交换的机制。这种通信允许不同的程序或进程在同一系统上进行协作,共享数据和资源。常见的IPC方式包括管道、套接字、信号量、共享内存和消息队列等。这些机制使得多个进程能够相互通信和协调工作,从而实现更复杂的任务。
二、进程间通信机制
1、管道
1.1 匿名管道(Anonymous Pipe)
①概念:
匿名管道用于在具有亲缘关系(例如父子进程)的进程之间传递数据。 匿名管道是单向的,只能用于父进程向子进程传递数据或者子进程向父进程传递数据,不支持双向通信。
在创建匿名管道时,使用 `pipe()` 系统调用来创建管道,不需要指定名称。匿名管道的生命周期限于创建它们的进程及其子进程。
用于传递数据量较小的场景,例如父子进程之间传递简单的命令或数据。
②API:
int pipe(int pipefd[2]);
功能: 创建一个匿名管道。
参数: pipefd 是包含两个整数的数组,用于存储管道的文件描述符。
返回值: 成功时返回0,失败时返回-1。
③示例代码:
#include <stdio.h>
#include <unistd.h>int main() {int pipefd[2];char buffer[20];pipe(pipefd); // 创建匿名管道pid_t child_pid = fork();if (child_pid == -1) {perror("fork");return 1;} else if (child_pid == 0) { // 子进程close(pipefd[1]); // 关闭写入端read(pipefd[0], buffer, sizeof(buffer));printf("Child received: %s\n", buffer);close(pipefd[0]); // 关闭读取端} else { // 父进程close(pipefd[0]); // 关闭读取端write(pipefd[1], "Hello from parent", 17);close(pipefd[1]); // 关闭写入端}return 0;
}
1.2 命名管道(Named Pipe)
①概念:
命名管道是一种允许不同进程之间通过一个具有唯一名称的文件进行通信的机制。
命名管道是在文件系统中创建的,可以用于不同进程之间的通信,甚至跨越不同的计算机。
命名管道是双向的,可以在两个方向上传递数据。
创建命名管道需要使用 `mkfifo` 命令或者调用 `mkfifo()` 系统调用,指定一个路径和名称。
用于传递较大数据量或者需要持久化的通信场景,例如不同进程之间的实时数据传输。
②API:
int mkfifo(const char *pathname, mode_t mode);
功能:创建一个命名管道。
参数:pathname 是管道的路径和名称,mode 是管道的权限模式。
返回值:成功时返回0,失败时返回-1。
③示例代码:
写进程(要先运行,确保管道创建成功)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>int main() {const char *fifoPath = "my_fifo";// 创建命名管道mkfifo(fifoPath, 0666);int fd = open(fifoPath, O_WRONLY); // 打开管道以写入数据char message[] = "Hello from named pipe!";write(fd, message, strlen(message) + 1);close(fd); // 关闭文件描述符return 0;
}
读进程
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>int main() {const char *fifoPath = "my_fifo";int fd = open(fifoPath, O_RDONLY); // 打开管道以读取数据char buffer[100];ssize_t bytesRead = read(fd, buffer, sizeof(buffer));if (bytesRead > 0) {buffer[bytesRead] = '\0';printf("Received message: %s\n", buffer);}close(fd); // 关闭文件描述符return 0;
}
2、信号量
2.1 概念
信号量是一种用于实现进程间同步和互斥的机制。它是一种同步原语,用于协调多个进程对共享资源的访问,以避免竞态条件(Race Condition)和数据冲突。信号量主要用于解决多个进程并发执行时可能出现的问题。
①进程间通信信号量具有以下特点:
同步: 信号量用于确保多个进程按照特定的顺序执行。通过等待和释放信号量,进程可以控制其执行的时间点,从而协调操作的顺序。
互斥: 信号量用于实现互斥,确保在任何时刻只有一个进程能够访问共享资源。通过使用信号量,进程可以避免多个进程同时修改相同的资源而引发冲突。
②进程间通信信号量的操作通常包括以下两种:
P(Wait)操作: 当一个进程想要使用共享资源时,它尝试执行 P 操作。如果信号量的计数值大于零,它会将计数值减少并获得访问权限。如果计数值为零,该进程将被阻塞,直到计数值大于零。
V(Signal)操作: 当进程使用完共享资源时,它执行 V 操作来释放信号量。这会将信号量的计数值增加,并唤醒等待的进程。
③信号量在进程间通信中有许多应用
进程同步: 确保进程按特定顺序执行,避免竞态条件。
互斥访问共享资源: 保证只有一个进程能够访问共享资源,防止数据损坏。
限制资源访问: 控制同时访问某些资源的进程数量,防止过多的进程占用资源。
2.2 API详解
常用的信号量相关的函数是在 POSIX 标准中定义的,用于在进程间通信中创建和操作信号量。下面是一些常用的信号量函数以及信号量的应用举例:
int sem_init(sem_t *sem, int pshared, unsigned int value);
- 初始化信号量。
- 参数 `sem` 是信号量指针,`pshared` 表示信号量的共享方式,`value` 是初始计数值。
- 返回值:成功返回0,失败返回-1。
int sem_wait(sem_t *sem);
- 对信号量执行 P(Wait)操作。
- 参数 `sem` 是信号量指针。
- 返回值:成功返回0,失败返回-1。
int sem_post(sem_t *sem);
- 对信号量执行 V(Signal)操作。
- 参数 `sem` 是信号量指针。
- 返回值:成功返回0,失败返回-1。
int sem_destroy(sem_t *sem);
- 销毁信号量。
- 参数 `sem` 是信号量指针。
- 返回值:成功返回0,失败返回-1。
2.3 使用示例
①生产者进程(共享内存作为进程间通信介质)
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <semaphore.h>
#include <fcntl.h>
#include <sys/mman.h>#define BUFFER_SIZE 5typedef struct {int buffer[BUFFER_SIZE];int in;int out;sem_t empty;sem_t full;sem_t mutex;
} shared_data_t;int main() {// 创建共享内存int shm_fd = shm_open("/my_shm", O_CREAT | O_RDWR, 0666);ftruncate(shm_fd, sizeof(shared_data_t));shared_data_t *shared_data = (shared_data_t *)mmap(NULL, sizeof(shared_data_t), PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, 0);// 初始化信号量sem_init(&(shared_data->empty), 1, BUFFER_SIZE);sem_init(&(shared_data->full), 1, 0);sem_init(&(shared_data->mutex), 1, 1);int data = 1;while (1) {sem_wait(&(shared_data->empty)); // 等待缓冲区非满sem_wait(&(shared_data->mutex)); // 加锁shared_data->buffer[shared_data->in] = data;printf("Produced: %d\n", data);data++;shared_data->in = (shared_data->in + 1) % BUFFER_SIZE;sem_post(&(shared_data->mutex)); // 解锁sem_post(&(shared_data->full)); // 增加缓冲区内数据数量sleep(1); // 生产数据后等待一段时间}munmap(shared_data, sizeof(shared_data_t));shm_unlink("/my_shm");sem_destroy(&(shared_data->empty));sem_destroy(&(shared_data->full));sem_destroy(&(shared_data->mutex));return 0;
}
②消费者进程
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <semaphore.h>
#include <fcntl.h>
#include <sys/mman.h>#define BUFFER_SIZE 5typedef struct {int buffer[BUFFER_SIZE];int in;int out;sem_t empty;sem_t full;sem_t mutex;
} shared_data_t;int main() {// 打开共享内存int shm_fd = shm_open("/my_shm", O_RDWR, 0666);shared_data_t *shared_data = (shared_data_t *)mmap(NULL, sizeof(shared_data_t), PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, 0);while (1) {sem_wait(&(shared_data->full)); // 等待缓冲区非空sem_wait(&(shared_data->mutex)); // 加锁int data = shared_data->buffer[shared_data->out];printf("Consumed: %d\n", data);shared_data->out = (shared_data->out + 1) % BUFFER_SIZE;sem_post(&(shared_data->mutex)); // 解锁sem_post(&(shared_data->empty)); // 增加缓冲区空闲数量sleep(2); // 消费数据后等待一段时间}munmap(shared_data, sizeof(shared_data_t));close(shm_fd);return 0;
}
③线程同步应用
#include <stdio.h>
#include <pthread.h>
#include <semaphore.h>sem_t semaphore1, semaphore2;void* thread1_function(void* arg) {sem_wait(&semaphore1); // 等待信号量1printf("Thread 1 is executing.\n");sem_post(&semaphore2); // 释放信号量2,允许线程2执行return NULL;
}void* thread2_function(void* arg) {sem_wait(&semaphore2); // 等待信号量2,确保线程1先执行printf("Thread 2 is executing.\n");sem_post(&semaphore1); // 释放信号量1,允许线程1再次执行return NULL;
}int main() {sem_init(&semaphore1, 0, 1); // 初始化信号量1为1sem_init(&semaphore2, 0, 0); // 初始化信号量2为0pthread_t thread1, thread2;pthread_create(&thread1, NULL, thread1_function, NULL);pthread_create(&thread2, NULL, thread2_function, NULL);pthread_join(thread1, NULL);pthread_join(thread2, NULL);sem_destroy(&semaphore1);sem_destroy(&semaphore2);return 0;
}
④线程互斥访问共享资源
#include <stdio.h>
#include <pthread.h>
#include <semaphore.h>sem_t semaphore;
int shared_variable = 0;void* thread1_function(void* arg) {sem_wait(&semaphore); // 请求信号量shared_variable++;printf("Thread 1: Incremented shared variable to %d.\n", shared_variable);sem_post(&semaphore); // 释放信号量return NULL;
}void* thread2_function(void* arg) {sem_wait(&semaphore); // 请求信号量shared_variable--;printf("Thread 2: Decremented shared variable to %d.\n", shared_variable);sem_post(&semaphore); // 释放信号量return NULL;
}int main() {sem_init(&semaphore, 0, 1); // 初始化信号量为1pthread_t thread1, thread2;pthread_create(&thread1, NULL, thread1_function, NULL);pthread_create(&thread2, NULL, thread2_function, NULL);pthread_join(thread1, NULL);pthread_join(thread2, NULL);sem_destroy(&semaphore);return 0;
}
⑤线程限制资源访问
#include <stdio.h>
#include <pthread.h>
#include <semaphore.h>sem_t semaphore;
int resource_count = 3;void* thread_function(void* arg) {sem_wait(&semaphore); // 请求信号量resource_count--;printf("Thread %d: Accessed resource. Remaining resources: %d.\n", (int)arg, resource_count);sem_post(&semaphore); // 释放信号量return NULL;
}int main() {sem_init(&semaphore, 0, 3); // 初始化信号量为3,允许同时访问3个资源pthread_t threads[5];for (int i = 0; i < 5; i++) {pthread_create(&threads[i], NULL, thread_function, (void*)(i + 1));}for (int i = 0; i < 5; i++) {pthread_join(threads[i], NULL);}sem_destroy(&semaphore);return 0;
}
3、消息队列
3.1 概念
消息队列是一种进程间通信的机制,用于在不同进程之间传递数据。它允许一个进程向消息队列发送消息,而另一个进程可以从队列中读取这些消息。消息队列是一种异步通信方式,进程可以通过消息队列实现数据的交换,而不需要直接共享内存。
消息队列的特点包括:
1. 异步通信:发送者将消息发送到队列后,不需要等待接收者处理完消息就可以继续自己的工作。
2. 松耦合:消息队列实现了进程间的松耦合,发送者和接收者之间无需直接知道彼此的存在。
3. 缓冲:消息队列可以缓存一定数量的消息,即使接收者暂时不可用,消息也不会丢失。
4. 多对多:多个进程可以同时向一个队列发送消息,也可以从队列读取消息。
5. 消息优先级:消息队列通常支持消息的优先级,以确保重要的消息能够尽快被处理。
消息队列在操作系统中是由内核维护的,通常由消息队列标识符来标识不同的消息队列。
3.2 API函数
消息队列通常使用操作系统提供的API来进行创建、发送和接收消息。以下是System V消息队列的常用API函数及其详细解释:
1. int msgget(key_t key, int msgflg);
- 创建或获取消息队列。
- 参数 `key` 是消息队列键值,`msgflg` 是标志位(如 `IPC_CREAT` 表示创建队列)。
- 返回值:成功返回消息队列标识符(非负整数),失败返回-1。
2. int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
- 发送消息到消息队列。
- 参数 `msqid` 是消息队列标识符,`msgp` 是指向消息结构的指针,`msgsz` 是消息的大小,`msgflg` 是标志位。
- 返回值:成功返回0,失败返回-1。
3. ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);
- 从消息队列接收消息。
- 参数 `msqid` 是消息队列标识符,`msgp` 是指向消息结构的指针,`msgsz` 是接收缓冲区大小,`msgtyp` 是消息的类型,`msgflg` 是标志位。
- 返回值:成功返回接收的消息大小,失败返回-1。
4. int msgctl(int msqid, int cmd, struct msqid_ds *buf);
- 控制消息队列属性。
- 参数 `msqid` 是消息队列标识符,`cmd` 是控制命令(如 `IPC_RMID` 表示删除队列),`buf` 是指向 `msqid_ds` 结构的指针用于获取或设置属性。
- 返回值:成功返回0,失败返回-1。
5. key_t ftok(const char *pathname, int proj_id);
pathname:一个存在的文件路径名,用于生成键值。通常选择一个在系统中具有唯一性的文件。
proj_id:一个整数,用于在 `pathname` 的基础上生成键值的一部分。这个值应该被选取为正整数,不同的项目可以选择不同的值。
返回值:返回一个 `key_t` 类型的键值,用于后续的系统调用。如果出错,返回 -1。
`ftok` 函数通过结合给定的文件路径名和项目标识号来生成一个键值,这个键值可以被用于创建和访问共享内存、消息队列等。在实际使用中,`ftok` 生成的键值应该在相关的进程间通信操作中保持一致。
3.3 应用代码
①发送进程示例代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>struct message {long type;char text[50];
};int main() {key_t key;int msgid;struct message msg;// 生成键值key = ftok("message_queue_example", 65);// 创建消息队列msgid = msgget(key, 0666 | IPC_CREAT);// 设置消息msg.type = 1;strcpy(msg.text, "Hello from sender!");// 发送消息msgsnd(msgid, &msg, sizeof(msg.text), 0);return 0;
}
②接收进程示例代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>struct message {long type;char text[50];
};int main() {key_t key;int msgid;struct message msg;// 生成键值key = ftok("message_queue_example", 65);// 获取消息队列msgid = msgget(key, 0666 | IPC_CREAT);// 接收消息msgrcv(msgid, &msg, sizeof(msg.text), 1, 0);printf("Received message: %s\n", msg.text);// 删除消息队列msgctl(msgid, IPC_RMID, NULL);return 0;
}
4、共享内存
4.1 概念
共享内存是一种进程间通信机制,允许多个进程共享同一块物理内存区域。相对于其他进程间通信方式,共享内存的特点是高效、快速,适用于大量数据的交换。
4.2 API
①key_t ftok(const char *pathname, int proj_id);
pathname:一个存在的文件路径名,用于生成键值。通常选择一个在系统中具有唯一性的文件。
proj_id:一个整数,用于在 `pathname` 的基础上生成键值的一部分。这个值应该被选取为正整数,不同的项目可以选择不同的值。
返回值:返回一个 `key_t` 类型的键值,用于后续的系统调用。如果出错,返回 -1。
`ftok` 函数通过结合给定的文件路径名和项目标识号来生成一个键值,这个键值可以被用于创建和访问共享内存、消息队列等。在实际使用中,`ftok` 生成的键值应该在相关的进程间通信操作中保持一致。
②int shmget(key_t key, size_t size, int shmflg);
- 创建或获取共享内存段。
- `key`:用于标识共享内存的键值。
- `size`:指定共享内存段的大小。
- `shmflg`:用于控制共享内存的标志,可以包括 `IPC_CREAT` 来创建共享内存。
- 返回值:成功时返回共享内存标识符,失败时返回-1。
③void *shmat(int shmid, const void *shmaddr, int shmflg);
- 连接到共享内存段。
- `shmid`:共享内存标识符。
- `shmaddr`:指定连接地址,通常设为NULL,由系统决定。
- `shmflg`:用于控制共享内存的标志。
- 返回值:连接成功时返回连接地址,失败时返回 `(void*)-1`。
④int shmdt(const void *shmaddr);
- 断开与共享内存的连接。
- `shmaddr`:连接地址。
- 返回值:成功时返回0,失败时返回-1。
⑤int shmctl(int shmid, int cmd, struct shmid_ds *buf);
- 控制共享内存段的属性。
- `shmid`:共享内存标识符。
- `cmd`:控制命令,如 `IPC_RMID` 用于删除共享内存。
- `buf`:用于获取或设置共享内存段的属性。
- 返回值:成功时返回0,失败时返回-1。
4.3 示例代码
创建共享内存
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/shm.h>int main() {key_t key = ftok("shared_memory_example", 65); // 生成键值int shmid = shmget(key, sizeof(int), 0666 | IPC_CREAT); // 创建共享内存if (shmid == -1) {perror("shmget");exit(1);}int *shared_data = (int*)shmat(shmid, NULL, 0); // 连接到共享内存*shared_data = 10; // 设置共享数据printf("Shared data set to %d\n", *shared_data);shmdt(shared_data); // 断开与共享内存的连接return 0;
}
使用共享内存
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/shm.h>int main() {key_t key = ftok("shared_memory_example", 65); // 生成键值int shmid = shmget(key, sizeof(int), 0666); // 获取共享内存if (shmid == -1) {perror("shmget");exit(1);}int *shared_data = (int*)shmat(shmid, NULL, 0); // 连接到共享内存printf("Shared data read: %d\n", *shared_data);shmdt(shared_data); // 断开与共享内存的连接return 0;
}
5、套接字
5.1 概念
套接字(Socket)是一种用于实现网络通信的编程接口,它提供了一种机制,使得不同计算机之间可以通过网络进行数据交换。套接字允许进程在不同主机上的应用程序通过网络进行通信,从而实现分布式系统和客户端-服务器模型。
套接字提供了一组API函数,用于在应用程序中创建、连接、发送和接收数据。它是网络编程的基础,用于实现各种通信协议,如TCP(传输控制协议)和UDP(用户数据报协议)。
5.2 API
1. int socket(int domain, int type, int protocol);
- 创建一个新的套接字。
- `domain`:指定套接字的协议族,如 `AF_INET` 表示IPv4。
- `type`:指定套接字的类型,如 `SOCK_STREAM` 表示面向连接的流套接字(TCP)。
- `protocol`:指定使用的传输协议,通常设为0以便自动选择合适的协议。
- 返回值:成功时返回套接字文件描述符,失败时返回-1。
2. int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
- 将一个本地地址与套接字绑定。
- `sockfd`:套接字文件描述符。
- `addr`:指向本地地址结构的指针。
- `addrlen`:本地地址结构的大小。
- 返回值:成功时返回0,失败时返回-1。
3. int listen(int sockfd, int backlog);
- 将套接字设为监听模式,准备接受连接请求。
- `sockfd`:套接字文件描述符。
- `backlog`:等待连接的队列长度。
- 返回值:成功时返回0,失败时返回-1。
4. int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
- 接受一个连接请求,创建一个新的套接字用于与客户端通信。
- `sockfd`:监听套接字文件描述符。
- `addr`:用于存储客户端地址的结构指针。
- `addrlen`:客户端地址结构的大小。
- 返回值:成功时返回新创建的套接字文件描述符,失败时返回-1。
5. int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
- 建立与远程服务器的连接。
- `sockfd`:套接字文件描述符。
- `addr`:指向远程服务器地址结构的指针。
- `addrlen`:远程服务器地址结构的大小。
- 返回值:成功时返回0,失败时返回-1。
6. ssize_t send(int sockfd, const void *buf, size_t len, int flags);
- 发送数据。
- `sockfd`:套接字文件描述符。
- `buf`:指向待发送数据的缓冲区。
- `len`:待发送数据的大小。
- `flags`:选项标志,通常设为0。
- 返回值:成功时返回发送的字节数,失败时返回-1。
7. ssize_t recv(int sockfd, void *buf, size_t len, int flags);
- 接收数据。
- `sockfd`:套接字文件描述符。
- `buf`:用于存储接收数据的缓冲区。
- `len`:缓冲区的大小。
- `flags`:选项标志,通常设为0。
- 返回值:成功时返回接收的字节数,连接关闭时返回0,失败时返回-1。
8. int close(int sockfd);
- 关闭套接字连接。
- `sockfd`:套接字文件描述符。
- 返回值:成功时返回0,失败时返回-1。
5.3 示例代码
服务端:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>int main() {int sockfd, newsockfd, portno, clilen;char buffer[256];struct sockaddr_in serv_addr, cli_addr;int n;// 创建套接字sockfd = socket(AF_INET, SOCK_STREAM, 0);if (sockfd < 0) {perror("Error opening socket");exit(1);}bzero((char *)&serv_addr, sizeof(serv_addr));portno = 12345;serv_addr.sin_family = AF_INET;serv_addr.sin_addr.s_addr = INADDR_ANY;serv_addr.sin_port = htons(portno);// 绑定套接字到地址if (bind(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) {perror("Error on binding");exit(1);}// 监听连接listen(sockfd, 5);clilen = sizeof(cli_addr);newsockfd = accept(sockfd, (struct sockaddr *)&cli_addr, &clilen);if (newsockfd < 0) {perror("Error on accept");exit(1);}// 读取客户端发送的消息bzero(buffer, 256);n = read(newsockfd, buffer, 255);if (n < 0) {perror("Error reading from socket");exit(1);}printf("Message from client: %s\n", buffer);// 关闭连接close(newsockfd);close(sockfd);return 0;
}
客户端:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>int main() {int sockfd, portno, n;struct sockaddr_in serv_addr;struct hostent *server;char buffer[256];portno = 12345;sockfd = socket(AF_INET, SOCK_STREAM, 0);if (sockfd < 0) {perror("Error opening socket");exit(1);}// 获取服务器信息server = gethostbyname("localhost");if (server == NULL) {fprintf(stderr, "Error, no such host\n");exit(1);}bzero((char *)&serv_addr, sizeof(serv_addr));serv_addr.sin_family = AF_INET;bcopy((char *)server->h_addr, (char *)&serv_addr.sin_addr.s_addr, server->h_length);serv_addr.sin_port = htons(portno);// 连接到服务器if (connect(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) {perror("Error connecting");exit(1);}printf("Please enter the message: ");bzero(buffer, 256);fgets(buffer, 255, stdin);n = write(sockfd, buffer, strlen(buffer));if (n < 0) {perror("Error writing to socket");exit(1);}// 关闭套接字close(sockfd);return 0;
}
相关文章:

Linux(进程间通信)
目录 一、通信概念 二、进程间通信机制 1、管道 1.1 匿名管道(Anonymous Pipe) 1.2 命名管道(Named Pipe) 2、信号量 2.1 概念 2.2 API详解 2.3 使用示例 3、消息队列 3.1 概念 3.2 API函数 3.3 应用代码 4、共享内…...

Go的Gorm数据库操作错误WHERE conditions required
这是我在写这个代码处出现的问题 result : db.Save(&emergency) 这个错误是由于在提交保存数据时,GORM 需要指定 WHERE 条件,确保能够正确执行数据库操作。要解决这个问题,可以尝试使用 Create 方法替换 Save 方法,同时将创…...

基于java swing和mysql实现的仓库商品管理系统(源码+数据库+运行指导视频)
一、项目简介 本项目是一套基于java swing和mysql实现的仓库商品管理系统,主要针对计算机相关专业的正在做毕设的学生与需要项目实战练习的Java学习者。 包含:项目源码、项目文档、数据库脚本等,该项目附带全部源码可作为毕设使用。 项目都经…...

6、css学习6(表格)
1、指定CSS表格边框,使用border属性。 2、表格双边框是因为th/td有各自独立的边框。 3、boder-collapse设置表格边框是否被折叠成一个单一的边框。 4、width和height属性定义表格的宽度和高度。 5、text-align属性设置水平对齐方式。 6、vertic-align属性设置垂…...

Ceph源码解析:PG peering
集群中的设备异常(异常OSD的添加删除操作),会导致PG的各个副本间出现数据的不一致现象,这时就需要进行数据的恢复,让所有的副本都达到一致的状态。 一、OSD的故障和处理办法: 1. OSD的故障种类: 故障A:一…...

解决jupyter notebook可以使用pytorch而Pycharm不能使用pytorch的问题
之前我是用的这个目录下的Python 开始更新目录 1、 2、 3、...

对建造者模式理解
当对象成员变量太多时,使用建造方法给变量赋值往往变得很臃肿,所以可以这样做 public class Something {private String a;private String b;private String c;private String d;private String e;public Something(Builder builder) {this.a builder.…...

回归预测 | MATLAB实现CSO-ELM布谷鸟算法优化极限学习机多输入单输出回归预测(多指标,多图)
回归预测 | MATLAB实现CSO-ELM布谷鸟算法优化极限学习机多输入单输出回归预测(多指标,多图) 目录 回归预测 | MATLAB实现CSO-ELM布谷鸟算法优化极限学习机多输入单输出回归预测(多指标,多图)效果一览基本介…...

静态链接库和动态链接库的区别
C静态链接库(Static Linking)和动态链接库(Dynamic Linking)的主要区别在于代码的组织和加载方式。 静态链接库 在编译时将库的代码和应用程序的代码合并在一起,生成一个单独的可执行文件。执行文件独立包含所需的库…...

使用 python 源码搭建 conda 环境
今天需要使用 python 2.6.8 的环境,发现 conda 设置成清华源后,没有旧版本了。所以打算从官网上下载一份 python 进行安装, 结果发现,conda 不能直接安装离线包(也可能我没找到方法),经过一番尝…...

dart 学习之 异步操作
import package:dio/dio.dart;// 定义一个异步函数,用于获取 URL 的内容 Future<String> getUrl(String url) async {Dio dio Dio();Response response await dio.get(url);return response.data; }void main() async {// 在主函数中执行异步操作var conten…...

《Flink学习笔记》——第二章 Flink的安装和启动、以及应用开发和提交
介绍Flink的安装、启动以及如何进行Flink程序的开发,如何运行部署Flink程序等 2.1 Flink的安装和启动 本地安装指的是单机模式 0、前期准备 java8或者java11(官方推荐11)下载Flink安装包 https://flink.apache.org/zh/downloads/hadoop&a…...
网易新财报:游戏稳、有道进、云音乐正爬坡
今年以来,AI大模型的火热程度屡屡攀升,越来越多的企业都加入到了AI大模型的赛场中,纷纷下场布局。而在众多参与者中,互联网企业的身影更是频频浮现,比如,百度、阿里巴巴、腾讯等等。值得一提的是࿰…...

Docsify的评论系统gitalk配置过程
💖 作者简介:大家好,我是Zeeland,开源建设者与全栈领域优质创作者。📝 CSDN主页:Zeeland🔥📣 我的博客:Zeeland📚 Github主页: Undertone0809 (Zeeland)&…...

HarmonyOS/OpenHarmony(Stage模型)卡片开发应用上下文Context使用场景二
3.创建其他应用或其他Module的Context 基类Context提供创建其他应用或其他Module的Context的方法为createModuleContext(moduleName:string),创建其他应用或者其他Module的Context,从而通过该Context获取相应的资源信息(例如获取其他Module的…...

数字货币量化交易平台
数字货币量化交易平台是近年来金融科技领域迅速崛起的一种创新型交易方式。它通过应用数学模型和算法策略,实现对数字货币市场的自动交易和风险控制。然而,要在这个竞争激烈的领域中脱颖而出,一个数字货币量化交易平台需要具备足够的专业性&a…...

2022 ICPC 济南 E Identical Parity (扩欧)
2022 ICPC 济南 E. Identical Parity (扩欧) Problem - E - Codeforces 大意:给出一个 n 和一个 k , 问是否能构造一个长 n 的排列使得所有长 k 的连续子序列和的奇偶性相同。 思路:通过分析可知 , 任两个间隔 k - 1 的元素奇偶…...

【BUG事务内消息发送】事务内消息发送,事务还未结束,消息发送已被消费,查无数据怎么解决?
问题描述 在一个事务内完成插入操作,通过MQ异步通知其他微服务进行事件处理。 由于是在事务内发送,其他服务消费消息,查询数据时还不存在如何解决呢? 解决方案 通过spring-tx包的TransactionSynchronizationManager事务管理器解…...

数据分析作业四-基于用户及物品数据进行内容推荐
## 导入支持库 import pandas as pd import matplotlib.pyplot as plt import sklearn.metrics as metrics import numpy as np from sklearn.neighbors import NearestNeighbors from scipy.spatial.distance import correlation from sklearn.metrics.pairwise import pairwi…...

在腾讯云服务器OpenCLoudOS系统中安装svn(有图详解)
1. 安装svn yum -y install subversion 安装成功: 2. 创建数据根目录及仓库 mkdir -p /usr/local/svn/svnrepository 创建test仓库: svnadmin create /usr/local/svn/test test仓库创建成功: 3. 修改配置test仓库 cd /usr/local/svn/te…...

C语言日常刷题5
文章目录 题目答案与解析1234567、 题目 1、以下叙述中正确的是( ) A: 只能在循环体内和switch语句体内使用break语句 B: 当break出现在循环体中的switch语句体内时,其作用是跳出该switch语句体,并中止循环体的执行 C: continue语…...

【LeetCode-中等题】73. 矩阵置零
题目 题解一:使用标记数组 public void setZeroes(int[][] matrix) {int m matrix.length;int n matrix[0].length;boolean[] row new boolean[m];boolean[] col new boolean[n];for(int i0; i< m;i){for(int j 0;j<n;j){if (matrix[i][j] 0) row[i]col…...

本地部署 FastGPT
本地部署 FastGPT 1. FastGPT 是什么2. 部署 FastGPT 1. FastGPT 是什么 FastGPT 是一个基于 LLM 大语言模型的知识库问答系统,提供开箱即用的数据处理、模型调用等能力。同时可以通过 Flow 可视化进行工作流编排,从而实现复杂的问答场景! …...

软件工程(十八) 行为型设计模式(四)
1、状态模式 简要说明 允许一个对象在其内部改变时改变它的行为 速记关键字 状态变成类 类图如下 状态模式主要用来解决对象在多种状态转换时,需要对外输出不同的行为的问题。比如订单从待付款到待收货的咋黄台发生变化,执行的逻辑是不一样的。 所以我们将状态抽象为一…...

Socket通信与WebSocket协议
文章目录 目录 文章目录 前言 一、Socket通信 1.1 BIO 1.2 NIO 1.3 AIO 二、WebSocket协议 总结 前言 一、Socket通信 Socket是一种用于网络通信的编程接口(API),它提供了一种机制,使不同主机之间可以通过网络进行数据传输和通信…...

新KG视点 | Jeff Pan、陈矫彦等——大语言模型与知识图谱的机遇与挑战
OpenKG 大模型专辑 导读 知识图谱和大型语言模型都是用来表示和处理知识的手段。大模型补足了理解语言的能力,知识图谱则丰富了表示知识的方式,两者的深度结合必将为人工智能提供更为全面、可靠、可控的知识处理方法。在这一背景下,OpenKG组织…...

详解过滤器Filter和拦截器Interceptor的区别和联系
目录 前言 区别 联系 前言 过滤器(Filter)和拦截器(Interceptor)都是用于在Web应用程序中处理请求和响应的组件,但它们在实现方式和功能上有一些区别。 区别 1. 实现方式: - 过滤器是基于Servlet规范的组件,通过实现javax.servlet.Filt…...

List常用的操作
1、看List里是否存在某个元素 contains //省略建立listboolean contains stringList.contains("上海");System.out.println(contains); 如果存在是true,不存在是false 2、看某个元素在List中的索引号 .indexOf List<String>stringList new Ar…...

Android studio APK切换多个摄像头(Camera2)
1.先设置camera的权限 <uses-permission android:name"android.permission.CAMERA" /> 2.布局 <?xml version"1.0" encoding"utf-8"?> <LinearLayout xmlns:android"http://schemas.android.com/apk/res/android"and…...

ChatGPT 对教育的影响,AI 如何颠覆传统教育
胜量老师 来源:BV1Nv4y1H7kC 由Chat GPT引发的对教育的思考,人类文明发展至今一直靠教育完成文明的传承,一个年轻人要经历若干年的学习,才能进入社会投入对文明的建设,而学习中有大量内容是需要记忆和反复训练的。 无…...