Контакты

Для связи с нами можно использовать:
irc://irc.chatnet.ru:#gfs
icq://546460
email://cobalt[@]gfs-team.ru

Все материалы предоставлены только с ознакомительной целью
ГлавнаяСтатьиКодингРабота с сокетами в UNIX
© s7r34m 04.04.2006

Сокеты - это одно из средств коммуникации между процессами. Главное отличие сокетов от других средств межпроцессного взаимодействия, таких как пайпы и очереди сообщений, состоит в том, что обменивающимся информацией процессам не обязательно находится на одном компьютере. В этой статье я постараюсь объяснить принципы работы с сокетами, и написать простые TCP клиент и сервер, которые после добавления необходимых функций могут быть использованы в реальных проектах.

Итак, алгоритм работы клиента:
socket() - создание сокета
connect() - его привязка к адресу и порту, установка соединения
write() - посылка запроса
read() - прием ответа
close() - закрытие сокета

int socket(int domain, int type, int protocol);

Параметр domain определяет семейство протоколов, которое будет использоваться для взаимодействия (список возможных значений можно посмотреть в bits/socket.h). Мы будем использовать семейство протоколов IP, поэтому положим этот параметр равным AF_INET. Второй параметр определяет тип сокета, т.е будет ли передаваться информация дейтаграммами с гарантией или без гарантии доставки, с установкой логического соединения, или еще как-нибудь. В рамках семейства IP протоколов реализованы передача дейтаграмм без гарантии доставки (протокол UDP) и надежная передача информации с установкой логического соединения (протокол TCP). Именно его мы и будем использовать. Для этого в качестве type укажем константу SOCK_STREAM. Третий параметр служит для указания конкретного протокола для данного типа сокета. Мы укажем в качестве этого параметра 0, т.к этот протокол единственный. В случае успешного завершения возвращается неотрицательное число - дескриптор, в случае неудачи возвращается -1.

int connect(int sockfd, struct sockaddr *serv_addr, socklen_t addrlen);

sockfd - дескриптор сокета, т.е. число которое возвратил системный вызов socket().
*serv_addr - указатель на структуру, содержащую информацию о сокете.
addrlen - размер этой структуры.
В случае успеха connect возвращает 0, а в случае неудачи -1.

Структура sockaddr выглядит следующим образом

struct sockaddr {
    unsigned short sa_family;
    char sa_data[14];
};

Для семейства AF_INET эта структура переопределена как

struct sockaddr_in{
    short sin_family;           //семейство протоколов – AF_INET
    unsigned short sin_port;    //номер порта
    struct in_addr sin_addr;    //адрес
    char sin_zero[8];           //не используется, должно быть заполнено нулями
};

Номер порта нужно задавать, используя сетевой порядок байт. Для преобразования между обычным и сетевым порядками служат функции htons и ntohs. Для преобразования адреса из символьного представления (127.0.0.1) в числовое служит функция inet_ntoa, обратная функция - inet_aton.

Для записи в сокет и чтения из него используются функции write и read, точно так же как и при работе с файлами.

int close(int sockfd);

Этот системный вызов закрывает дескриптор сокета, тем самым, завершая соединение.

Теперь мы можем написать простой TCP клиент.

#define IP "127.0.0.1"
#define PORT 123
#define BUFLEN 1024

#include <sys>
#include <sys>
#include <netinet>
#include <arpa>
#include <string.h>
#include <stdio.h>
#include <errno.h>
#include <unistd.h>

int main()
{
    int sockfd;
    int num;
    char buffer[BUFLEN];
    struct sockaddr_in servaddr;
    
    //Обнуляем буффер
    bzero(buffer,BUFLEN);

    //Создаем сокет
    if((sockfd=socket(AF_INET,SOCK_STREAM,0))<0)
    {
        perror("socket");
        exit(1);
    }

    //Заполняем структуру sockaddr
    bzero(&servaddr,sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_port = htons(PORT);
    inet_aton(IP, &servaddr.sin_addr);

    //Пытаемся приконнектится
    if(connect(sockfd,(struct sockaddr*)&servaddr,sizeof(servaddr))<0)
    {
        perror("connect");
        close(sockfd);
        exit(1);
    }

    //Запрашиваем строку у пользователя и посылаем ее серверу
    fgets(buffer,BUFLEN,stdin);
    if((num=write(sockfd,buffer,strlen(buffer)+1))<0)
    {
        perror("write");
        close(sockfd);
        exit(1);
    }

    //Получаем и печатаем ответ
    if((num=read(sockfd,buffer,BUFLEN-1))<0)
    {
        perror("Can\'t read\n");
        close(sockfd);
        exit(1);
    }
    printf("%s",buffer);
    
    //Закрываем соединение
    close(sockfd);

    return 0;
}

Теперь приступим к написанию нашего сервера.
Алгоритм его работы:
socket() - создание сокета
bind() - привязка сокета к адресу
listen() - перевод сокета в пассивный режим
accept() - прием полностью установленного соединения
read() - чтение запроса
write() - посылка ответа
close() - закрытие сокета

int bind(int sockfd, struct sockaddr *my_addr, int addrlen);

С аргументами все очевидно. Возвращает 0 в случае успешного завершения, отрицательное число в противном случае. Для клиента вызов bind не требуется т.к. привязка сокета к адресу производится функцией connect.

int listen(int sockfd, int backlog);

Переводит сокет в слушающее состояние и устанавливает максимальный размер очереди соединений - параметр backlog.

int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

Выбирает первый запрос на соединение из очереди. Записывает информацию о подключившемся клиенте в структуру *addr, и ее размер в *addrlen.

Код сервера:

#define BUFLEN 1024
#define PORT 123

#include <sys>
#include <sys>
#include <netinet>
#include <arpa>
#include <string.h>
#include <stdio.h>
#include <errno.h>
#include <unistd.h>

int main()
{
    int sockfd,newsockfd;
    int clilen;
    int n;
    char buffer[BUFLEN];
    struct sockaddr_in servaddr,cliaddr;

    //Создаем сокет
    if((sockfd=socket(AF_INET,SOCK_STREAM,0))<0)
    {
        perror("socket");
        exit(1);
    }
    
    //Заполняем структуру
    servaddr.sin_family= AF_INET;
    servaddr.sin_port=htons(PORT);
    servaddr.sin_addr.s_addr=htonl(INADDR_ANY); //Принимаем соединения с
    любого адреса

    //Биндим сокет к адресу
    if(bind(sockfd, (struct sockaddr *) &servaddr, 
    sizeof(servaddr)) < 0){
        perror("bind");
        close(sockfd);
        exit(1);
    }
    //Переводим сокет в слушающее состояние
    if(listen(sockfd, 5) < 0){
        perror("listen");
        close(sockfd);
        exit(1);
    }
    
    //Принимаем соединение
    clilen=sizeof(cliaddr);
    if((newsockfd=accept(sockfd,(struct sockaddr*)&cliaddr,&clilen))<0)
    {
        perror("accept");
        close(sockfd);
        exit(1);
    }
    
    printf("Client %s connected\n",inet_ntoa(cliaddr.sin_addr));
        
    while((n = read(newsockfd,buffer,BUFLEN-1))>0)
    {
        //Отправляем обратно принятые данные
        if((n=write(newsockfd,buffer,strlen(buffer)+1))<0)
        {
            perror("write");
            close(sockfd);
            close(newsockfd);
            exit(1);
        }
    }
    
    if(n < 0) //При чтении произошла ошибка
    {
        perror("read");
        close(sockfd);
        close(newsockfd);
        exit(1);
    }
        

    close(newsockfd);
    close(sockfd);

    return 0;    
}

Приведенные программы можно усовершенствовать. Например, сделать чтобы параметры задавались из командной строки; адреса можно было бы резолвить, используя функцию gethostbyname; сервер, используя функцию fork, мог параллельно обслуживать несколько клиентов. Более подробное описание всех использованных функций можно прочесть в манах.




© s7r34m 04.04.2006

e-Commerce Partners Network
Ник:

Текст:
P Br B I Qute



Код: обновить
Последние комментарии
26.06.2017 07:33:17 DeweyAloma написал:
If you have a desire to learn how to earn from...
XSS Часть II
26.06.2017 07:09:03 DeweyAloma написал:
If you have a desire to learn how to earn from...
Взлом приложений WEB 2.0 из Firefox
26.06.2017 04:42:47 DeweyAloma написал:
If you have a desire to learn how to earn from...
HTTP. Описание протокола
Реклама

Тут должна была быть ваша реклама, но мы потеряли глиняную табличку с ее текстом. SapeId: 665044

Rambler's Top100