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

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

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

相关文章

湖北咸宁农业三维扫描数字化农业3d打印制造应用-CASAIM中科广电

农业是人类衣食之源、生存之本&#xff0c;是一切生产的首要条件&#xff0c;CASAIM在农业三维扫描和3d打印应用上有丰富经验。 1.三维扫描技术在农业领域的应用 CASAIM三维扫描是集光学、机电和计算机技术于一体的高新无损检测技术&#xff0c;能够对实物的空间外形、结构乃…

adb shell获取安卓设备电量ROM内存帧率等信息

adb shell获取安卓设备电量ROM内存帧率等信息 adb shell指令获取Android设备的运行状态&#xff0c;如电池信息&#xff08;包含电量百分比&#xff0c;电池状态&#xff0c;电池温度&#xff0c;电池电压&#xff0c;充放电电流&#xff09;&#xff0c;CPU占比&#xff0c;内…

2023年MySQL核心技术面试第一篇

目录 一 . 存储&#xff1a;一个完整的数据存储过程是怎样的&#xff1f; 1.1 数据存储过程 1.1.1 创建MySQl 数据库 1.1.1.1 为什么我们要先创建一个数据库&#xff0c;而不是直接创建数据表&#xff1f; 1.1.1.2基本操作部分 1.2 选择索引问题 二 . 字段&#xff1a;这么多的…

【算法与数据结构】513、LeetCode找树左下角的值

文章目录 一、题目二、解法三、完整代码 所有的LeetCode题解索引&#xff0c;可以看这篇文章——【算法和数据结构】LeetCode题解。 一、题目 二、解法 思路分析&#xff1a;这道题用层序遍历来做比较简单&#xff0c;最底层最左边节点就是层序遍历当中最底层元素容器的第一个值…

Nacos基础(2)——nacos的服务器和命名空间 springBoot整合nacos 多个nacos配置的情况

目录 引出nacos服务器和命名空间Nacos服务器命名空间 springBoot整合nacosspringcloud Alibaba 版本与springcloud对应关系引包配置maincontroller 报错以及解决【报错】错误&#xff1a;缺少服务名称报错&#xff1a;9848端口未开放 启动测试引入多个nacos配置多个配置的情况没…

【MCU】SD NAND芯片之国产新选择

文章目录 前言传统SD卡和可贴片SD卡传统SD卡可贴片SD卡 实际使用总结 前言 随着目前时代的快速发展&#xff0c;即使是使用MCU的项目上也经常有大数据存储的需求。可以看到经常有小伙伴这样提问&#xff1a; 大家好&#xff0c;请问有没有SD卡芯片&#xff0c;可以直接焊接到P…

排序算法:快速排序

快速排序算法由 C. A. R. Hoare 在 1960 年提出。它的时间复杂度也是 O(nlogn)&#xff0c;但它在时间复杂度为 O(nlogn) 级的几种排序算法中&#xff0c;大多数情况下效率更高&#xff0c;所以快速排序的应用非常广泛。再加上快速排序所采用的分治思想非常实用&#xff0c;使得…

香港服务器怎么打开SSH

​  SSH是一种远程登录协议&#xff0c;可以通过加密方式在网络上安全地传输数据。它允许用户在远程服务器上执行命令&#xff0c;管理文件和目录&#xff0c;并进行其他系统管理任务。 如何打开SSH服务? 1.确认已安装OpenSSH服务器&#xff1a; 你可以通过命令sudoapt-geti…

Excel显示此值与此单元格定义的数据验证限制不匹配怎么办?

总结&#xff1a;1、在编辑excel文档的时候&#xff0c;弹出此时预测单元格定义的数据验证&#xff0c;限制不匹配的提示。2、这是我们点击菜单来的数据菜单。3、然后点击数据工具栏的数据验证下拉按钮。4、在弹出的菜单中选择数据验证的菜单项。5、然后在打开的窗口中点击左下…

Jmeter(二十七):BeanShell PostProcessor跨线程全局变量使用

在性能测试中&#xff0c;两个相关联的接口不一定都在同一个线程组&#xff0c;遇见这种情况时&#xff0c;我们要进行跨线程组传参&#xff0c;此处用登录和查询配送单两个请求举例&#xff1b; 1、登录请求中配置json提取器&#xff0c;将接口返回的token保存在变量中&#…

Python爬虫异常处理实践:处理被封禁和网站升级问题

在这篇文章中&#xff0c;我们将一起探讨Python爬虫异常处理实践&#xff0c;特别关注处理被封禁和网站升级问题。让我们一起来看看如何解决这些问题&#xff0c;提高我们爬虫程序的稳定性和可靠性。   首先&#xff0c;我们要了解为什么会遇到这些问题。网站封禁爬虫的原因主…

从C语言到C++_34(C++11_下)可变参数+ lambda+function+bind+笔试题

目录 1. 可变参数模板 1.1 展开参数包 1.1.1 递归函数方式展开 1.1.2 逗号表达式展开 1.2 emplace相关接口 2. lambda表达式&#xff08;匿名函数&#xff09; 2.1 C11之前函数的缺陷 2.2 lambda表达式语法 2.3 函数对象与lambda表达式 3. 包装器 3.1 function包装器…

不同子网络中的通信过程

从输入www.baidu.com经历了什么 一、DNS&#xff08;网址->IP&#xff09; 二、ARP&#xff08;IP->MAC&#xff09; A->B&#xff1a;有数据发送&#xff0c;数据封装ip之后发现没有主机B的mac地址。然后ARP在本网段广播&#xff1a;检查目标地址和源地址是否在同一…

【程序猿书籍大放送:第二期】《强化学习:原理与Python实战》

&#x1f339;欢迎来到爱书不爱输的程序猿的博客, 本博客致力于知识分享&#xff0c;与更多的人进行学习交流 爱书不爱输的程序猿&#xff1a;送书第二期 一、搞懂大模型的智能基因&#xff0c;RLHF系统设计关键问答1.RLHF是什么&#xff1f;2.RLHF适用于哪些任务&#xff1f;3…

MySQL日志管理 备份和恢复

备份的主要目的是灾难恢复&#xff0c;备份还可以测试应用、回滚数据修改、查询历史数据、审计等。 而备份、恢复中&#xff0c;日志起到了很重要的作用 #######前言&#xff1a;日志⭐⭐ MySQL 的日志默认保存位置为 /usr/local/mysql/data ##配置文件 vim /etc/my.cnf [mys…

c++23中的新功能之十三Ranges的as系列

一、介绍 在前面反复提到过&#xff0c;c编程的语法也越来越向着自然语言方向发展&#xff0c;今天这个as_函数就是如此&#xff0c;一般学习过英文的都知道as是啥意思。非常容易理解&#xff0c;所以c23中用这个词来描述函数&#xff0c;其实也是非常走心了。 二、as_const …

Git仓库简介

1、工作区、暂存区、仓库 工作区&#xff1a;电脑里能看到的目录。 暂存区&#xff1a;工作区有一个隐藏目录.git&#xff0c;是Git的版本库&#xff0c;Git的版本库里存了很多东西&#xff0c;其中最重要的就是称为stage&#xff08;或者叫index&#xff09;的暂存区&#xf…

vue数字输入框

目录 1.emitter.JS function broadcast (componentName, eventName, params) {this.$children.forEach(child > {var name child.$options.componentNameif (name componentName) {child.$emit.apply(child, [eventName].concat(params))} else {broadcast.apply(child, …

基于Spring Boot的软件缺陷追踪系统的设计与实现(Java+spring boot+MySQL)

获取源码或者论文请私信博主 演示视频&#xff1a; 基于Spring Boot的软件缺陷追踪系统的设计与实现&#xff08;Javaspring bootMySQL&#xff09; 使用技术&#xff1a; 前端&#xff1a;html css javascript jQuery ajax thymeleaf 微信小程序 后端&#xff1a;Java spri…

GNS3 在 Linux 上的安装指南

文章目录 GNS3 在 Linux 上的安装指南1. 基于 Ubuntu 的发行版安装 GNS32. 基于 Debian 的安装3. 基于 ArchLinux 的安装4. 从 Pypi 安装 GNS35. 启动 GNS3 服务端GNS3 在 Linux 上的安装指南 大家好,今天我们来聊聊如何在 Linux 上安装 GNS3。GNS3 是一个非常受欢迎的网络模…