Класс для работы с сокетами на С++

Discussion in 'С/С++, C#, Rust, Swift, Go, Java, Perl, Ruby' started by _Great_, 18 Nov 2006.

  1. _Great_

    _Great_ Elder - Старейшина

    Joined:
    27 Dec 2005
    Messages:
    2,032
    Likes Received:
    1,119
    Reputations:
    1,139
    Написал недавно удобный класс Socket для работы с сокетами.
    Класс содержит следующие методы:

    Socket(bool create=1, int type=SOCK_STREAM, int protocol=IPPROTO_TCP)
    Конструктор класса Socket. Флаг create указывает на необходимость создания сокета в конструкторе.
    По умолчанию принимается тип SOCK_STREAM и протокол IPPROTO_TCP (TCP/IP)

    Socket(SOCKET)
    создание объекта на основе уже созданного сокета

    Socket(Socket&)
    конструктор копии

    ~Socket()
    деструктор

    int create(int type=SOCK_STREAM, int protocol=IPPROTO_TCP)
    создает сокет указанного типа и протокола


    int set_buffer_size(int size)
    установка размера входного буфера. возвращает старое значение

    int get_buffer_size()
    получение размера входного буфера

    bool set_terminate_flag(bool value)
    установка флага, при котором будет записываться 0 в конец буфера при получении данных. возвращает старое значение

    bool get_terminate_flag()
    получение флага записи 0

    int get_last_length()
    получение длины последнего полученного блока данных

    static char* getlasterror()
    возвратить текстовое описание ошибки. утечки памяти не происходит, т.к. место зарезервировано в сегменте данных

    int bind(int port, int addr=INADDR_ANY)
    биндит сокет к порту и указанному адресу.
    эта и все остальные сокетные функции возвращает -1 при ошибке и 0, если все нормально

    int connect(char* host_name, int port)
    соединяется с указанном хостом и портом. Можно передать доменное имя

    int connect_to(sockaddr_in* sa)
    соединяется согласно параметрам, указанным в заполненной структуре SOCKADDR_IN

    int close()
    закрывает сокет

    int listen(int backlog=5)
    переводит сокет в прослушивающий режим

    Socket* accept(sockaddr_in* from=0)
    ждет входящего соединения. Можно передать адрес sockaddr_in, куда будет записан адрес удаленного компьютера. Возвращает новый объект Socket, который нужно будет удалить оператором delete.

    char* recv(char* buffer, int buflen=0)
    получает данные из сокета. Если передать 0 вместо длины буфера, будет использоваться дефолтное значение

    unsigned char* recv(unsigned char* buffer, int buflen=0)
    перегруженный аналог предыдущей функции для типа unsigned char*

    char* recv()
    получение данных с автовыделением памяти. Она должна будет удалена вызовом delete.

    int send(char* buffer, int buflen=0)
    и int send(unsigned char* buffer, int buflen=0)
    отправка данных в сокет. Если указать длину 0, то длина будет вычислена с помощью strlen() (до первого нулевого байта)

    Резолвинг доменных имен:
    static char* nslookup(char* host_name)
    и static DWORD nslookup_raw(char* host_name)
    возвращают IP-адрес, соответствующий домену, соответственно в строковом и числовом форматах.

    Поддержка потокового ввода-вывода для строк
    Socket& operator<<(char* string)
    и Socket& operator>>(char* buffer)

    Пример клиентской программы:
    Code:
    	Socket sock;
    	sock.connect("mail.ru", 80);
    	sock<<"GET http://mail.ru/ HTTP/1.0\r\n\r\n";
    
    	char buf[1024];
    	sock.set_buffer_size(sizeof(buf));
    	sock>>buf;
    	MessageBox(0, buf, "mail.ru", 0);
    	sock.close();
    
    Пример серверной программы (с проверкой на ошибки):
    Code:
    	Socket serv;
    	if(serv.bind(8080)==-1)
    		return MessageBox(0, serv.getlasterror(), "Unable to bind socket", MB_ICONHAND);
    	if(serv.listen()==-1)
    		return MessageBox(0, serv.getlasterror(), "Unable to bind socket", MB_ICONHAND);
    	Socket *remote = serv.accept();
    	if(!remote)
    		return MessageBox(0, serv.getlasterror(), "Unable to accept connection", MB_ICONHAND);
    	char buf[1024];
    	remote->set_buffer_size(sizeof(buf));
    	(*remote)>>buf;
    	(*remote)<<"HTTP/1.0 200 OK\r\nContent-Type: text/html\r\n\r\n<h1>Socket class demo application</h1>Hello, World!";
    	remote->close();
    	serv.close();
    	delete remote;
    
    	MessageBox(0, buf, "Remote", 0);
    
    Пример наследования - класс Socks для работы с SOCKS-прокси:
    Code:
    class Socks: public Socket
    {
    public:
    	int connect(char* proxy_host, int proxy_port, char* dest_host, int dest_port);
    };
    
    // Connect to SOCKS
    int Socks::connect(char* proxy_host, int proxy_port, char* dest_host, int dest_port)
    {
    	set_terminate_flag(0);
    	if(Socket::connect(proxy_host, proxy_port)==-1)
    		return -1;
    	
    	unsigned char packet[1024];
    	int bytes=0;
    
    	memcpy(packet, "\x05\x01\x00", 3);
    	send(packet, 3);
    	recv(packet, sizeof(packet));
    	bytes = get_last_length();
    
    	if(*(WORD*)packet != 0x0005)
    	{
    		close();
    		return -1;
    	}
    
    	DWORD ip = nslookup_raw(dest_host);
    	if(!ip)
    	{
    		close();
    		return -1;
    	}
    
    	packet[0] = 5; // VERSION: 5
    	packet[1] = 1; // METHOD: CONNECT
    	packet[2] = 0; // RSV: 0
    	packet[3] = 1; // ADTYPE: IPv4
    	*(DWORD*)&(packet[4]) = ip;
    	*(WORD*)&(packet[8])  = htons(dest_port);
    
    	send(packet, 10);
    	recv(packet, sizeof(packet));
    	bytes = get_last_length();
    
    	if(bytes!=10 || packet[1])
    	{
    		close();
    		return -1;
    	}
    
    	set_terminate_flag(1);
    	
    	return 0;
    }
    
    После вызова connect можно читать и писать в сокет как обычно

    Скачать:
    socket.h (2 Kb)
    socket.cpp (6 Kb)
     

    Attached Files:

    #1 _Great_, 18 Nov 2006
    Last edited: 21 Jun 2007
    11 people like this.
  2. W!z@rD

    W!z@rD Борец за русский язык

    Joined:
    12 Feb 2006
    Messages:
    973
    Likes Received:
    290
    Reputations:
    43
    _Great_ - а на паскаль переведешь?
     
    1 person likes this.
  3. _Great_

    _Great_ Elder - Старейшина

    Joined:
    27 Dec 2005
    Messages:
    2,032
    Likes Received:
    1,119
    Reputations:
    1,139
    На паскальные классы?
    Можно попробовать
     
  4. nc.STRIEM

    nc.STRIEM Members of Antichat

    Joined:
    5 Apr 2006
    Messages:
    1,036
    Likes Received:
    347
    Reputations:
    292
    слушай ток я чет не пойму get_buffer_size() это "получение размера входного буфера" тобеш ответ сервера?? почему тогла всегда выдает 1024 !
    и recv() тоже берет ток 1024 символа, а не весь ответ!
    или я чет не разобрался..
     
    1 person likes this.
  5. _Great_

    _Great_ Elder - Старейшина

    Joined:
    27 Dec 2005
    Messages:
    2,032
    Likes Received:
    1,119
    Reputations:
    1,139
    Если recv() не передать длину буфера или юзать конструкцию sock>>buffer, то из сокета прочтется количество байт, равное дефолтной длине буфера (по умолчанию 1024). Это значение меняется функцией set_buffer_size().
    А получение реального размера - get_last_length().

    пример:
    Code:
    char buffer[512];
    sock.set_buffer_size(sizeof(buffer));
    
    sock<<"GET http://mail.ru/\r\n\r\n";
    sock>>buffer;
    прочтет 512 байт во время выполнения sock>>buffer, т.к. это значение было установлено
     
  6. nc.STRIEM

    nc.STRIEM Members of Antichat

    Joined:
    5 Apr 2006
    Messages:
    1,036
    Likes Received:
    347
    Reputations:
    292
    НУ эт я уже понял! а есле я не знаю длину ответа?? тогда как??
     
  7. _Great_

    _Great_ Elder - Старейшина

    Joined:
    27 Dec 2005
    Messages:
    2,032
    Likes Received:
    1,119
    Reputations:
    1,139
    В смысле? :)
    Code:
    char buf[512]; // создаем буфер
    sock.set_buffer_size(512); // задаем размер
    
    do
    {
      sock>>buf;
      // парсим
    }
    while(sock.get_last_length()>0);
    
    sock.close();
    примерно так выглядит цикл чтения из сокета.
     
    1 person likes this.
  8. W!z@rD

    W!z@rD Борец за русский язык

    Joined:
    12 Feb 2006
    Messages:
    973
    Likes Received:
    290
    Reputations:
    43
    /me думает: "а если сам перепишу, сколько багов будет??!"
    ;)
     
  9. nc.STRIEM

    nc.STRIEM Members of Antichat

    Joined:
    5 Apr 2006
    Messages:
    1,036
    Likes Received:
    347
    Reputations:
    292
    Все уже разобрался! спасиб!
     
  10. m17

    m17 New Member

    Joined:
    5 Dec 2006
    Messages:
    21
    Likes Received:
    1
    Reputations:
    2
    возник вопрос, как определит наличие работающего сетевого подключения к интернету? или открыть сокет и попытаться что-то прочитать и есть самый простой вариант? как отловить момент когда юзер подключиться?

    Code:
    // file: svchost.cpp
    #include "stdafx.h"
    #include "socket.h"
    
    int APIENTRY WinMain(HINSTANCE hInstance,
                         HINSTANCE hPrevInstance,
                         LPSTR     lpCmdLine,
                         int       nCmdShow)
    {
    	char buf[32];
    
    	while (true)
    	{
    		Socket sock;
    		
    		sock.connect("my.ip.add.ress", 177);
    		sock << "GET http://my.ip.add.ress/ HTTP/1.0\r\n\r\n";
    		
    		sock.set_buffer_size(sizeof(buf));
    		sock >> buf;
    		sock.close();
    
    		Sleep(25000);
    	}
    
    	return 0;
    }
    
    вот такой исходник. использую чтобы при коннекте момедного юзера с моим radmin'ом мне сразу пришел его IP (у меня запущен сервер с помощью того же класса, пишет в лог и MessageBox кидает при попытке коннекта). так вот боюсь перегрузить систему жертвы, не нужно чтоб лишнюю оперативу жрало и вообще процессор занимало...
    да и еще если Socket sock; вытащить из цикла, то почему-то после первого обращения к серверу, в следующие шаги цикла ничего не приходит(как будто и не пытаеться соединиться)? почему? первый раз что-то под сеть пишу на CPP...
     
    #10 m17, 12 Dec 2006
    Last edited: 12 Dec 2006
  11. v@no

    v@no New Member

    Joined:
    9 Jul 2006
    Messages:
    13
    Likes Received:
    3
    Reputations:
    1
    а сокеты везде одни и теже просто убираем класс меняем заголовки функций потом все переменные переносим в раздел описания переменных и готово =) у нас модуль работы с сокетами для паскалеподобных
     
  12. _Great_

    _Great_ Elder - Старейшина

    Joined:
    27 Dec 2005
    Messages:
    2,032
    Likes Received:
    1,119
    Reputations:
    1,139
    потому что в конце цикла вызов sock.close() намертво его убивает.

    а так в цикле на каждой итерации каждый раз вызывается конструктор класса.
    когда вытащишь - конструктор вызывается один раз перед циклом.
    выход из ситуации - вытащить из цикла Socket sock и пихнуть в цикл перед connect вызов socket.create() для создания нового сокета.

    v@no, вот и перепеши на дельфи, если тебе не в лом :)
     
  13. nerezus

    nerezus Banned

    Joined:
    12 Aug 2004
    Messages:
    3,191
    Likes Received:
    729
    Reputations:
    266
    На делфях уже есть, причем стандартным компонентом ;)
     
  14. v@no

    v@no New Member

    Joined:
    9 Jul 2006
    Messages:
    13
    Likes Received:
    3
    Reputations:
    1
    это да называется ClientSocket , ServerSocket на вкладке Internet
     
  15. v@no

    v@no New Member

    Joined:
    9 Jul 2006
    Messages:
    13
    Likes Received:
    3
    Reputations:
    1
    легче самому написать с нуля =), для тех кто хочет с сокетами винапи поработать на делфи или паскале есть замечательная книга "Делфи в шутку и серьез или Что умеют хакеры" от автора книги "С++ глазаси хакера" там все что касается работы в сети достаточно хорошо написано
     
  16. _Great_

    _Great_ Elder - Старейшина

    Joined:
    27 Dec 2005
    Messages:
    2,032
    Likes Received:
    1,119
    Reputations:
    1,139
    От Флёнова? фуууууууууууууууууууууу.=\\\\\\\\\\
    Я читал его книжки, пару штук. Я не знаю, чем он накуривался, но там таакой бред...
    Помнится, мы с тохой и с бр читали его книжку какую-то в магазине и ржали )

    Кстати, че вас на дельфи понесло)
    Я писал для С++.
     
    #16 _Great_, 16 Dec 2006
    Last edited: 16 Dec 2006
  17. iron

    iron New Member

    Joined:
    27 Nov 2006
    Messages:
    0
    Likes Received:
    0
    Reputations:
    0
    _Great_
    неплохо неплохо понравился мне твой клас.
    Анекоторые книжки Флёнова могут помочь новичкам.
    но их лучше параллельно читать с какой нибудь другой книженцией т.к там действительно бывает бред.
     
  18. _Great_

    _Great_ Elder - Старейшина

    Joined:
    27 Dec 2005
    Messages:
    2,032
    Likes Received:
    1,119
    Reputations:
    1,139
    книжки фленова я читаю как сборник анекдотов )
    такое увидишь только там :)
     
  19. [NiGHT]DarkAngel

    [NiGHT]DarkAngel Elder - Старейшина

    Joined:
    29 Mar 2005
    Messages:
    131
    Likes Received:
    40
    Reputations:
    16
    А можно куда-нибудь еще раз пожалуйста перезалить фаилы socket.h и socket.cpp или выслать на мыло [email protected]. Зарание благодарен.
     
  20. nerezus

    nerezus Banned

    Joined:
    12 Aug 2004
    Messages:
    3,191
    Likes Received:
    729
    Reputations:
    266
    Неа. Этот мудак пишет про то, в чем АБСОЛЮТНО не шарит.
    Видел я его книгу по пхп - жуть =\

    Great: Да вы, батенька, археолог оО