Федеральное агентство связи ФЕДЕРАЛЬНОЕ ГОСУДАРСТВЕННОЕ БЮДЖЕТНОЕ
ОБРАЗОВАТЕЛЬНОЕ УЧРЕЖДЕНИЕ ВЫСШЕГО ОБРАЗОВАНИЯ «САНКТ-ПЕТЕРБУРГСКИЙ ГОСУДАРСТВЕННЫЙ УНИВЕРСИТЕТ ТЕЛЕКОММУНИКАЦИЙ ИМ. ПРОФ. М. А. БОНЧ-БРУЕВИЧА» (СПбГУТ)
Факультет инфокоммуникационных сетей и систем Кафедра программной инженерии и вычислительной техники
ЛАБОРАТОРНАЯ РАБОТА №9 по дисциплине «Операционные системы и сети»
на тему «Разработка кросс-платформенной программы с использованием POSIX функций»
Выполнил: студент 3-го курса дневного отделения группы ИКПИ-85
Коваленко Леонид Александрович Преподаватель:
доцент кафедры ПИиВТ Дагаев Александр Владимирович
Санкт-Петербург 2020
Цель работы Разработать кросс-платформенную программу с использованием
POSIX функций.
Написать кросс-платформенную программу на C с использованием POSIX функций и продемонстрировать ее работу.
Работа выполняется в операционной системе Linux Debian.
Стандарт POSIX описывает множество базовых, системных сервисов, необходимых для функционирования прикладных программ. Цель POSIX — сделать приложения мобильными на уровне исходного кода. Это значит, в частности, что при переносе C-программ на другую операционную платформу потребуется всего лишь перекомпиляция.
Напишем кросс-платформенную программу на C с использованием только POSIX функций.
Суть программы будет заключаться в том, чтобы несколько процессовклиентов могли обмениваться сообщениями с процессом-сервером посредством сокетов (табл. 1, 2).
Таблица 1 — Файл server.c
#include <arpa/inet.h> #include <pthread.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/socket.h> #include <unistd.h> #define PORT 8080 #define BUFSIZE 512 #define CONNECTIONS 4
//Структура для потока записи typedef struct {
pthread_t *read_threads; int *sockets;
} WriteThreadData;
//Структура для потоков чтения typedef struct {
int user_id;
int *socket, *allow; } ReadThreadData;
//Функция для потока записи void *runWriteThread(void *arg) {
2
WriteThreadData *data = (WriteThreadData *)arg; char buffer[BUFSIZE], temp[BUFSIZE];
while (1) {
memset(buffer, '\0', BUFSIZE); // Очистка printf("#> ");
fgets(buffer, BUFSIZE, stdin); // Ввод
//Если ввели 'list', то выводим список пользователей if (strncmp(buffer, "list", sizeof(char) * 4) == 0) {
printf("Клиенты: ");
for (int i = 0; i < CONNECTIONS; ++i) if (data->sockets[i] != -1)
printf("%d ", i); printf("\n"); fflush(stdout); continue;
}
//Если ввели stop X, то прерываем соединение с клиентом X if (strncmp(buffer, "stop", sizeof(char) * 4) == 0) {
int user_id = atoi(buffer + 5); pthread_cancel(data->read_threads[user_id]); close(data->sockets[user_id]); data->sockets[user_id] = -1;
printf("Соединение с клиентом %d прервано\n", user_id); fflush(stdout);
continue;
}
//Если ввели exit, то завершаем работу сервера
if (strncmp(buffer, "exit", sizeof(char) * 4) == 0) { // Освобождаем ресурсы
for (int i = 0; i < CONNECTIONS; ++i) { if (data->sockets[i] != -1) {
pthread_cancel(data->read_threads[i]); close(data->sockets[i]);
}
}
printf("Конец работы\n"); _exit(0);
return NULL;
}
// Если введено @X <message>, то отправляем сообщение пользователю X if (*buffer == '@') {
int user_id;
sscanf(buffer + 1, "%d %[^\n]s", &user_id, temp); strcat(temp, "\n");
if (data->sockets[user_id] == -1) { printf("Клиента %d не существует\n", user_id);
}else if (send(data->sockets[user_id], temp, strlen(temp), 0) <=
0){
pthread_cancel(data->read_threads[user_id]); close(data->sockets[user_id]); data->sockets[user_id] = -1;
printf("Клиент #%d прервал соединение\n", user_id); fflush(stdout);
}
} else { // Иначе отправляем всем
for (int i = 0; i < CONNECTIONS; ++i) { if (data->sockets[i] != -1 &&
send(data->sockets[i], buffer, strlen(buffer), 0) <= 0) { pthread_cancel(data->read_threads[i]); close(data->sockets[i]);
data->sockets[i] = -1;
printf("Клиент #%d прервал соединение\n", i); fflush(stdout);
}
}
3
}
}
}
// Функция для потока чтения void *runReadThread(void *arg) {
ReadThreadData data = *(ReadThreadData *)arg; *data.allow = 1;
char buffer[BUFSIZE]; while (1) {
memset(buffer, '\0', BUFSIZE); // Очистка
if (recv(*data.socket, buffer, BUFSIZE - 1, 0) <= 0) { close(*data.socket);
*data.socket = -1;
printf("\nКлиент #%d прервал соединение\n#> ", data.user_id); fflush(stdout);
return NULL;
}
printf("\nКлиент %d: %s#> ", data.user_id, buffer); fflush(stdout);
}
}
int main() {
struct sockaddr_in address;
int socket_descriptor = 0, option = 1, new_socket_descriptor, address_length = sizeof(address);
// Создание конечной точки соединения
if ((socket_descriptor = socket(AF_INET, SOCK_STREAM, 0)) == 0) { printf("Ошибка создания конечной точки соединения\n"); perror("socket");
return 1;
}
// Установка флагов
if (setsockopt(socket_descriptor, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT,
&option, sizeof(option))) { printf("Ошибка установки флагов на сокете\n"); perror("setsockopt");
return 2;
}
address.sin_family = AF_INET; address.sin_addr.s_addr = INADDR_ANY; address.sin_port = htons(PORT);
// Привязка имени к сокету
if (bind(socket_descriptor, (struct sockaddr *)&address, sizeof(address))
<
0) {
printf("Ошибка привязки имени к сокету\n"); perror("bind");
return 3;
}
//Задание размера очереди в число CONNECTIONS if (listen(socket_descriptor, CONNECTIONS) < 0) {
printf("Ошибка прослушивания соединения на сокете\n"); perror("listen");
return 4;
}
//Настройка потока записи и др.
pthread_t read_threads[CONNECTIONS]; int sockets[CONNECTIONS];
memset(sockets, -1, sizeof(int) * CONNECTIONS); WriteThreadData write_thread_data = {read_threads, sockets}; pthread_t write_thread_descriptor; pthread_create(&write_thread_descriptor, NULL, runWriteThread,
4
&write_thread_data); int user_id = 0;
// Осуществление соединений while (1) {
// Ожидание соединения
if ((new_socket_descriptor =
accept(socket_descriptor, (struct sockaddr *)&address, (socklen_t *)&address_length)) < 0) {
printf("Ошибка принятия соединения на сокете\n"); perror("accept");
return 5;
}
//Поиск в базе клиентов свободного слота for (int i = 0; i < CONNECTIONS; ++i)
if (sockets[i] == -1) { user_id = i; break;
}
sockets[user_id] = new_socket_descriptor; printf("\nНовый клиент #%d присоединился\n#> ", user_id); fflush(stdout);
//Создание потока для чтения без окончания его ожидания int allow = 0;
ReadThreadData data = {user_id, &sockets[user_id], &allow}; pthread_create(&read_threads[user_id], NULL, runReadThread, &data); while (allow == 0)
continue;
}
return 0;
}
Таблица 2 — Файл client.c
#include <arpa/inet.h> #include <pthread.h> #include <stdio.h> #include <string.h> #include <sys/socket.h> #include <unistd.h> #define PORT 8080 #define BUFSIZE 512
//Структура для потоков записи и чтения typedef struct {
int socket;
pthread_t *read_thread, *write_thread; } ThreadsData;
//Функция для потока записи
void *runWriteThread(void *arg) { ThreadsData *data = (ThreadsData *)arg; char buffer[BUFSIZE];
while (1) {
memset(buffer, '\0', BUFSIZE); // Очистка printf("#> ");
fgets(buffer, BUFSIZE, stdin); // Ввод
// Если ввели exit, то завершаем работу клиента
if (strncmp(buffer, "exit", sizeof(char) * 4) == 0) { // Освобождаем ресурсы pthread_cancel(*data->read_thread); close(data->socket);
return NULL;
}
// Отправляем сообщение
if (send(data->socket, buffer, strlen(buffer), 0) <= 0) {
5