LINUX.ORG.RU

Типовое решение при работе с сокетами

 ,


1

2

Есть в коде функция:

result = recvfrom(iSocket, array, BUF_SIZE, 0, (struct sockaddr *)&sipx, (socklen_t*)&len_addr) ;		
В результате ее работы в массиве array оказывается пакет размером BUF_SIZE. Есть ли какое-то стандартное решение при работе с этой функцией, чтобы постоянно принимать пакеты, да еще не все подряд а, отфильтрованные по значению в одном из элементов массива. Я пытался вот такой подход использовать:
do
   result = recvfrom(iSocket, array, BUF_SIZE, 0, (struct sockaddr *)&sipx, (socklen_t*)&len_addr) ;
while ( array[8] == 42 ) ;// принимать пакет пока в array не окажется то, что нужно
Устраивало, до тех пор, пока не потребовалось еще одному процессу читать через этот сокет.



Последнее исправление: podovalov5 (всего исправлений: 1)
Ответ на: комментарий от pathfinder

Как ты себе представляешь работу с одним сокетом и толпой клиентов без потоков/процессов?

Единственный вариант: заставлять клиентов ждать, пока обслуживаешь очередного. Если у тебя время обслуги — минут 10, а клиентов тысяча, то это будет жесть!

Eddy_Em ☆☆☆☆☆
()
Ответ на: комментарий от Eddy_Em

Как ты себе представляешь работу с одним сокетом и толпой клиентов без потоков/процессов?

Легко и непринуждённо.

Единственный вариант: заставлять клиентов ждать

Тебе не надо юлить - тебе надо запилить его гуано - там ничего не надо ждать.

Если у тебя время обслуги — минут 10

Пример обслуги на 10минут.

а клиентов тысяча, то это будет жесть!

У тебя? Тысяча? Куллстори.

anonymous
()
Ответ на: комментарий от Eddy_Em

Если у тебя время обслуги — минут 10, а клиентов тысяча, то это будет жесть!

В таких случаях обычно «обслуга» отправляется в отдельный пул потоков на обработку, а клиенты потом вовремя получают статус выполнения (опять же, можно асинхронно)

yoghurt ★★★★★
()
Ответ на: комментарий от yoghurt

Нет, я не могу понять, как можно более чем с одним клиентом работать на одном потоке!

Вот, допустим, я открываю сокет. Его одновременно открывают три клиента (вообще, это один клиент, но открываем с разными ключами: один канал изображения гонит, другой принимает и обрабатывает управляющие команды, третий передает клиенту сообщения об ошибках).

Ну и как я в этот сокет что-нибудь напишу, чтобы оно именно нужному клиенту пришло? Хранить пул дескрипторов? А теперь представь, что у тебя одновременно толпа народу может к этому сокету обратиться. В итоге у тебя получится бешеный пул дескрипторов и ты в реальном времени вынужден будешь выдирать из пула те, в которые видео писать, писать туда видео; выдирать нужные командные и обрабатывать полученное оттуда...

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

Eddy_Em ☆☆☆☆☆
()
Ответ на: комментарий от Eddy_Em

А в многонитевом приложении ты как клиенты различаешь? Пойми ты, многонитевое приложение и конечный автомат для обработки нескольких соединений это одно и то же, просто когда есть нити, тебе этого автомата не видно, его ядро и/или библиотека поддержки нитей скрывают.

anonymous
()
Ответ на: комментарий от Eddy_Em

Ну так а на кой черт себе выдумывать лишний геморрой, если можно переложить это на ядро?

Многопоточные приложения отлаживать на порядок сложнее.

pathfinder ★★★★
()
Ответ на: комментарий от Eddy_Em

Ох, выкладываю файлы

main.cpp

#include <stdio.h>
#include <vector>
#include <signal.h>

#include <zCommon/zException.hpp>
#include <zCommon/zEnviroment.hpp>
#include <zCommon/zUtils.hpp>

#include <zIO/zMainLoop.hpp>
#include <zIO/zIOStreamNormal.hpp>
#include <zIO/_zSocketServer.hpp>
#include <zIO/zUnixSocketHelper.hpp>

#include "MyServer.hpp"

using namespace zCommon;
using namespace zIO;
using namespace std;


int main(int argc,char** argv)
{
	zEnviroment::Init(); //Инициализация некоторых сущностей, ничего интересного
	
	zMainLoop loop; //Основной цикл обработки сообщений
	
	MyServer* server = MyServer::Create(1234); //Создаем обработчик сервера
	loop.Handler_Add(server); //Добавляем обработчик в цикл обработки сообщений
	
	loop.Run(); //Запускаем цикл обработки сообщений, в недрах этого метода будет вызываться epoll()

	return 0;
}

pathfinder ★★★★
()
Ответ на: комментарий от pathfinder

файл MyServer.hpp

#pragma once

#include <zIO/_zTCPSocketServer.hpp>

/*
 * Класс описывет сервер
 */
class MyServer : public zIO::_zTCPSocketServer
{
public:
	void server_Open(); //Метод вызывается при создании сервера
	void server_Close(); //Метод вызывается при закрытии сервера

	/*
	 * Этот метод вызывается каждый раз, когда кто-то устанавливает соединение.
	 * Метод должен вернуть обработчик отдельного соединения
	 */
	zIO::_zTCPSocketServer::local_Session* server_CreateSession(struct sockaddr_in& addr);
	void server_Think();

	static MyServer* Create(int port);
};
pathfinder ★★★★
()
Ответ на: комментарий от pathfinder

файл MyServer.cpp

#include "MyServer.hpp"
#include "MyServerSession.hpp"

#include <iostream>

using namespace zIO;
using namespace zCommon;
using namespace std;

void MyServer::server_Open()
{
	cout << "MyServer open" << endl;
}
void MyServer::server_Close()
{
	cout << "MyServer close" << endl;
}
zIO::_zTCPSocketServer::local_Session* MyServer::server_CreateSession(struct sockaddr_in& addr)
{
	return new MyServerSession();
}
void MyServer::server_Think()
{

}
MyServer* MyServer::Create(int port)
{
	MyServer* server = new MyServer;
	server->SetAddr(port);
	return server;
}
pathfinder ★★★★
()
Ответ на: комментарий от pathfinder

файл MyServerSession.hpp

#pragma once

#include <zIO/_zTCPSocketServer.hpp>
#include <zCommon/zTime.hpp>

/*
 * Класс отдельного соединения
 */
class MyServerSession : public zIO::_zTCPSocketServerSess
{
private:
	zCommon::zTime t0; //Метка времени последней активности
public:
	virtual void stream_BeginStream(); //Вызывается, когда соединение установлено и готово для обмена
	virtual void stream_EndStream(); //Вызывается, когда соединение разорвалось
	virtual void stream_Recv(); //Вызывается каждый раз, когда приходят какие-нибудь данные
	virtual void stream_Think(); //Вызывается каждые 100 милисекунд, нужно для таймаутов
	virtual void stream_IOError(int error_source,int error_num); //Вызывается при сбоях

private:
	void SendFrame(char r); //В этом методе мы послаем ответный фрейм с XOR-кодом
};
pathfinder ★★★★
()
Ответ на: комментарий от pathfinder

Файл MyServerSession.cpp

#include "MyServerSession.hpp"
#include <zCommon/zCursor.hpp>
#include <iostream>

using namespace std;
using namespace zCommon;
using namespace zIO;


void MyServerSession::stream_BeginStream()
{
	t0.SetNow();
	cout << "session: begin stream" << endl;
}
void MyServerSession::stream_EndStream()
{
	cout << "session: end stream" << endl;
}
void MyServerSession::stream_Recv()
{
	while(true) //Зачем нужен цикл? Теоретически, мы можем получить два фрейма за одно пробуждение
	{
		/*
		 * inq - это циклический буффер, содержащий принятые байты (да, ещё одна обертка)
		 * Если данных в буфере меньше трех байт, то выходим и ждем получения
		 * остальных байт
		 */
		if(inq.GetDataSize()<3)
			break;
		
		uint8_t sig=0;
		uint8_t func=0;
		uint8_t frame_size=0;
		char data[256];

		/*
		 * Данные можно доставать прямиком из циклического буфера inq,
		 * но мы будем использовать курсор буфера.
		 * Курсор полезен, когда для того чтобы понять, пришел ли весь фрейм
		 * надо достать часть данных из буфера. Если фрейм полностью пока ещё
		 * не пришел, то операция отменяется и из циклического буфера как бы и ничего
		 * и не доставали.
		 * В данном случае нас интересует байт frame_size с размером. Без него мы не
		 * узнаем каких размеров весь фрейм
		 */
		zCursor c(&inq);
		c.Pop_UI8(sig);
		c.Pop_UI8(func);
		c.Pop_UI8(frame_size);

		/*
		 * Проверяем на ошибки
		 */
		if(sig!=0xAF)
		{
			cout << "Invalid frame " << endl;
			this->CloseForced();
			return;
		}
		if(func!=1)
		{
			cout << "Unknown function " << (int)func << endl;
			this->CloseForced();
			return;
		}
		if(frame_size==0)
		{
			cout << "Invalid frame " << endl;
			this->CloseForced();
			return;
		}

		c.Pop_CharArray(data,frame_size); //Достаем последние данные
		c.ApplyChanges(); //Достали весь кадр, теперь фиксируем изменения в циклическом буфере

		char r = data[0];
		for(int i=1;i<frame_size;i++)
			r^=data[i]; //Считаем XOR

		SendFrame(r); //Отсылаем фрейм назад
		t0.SetNow(); //устанавливаем метку времени последней активности
	}
}
void MyServerSession::stream_Think()
{
	zTime now;
	now.SetNow();
	if(now.isElapsed(t0,10000))
	{
		this->CloseForced(); //Десять секунд прошло, никакой активности не было, закрываем
		return;
	}
}
void MyServerSession::stream_IOError(int error_source,int error_num)
{
	cout << "session: io error " << error_num << endl;
}

void MyServerSession::SendFrame(char r)
{
	uint8_t sig=0xAF;
	uint8_t func=2;
	uint8_t frame_size=1;
	Write_Begin();
	outq.Push_UI8(sig);
	outq.Push_UI8(func);
	outq.Push_UI8(frame_size);
	outq.Push_CharArray(&r,1);
	Write_End();
}

pathfinder ★★★★
()
Ответ на: комментарий от pathfinder

А сфигали у тебя С++? На сях договаривались!

Ну, я тоже так могу:

int main()
  int fd = open_socket(1234);
  socket_add_handler(fd, server);
  socket_process(fd);
  return 0;
}

Eddy_Em ☆☆☆☆☆
()
Ответ на: комментарий от Eddy_Em

На сях договаривались!

Кто договаривался? Я ни слова про Си не сказал. Пиши хоть на брейнфаке, главное чтобы было компактнее, понятнее, красивее и при этом была напрямую работа с системным вызовом select()/poll()/epoll() без всяких архитектурных абстракций, прослоек, оберток.

pathfinder ★★★★
()
Ответ на: комментарий от Eddy_Em

В общем, тебе уже поздно сочинять отмазки, ты согласился. Я привел свой код. Теперь ты выложешь то, что ты там собирался изначально выкладывать.

pathfinder ★★★★
()
Ответ на: комментарий от pathfinder

Хорошо. Напомни в воскресенье вечером. Что-нибудь на коленке сварганю, чтобы обслуживать N клиентов в N потоков. Скажем, тупое эхо.

Eddy_Em ☆☆☆☆☆
()
Ответ на: комментарий от Eddy_Em

Что-нибудь на коленке сварганю, чтобы обслуживать N клиентов в N потоков. Скажем, тупое эхо.

Неее, код должен отражать всю ту логику, что в задании приведена. Указана структура фрейма и логика работы сервера. Ну и ещё про 10 секундный таймаут не забудь.

pathfinder ★★★★
()
Ответ на: комментарий от Eddy_Em

Только я в твоем коде обещанного epoll не вижу.

Правильно, epoll() скрыт в недрах класса zMainLoop. В том то и смысл оберток, что они скрывают реализацию. Если бы ты видел epoll(), то это была бы уже прямая работа с системным вызовом. Такая задача стоит у тебя.

pathfinder ★★★★
()
Ответ на: комментарий от pathfinder

Я хотел показать, как много можно скрыть за обертками. И это очень сильно облегчает труд программиста.

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

Написали один раз такую библиотек-обертку или взяли готовую типа Boost::Asio, а потом пишем только специфичный код для конкретной задачи. Удобно, ты концентрируешься только на прикладной логике, а не захламляешь текст программы кусками кода, которые повторяются (примерно), от одного сервера к другому.

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

pathfinder ★★★★
()
Ответ на: комментарий от pathfinder

Иди нафиг! Я тогда тоже скрою все самое интересное и покажу тупо вершину.

Либо весь код, либо никакого.

Eddy_Em ☆☆☆☆☆
()
Ответ на: комментарий от pathfinder

Я приведу то, что мне понадобилось бы написать, для реализации такого сервера имея сделанные ранее наработки по оберткам

Можно мне увидеть эти самый наработки? Можно мне рабочий код?

anonymous
()
Ответ на: комментарий от anonymous

Все украдено до нас! Очень напоминает обсуждение проблемы обслуживания 10000 клиентов. Вот классическая статья http://www.kegel.com/c10k.html. Там рассмотрены разные стратегии решения. (кстати статье уже 15 лет). Там есть ссылки на примеры реализации и на С и на С++ и на Java

sigurd ★★★★★
()
Ответ на: комментарий от sigurd

Мне надо ни мистический с10к, ни какой либо другой, а надо именно его код. Тем более с10к протух лет 10назад, ибо сейчас о чем-то кроме с500к активных соединений даже говорить не стоит.

anonymous
()
Ответ на: комментарий от anonymous

Можно мне увидеть эти самый наработки? Можно мне рабочий код?

Не, я не хочу выкладывать. Там слишком много всего. Код явно не рассчитан на обучение и выкладывание и я считаю этот код своей интеллектуальной собственностью.

Если тема интересна, я может накатаю упрощенный вариант библиотеки, где все будет проще и понятнее и оформлю это в виде отдельного треда. Но обещать не могу. Я ленивый.

pathfinder ★★★★
()
Ответ на: комментарий от pathfinder

Не, я не хочу выкладывать.

Зря.

Там слишком много всего.

В этом и суть.

Код явно не рассчитан на обучение

Сомневаюсь, что есть хоть какой-то шанс, что твоей код меня может чему-то научить.

я считаю этот код своей интеллектуальной собственностью

Что из этого следует?

Если тема интересна, я может накатаю упрощенный вариант библиотеки, где все будет проще и понятнее и оформлю это в виде отдельного треда. Но обещать не могу. Я ленивый.

Ты мне хотябы wс -l выкати своих портянок.

Т.е. берёшь свою «интеллектуальную» собственность и считаешь в ней кол-во строк. Выкатываешь результат. Всё просто. Или это тоже «„интеллектуальную“ собственность»?

anonymous
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.