QNX и TCP/IP c SOCKETS

Сопрягали мы недавно в кораблике одну РЛС со своей системой управления по TCP/IP через сокеты, система управления — клиент, РЛС тоже клиент, а сопряжение 2 сервера. Сопрячь надо было блок обработки сигналов и устройство где оператор тырцает кнопки и смотрит кто нас хочет завалить, нас это кораблик то есть, но да не об этом сейчас, а о сервере и клиенте…

Любые наши программы проходят проверку, и чтобы проверить наш сервер необходимо было написать 2 клиентов для двух серверов, клиенты забирали и передавали на сервера информацию, при этом в любой момент времени сервера или клиенты могли перезагружаться и должны были подконнектиться снова и продолжить работать, с этим и могут возникнуть проблемы.

В интернете немало примеров серверов и клиентов для socket протоколов, но полностью и правильно работающих да и еще и под QNX 6.3.2 мы не видели. Приведем пример для следующего поколения программистов этого кода (сервера практически одинаковые приведем один), будем использовать стандартные BSD sockets:

/*
******************************************************************
* file RMO1US.c
* brief Главный файл проекта связи с неведомой супер штукой
*
* В данном файле содержатся функции для работы
* с протоколом
*
* Copyright @ 2008-2010 NTC "RIF", jsc
******************************************************************
*/

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include "BOSMessages.h"

int sFd[2];// 2 сокета
int newFd; 

pthread_t	readFromUsThr_id;
void * readFromUSTHr(void *);

int main(int argc, char *argv[]) {

struct sockaddr_in serverAddr;
int sockAddrSize; 

unsigned char buffUSask[1024]; // типа временный буфер

sockAddrSize = sizeof (struct sockaddr_in);
bzero ((char *) &serverAddr, sockAddrSize);
serverAddr.sin_family = AF_INET;
serverAddr.sin_len = (u_char) sockAddrSize;
serverAddr.sin_port = htons (SERVER_BOS_PORT_NUM);
serverAddr.sin_addr.s_addr = htonl (INADDR_ANY);// вообще с любого адреса канектим в этот порт

if ((sFd[0] = socket (AF_INET, SOCK_STREAM, 0)) == -1)
{
perror ("socket");
return (-1);
}
	setsockopt(sFd[0], SOL_SOCKET,SO_KEEPALIVE, NULL, 0);// супер настройка аж всего через 2 часа сервер увидит что соединение погибло))

if (bind (sFd[0], (struct sockaddr *) &serverAddr, sockAddrSize) == -1)
{
perror ("bind");
close (sFd[0]);
return (-1);
}

if (listen (sFd[0], SERVER_MAX_CONNECTIONS) == -1)
{
perror ("listen");
close (sFd[0]);
return (-1);
}
while (1)
{
if ((newFd = accept (sFd[0], (struct sockaddr *) &clientAddr,&sockAddrSize)) == -1)
// пока хоть какой нибудь клиент не приконнектится код висит на этой строчке
{
perror ("accept");
close (sFd[0]);
return (-1);
}
// завалить старый поток и создать его заново, допишем потом 

// создадим поток чтения всякого
if (pthread_create(&readFromUsThr_id, NULL,&readFromUSTHr, NULL) == -1) {
		perror ("pthread_create()");
		return (-1);
	}

while (1)
{
	// будем посылать ответы
	 	buffUSask[0]=CODOGRAMM_ZAPROS_MODE;
		if (send(newFd,buffUSask,SIZEOF_CODOGRAMM_ZAPROS_MODE,0==0)
                          break; 
// ... всякие другие секретные кодограммы 
// ... проверка других условий выхода из отправки 
}
}
return 0;
}
 /**********************************************  
* Функция вызываемая для чтения того что накидал клиент  
*/ 
void * readFromUSTHr(void * arg) 
{
 	unsigned char buffUSask[1024];
 	unsigned char buffSend[1024];
 	while (1) 
	if (recv(newFd,&buffUSask[0],1,0);0)
	{
		switch (buffUSask[0])
		{
		case OPERATOR_DIRECTIVE:
		recv(newFd,&buffSend,SIZEOF_OPERATOR_DIRECTIVE-1,0);
		default:
		break;
		}
	}
}

Это был упрощенный, но работающий код сервера, без проверок на разрыв соединения, их вы можете добавить сами или спросить у нас как.

А вот клиент к нему, тоже простенький:

/*
******************************************************************
* file RMO1ImitatorClient.c
* brief Главный файл проекта связи с
*
* В данном файле содержатся функции для работы
* с протоколом
*
* Copyright @ 2008-2010 NTC "RIF", jsc
******************************************************************
*/

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include "BOSMessages.h"

int sFd[2];// 2 сокета
pthread_t	readFromUsThr_id;
void * readFromUSTHr(void *);

int main(int argc, char *argv[]) {

unsigned char buffSend[1024];

struct sockaddr_in serverAddr;
int sockAddrSize; 

sockAddrSize = sizeof (struct sockaddr_in);
bzero ((char *) &serverAddr, sockAddrSize);
serverAddr.sin_family = AF_INET;
serverAddr.sin_len = (u_char) sockAddrSize;
serverAddr.sin_port = htons (SERVER_BOS_PORT_NUM);// порт сервера
serverAddr.sin_addr.s_addr = inet_addr(RMO1USADDR);
// айпи адрес сервера "192.168.четатам.четатам"
 while (1)
   {

if ((sFd[0] = socket (AF_INET, SOCK_STREAM, 0)) == -1)
{
perror ("socket");
return (-1);
}
setsockopt(sFd[0], SOL_SOCKET,SO_KEEPALIVE, NULL, 0);

  if (connect(sFd[0], (struct sockaddr *)&serverAddr, sockAddrSize) != -1)
    {
    	break; // не удался коннект

    }
    else
    {
    	close(sFd[0]);
    }
    delay(2000); 
// если сервер вдруг не загружен, то без этой задержки клиент  
// не подключится к серверу, даже, если тот загрузится вскоре
}
// поток чтение из сервера
   if (pthread_create(&readFromUsThr_id, NULL,&readFromUSTHr, NULL) == -1) {
		perror ("pthread_create()");
		return (-1);
	}

// что нибудь шлем серверу
	while (1)
	{
		buffSend[0]=OPERATOR_DIRECTIVE;
		send(sFd[0],&buffSend,SIZEOF_OPERATOR_DIRECTIVE,0);
// другие мегакоманды

             }

	return (0);
}

/***********************************************************
 *
 * Функция поток для чтения того чего отвечает сервер
 */

void * readFromUSTHr(void * arg)
{
	unsigned char buffUSask[1024];
	while (1)
	if (recv(sFd[0],&buffUSask[0],1,0);0)
	{
		switch (buffUSask[0])
		{
		case CODOGRAMM_TESTFAZAVR:
		recv(sFd[0],buffUSask+1,SIZEOF_CODOGRAMM_TESTFAZAVR-1,0);
		break;
// другие мегакоманды
		default:
		break;
		}
	}
}

Это были отлаженые куски сервера и клиента на 2 компьютерах, которые вы можете использовать в своих приложениях, измените команды на свои и все заработает, как надо вам.
Для использования этоко кода в Windowse или других ОС необходимо использовать библиотеку sockets2, если это не POSIX система(только виндовс не поддерживает сокеты BSD), и изменить вызовы создания потоков (они разные во всех ОС).
Будем рады, если кому-нибудь поможет.

Похожий бред:

QNX и TCP/IP c SOCKETS: 9 комментариев

  1. Инет местами штука богопротивная…
    Под нейтрину аналогичное списывал из книжек по сетевому обуздательству для Linux.

  2. Какой ужасный код.
    Accept может возвращать -1 по нормальным причинам , например израсходованы все файловый дескрипторы, пришел сигнал. Нельзя после этого падать.
    Неплохо бы для listen сокета установить SO_REUSEADDR.
    Дальше вообще мрак.
    Зачем вам здесь многопоточность да еще криво написанная, если сервер поддерживает только один коннект. Зачем создавать себе проблемы на пустом месте.
    Кроме того TCP/IP — это потоковый протокол, данные могут рваться. Нужно всегда анализировать, то что вернул send и recv.
    Может это и будет работать, но очень нестабильно, с одним клиентом, с малым кол-вом данных и с хорошей связью.

  3. Что же делать если дескрипторы кончились, как не упасть? Можно конечно лампочками поморгать, попищать)) На кораблике то?
    Про send recv я написал добавьте сами, кому надо.
    Даже эта не боевая программа будет работать вполне стабильно))
    По поводу многопоточности надо нам и в чем кривота ее? Обоснуйте.
    Кому ужасный напишите краше и покажите свй мега код.

  4. Работа асинхронная поэтому пример сервера и клиента разнесен по потокам, про SIGPIPE мы, конечно, слышали, но видеть чтобы приложение по нему вылетало, не видели.В рабочем коде анализ EPIPE стоит, но при любых извращениях с двумя нашими отладочными бордами нам его словить не удалось. Кстати на клиенте под WinXP а сервере на VxWorks тоже не прилетало…
    Про SO_REUSEADDR юные и не очень втыкатели могут почитать хелп нужен он им или нет.

  5. Вероятно у вас трафик не очень плотный, если клиент закроет соединение в момент передачи сервером, то сервер получит SIGPIPE. Как и любой сигнал без назначенного обработчика этот приложение положит. А если сработает обработчик, то send вернёт -1 с EPIPE в errno. Ну а дальше только адекватно обработать.

    Сам тоже во всю магию епайпа не вникал, но виндовый .NET клиент у нас при закрытии почти всегда таковой вызывает

  6. По специфике приложения закрытия соединения, как такового, с помошью спец функций неожидается, а сама система долго думает прежде чем понять, что связь оборвалась, за это время мы сами все прибиваем, поэтому, наверное, этих SIGPIPE и не приходит. В любом случае замечание ценное, для тех кто захочет скопипастеть этот кусок.

  7. О как ! Век живи век учись ! Не знал, что так можно с потоковыми сокетами — один и тоже дескриптор в разных потоках читать/писать. Пасибки вам ! (осталось проверить эту идею в четыре-двадцатьпять, где всё не совсем так, как казалось должно быть)

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Protected by WP Anti Spam