Linux (межпроцессное взаимодействие)

Оглавление

1. Концепция коммуникации

2. Механизм межпроцессного взаимодействия

1. Трубопровод

1.1 Анонимная трубка

1.2 Именованный канал

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 Пример кода


1. Концепция коммуникации

Межпроцессное взаимодействие (IPC) относится к механизму обмена данными и информацией между различными процессами в операционной системе. Такое взаимодействие позволяет различным программам или процессам сотрудничать в одной системе и обмениваться данными и ресурсами. Общие методы IPC включают каналы, сокеты, семафоры, общую память, очереди сообщений и т. д. Эти механизмы позволяют нескольким процессам взаимодействовать и координировать работу друг с другом для достижения более сложных задач.

2. Механизм межпроцессного взаимодействия

1. Трубопровод

1.1 Анонимная трубка

① Концепция:

        Анонимные каналы используются для передачи данных между процессами, имеющими родственные отношения (например, родительско-дочерние процессы).. Анонимные каналы являются односторонними и могут использоваться только для передачи данных из родительского процесса в дочерний процесс или из дочернего процесса в родительский процесс. Двунаправленная связь не поддерживается.
        При создании анонимного канала используйте системный вызов `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 Именованный канал

①Концепция:
     Именованный канал — это механизм, который обеспечивает связь между различными процессами через файл с уникальным именем.
     Именованные каналы создаются в файловой системе и могут использоваться для связи между различными процессами, даже на разных компьютерах.
     Именованные каналы являются двунаправленными и могут передавать данные в обоих направлениях.
     Чтобы создать именованный канал, вам нужно использовать команду `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 (ожидание): когда процесс хочет использовать общий ресурс, он пытается выполнить операцию P. Если счетчик семафора больше нуля, он уменьшает счетчик и получает доступ. Если счетчик равен нулю, процесс будет заблокирован до тех пор, пока счетчик не станет больше нуля.

Операция V (сигнал): когда процесс завершает использование общих ресурсов, он выполняет операцию 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 (ожидание) на семафоре.
   — Параметр `sem` является указателем семафора.
   — Возвращаемое значение: 0 в случае успеха, -1 в случае неудачи.

int sem_post(sem_t *sem);
   — выполнить операцию V (сигнал) на семафоре.
   — Параметр `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 5

typedef 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 5

typedef 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);   // 等待信号量1
    printf("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为1
    sem_init(&semaphore2, 0, 0);  // 初始化信号量2为0

    pthread_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);  // 初始化信号量为1

    pthread_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, предоставляемые операционной системой, для создания, отправки и получения сообщений. Ниже приведены общие функции API очереди сообщений System V и их подробные пояснения:

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 сокет(домен int, тип int, протокол int);
— Создать новый сокет.
- `domain`: указывает семейство протоколов сокета, например `AF_INET`, указывающее IPv4.
- `type`: определяет тип сокета, например `SOCK_STREAM` представляет собой потоковый сокет с установлением соединения (TCP).
- `protocol`: указывает используемый транспортный протокол, обычно устанавливается равным 0 для автоматического выбора соответствующего протокола.
— Возвращаемое значение: в случае успеха возвращает дескриптор файла сокета, в случае неудачи -1.
2.intbind(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;
}

おすすめ

転載: blog.csdn.net/qq_57594025/article/details/132534192