Видео смотреть бесплатно

Смотреть гиг видео

Официальный сайт forexnw 24/7/365

Смотреть видео бесплатно

23.01.10 19:45 dementiy

CodingСоздание сокетов в Linux

Wikipedia гласит, что сокет - «название программного интерфейса для обеспечения обмена данными между процессами. Процессы при таком обмене могут исполняться как на одной ЭВМ, так и на различных ЭВМ, связанных между собой сетью. Сокет — абстрактный объект, представляющий конечную точку соединения.».

Рассмотрим простой пример применения сокетов:
client.c:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
        #include <stdio.h>
        #include <netinet/in.h>
        #include <sys/types.h>
        #include <string.h>
        #include <stdlib.h>
        int main(int argc, char *argv) {
                char* echo_host = "127.0.0.1";
                int echo_port = 7777;
                int sockfd;
                struct sockaddr_in *server =
                        (struct sockaddr_in*)malloc(sizeof(struct sockaddr_in));
                server->sin_family = AF_INET;
                server->sin_port = htons(echo_port);
                server->sin_addr.s_addr = inet_addr(echo_host);
                sockfd = socket(AF_INET, SOCK_STREAM, 0);
                printf("Connecting to %s \n", echo_host);
                printf("Numeric: %u\n", server->sin_addr.s_addr);
                connect(sockfd, (struct sockaddr*)server, sizeof(*server));
                char* msg = "Yeah, it's works!";
                printf("\nSend: ’%s’\n", msg);
                write(sockfd, msg, strlen(msg));
                char* buf = (char*)malloc(1000);
                int bytes = read(sockfd, (void*)buf, 1000);
                printf("\nBytes received: %u\n", bytes);
                printf("Text: ’%s’\n", buf);
                close(sockfd);
                return 0;
        }

server.c:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
        #include <stdio.h>
        #include <netinet/in.h>
        #include <sys/types.h>
        #include <string.h>
        #include <stdlib.h>
        int main(int argc, char *argv) {
                char* echo_host = "127.0.0.1";
                int echo_port = 7777;
                int sockfd;
                struct sockaddr_in *server =
                        (struct sockaddr_in*)malloc(sizeof(struct sockaddr_in));
                server->sin_family = AF_INET;
                server->sin_port = htons(echo_port);
                server->sin_addr.s_addr = inet_addr(echo_host);
                sockfd = socket(AF_INET, SOCK_STREAM, 0);
                if (bind(sockfd, (struct sockaddr*)server, sizeof(*server))) {
                        printf("bind failed\n");
                }
                listen(sockfd, SOMAXCONN);
                int clientfd;
                struct sockaddr_in* client =
                (struct sockaddr_in*)malloc(sizeof(struct sockaddr_in));
                int client_size = sizeof(*client);
                char* buf = (char*)malloc(1000);
                int bytes;
                printf("Wait for connection to port %u\n", echo_port);
                clientfd = accept(sockfd, (struct sockaddr*)client, &client_size);
                printf("Connected to %s:%u\n\n", inet_ntoa(client->sin_addr),
                                    ntohs(client->sin_port));
                printf("Numeric: %u\n", ntohl(client->sin_addr.s_addr));
                while(1) {
                        bytes = read(clientfd, (void*)buf, 1000);
                        if (bytes <= 0) {
                                close(clientfd);
                                printf("Connection closed.\n");
                                exit(0);
                        }
                printf("Bytes received: %u\n", bytes);
                printf("Text: ’%s’\n", buf);
                write(clientfd, buf, bytes);
                }
        }

Итак рассмотрим, что представляет из себя структура struct sockaddr_in. Данная структура определена следующим образом:

/usr/include/netinet/in.h:
1
2
3
4
5
6
7
8
9
10
        struct sockaddr_in
        {
                __SOCKADDR_COMMON (sin_);
                in_port_t sin_port;
                struct in_addr sin_addr;
                unsigned char sin_zero[sizeof (struct sockaddr) -
                           __SOCKADDR_COMMON_SIZE -
                           sizeof (in_port_t) -
                           sizeof (struct in_addr)];
        };

/usr/include/bits/sockaddr.h:
1
2
3
4
5
        ...
        typedef unsigned short int sa_family_t;
        #define __SOCKADDR_COMMON(sa_prefix) \
                        sa_family_t sa_prefix##family
        ...

Полю sin_family присваивается значение AF_INET, что соответствует протоколу TCP/IP версии 4 (AF_INET6 – версия 6), sin_port и sin_addr номер порта и адрес сервера соответственно. Данные этой структуры используются при установлении соединения. После заполнения структуры происходит вызов функции socket():

/usr/include/sys/socket.h:
1
2
3
4
5
6
        /*
         * Create a new socket of type TYPE in domain DOMAIN, using
         * protocol PROTOCOL. If PROTOCOL is zero, one is chosen automatically.
         * Returns a file descriptor for the new socket, or -1 for errors.
         */

        extern int socket(int __domain, int __type, int __protocol) __THROW;

Итак, как видно из комментария, данная функция создает новый сокет и при успешном выполнении возвращает дескриптор на созданный сокет, при возникновении ошибки возвращает значение меньше 0 (аналогично работе функции open()). Теперь рассмотрим, что происходит после ее вызова в ядре.
Запускаем клиентскую часть, как и ранее (Механизмы создания процессов в Linux) выполняя трассировку программы:

1
2
3
4
5
6
7
8
9
10
11
        $ ltrace -S ./client
        ...
        socket(2, 1, 0 <unfinished ...>
        SYS_socketcall(1, 0xbfca5f40, 0xb80bcff4, 0x8048720, 0x80484f0) = 3
        <... socket resumed> )
        ...

        $ strace ./client
        ...
        socket(PF_INET, SOCK_STREAM, IPPROTO_IP) = 3
        ...

Примечание: strace раскрывает «непонятные» цифры 2, 1, 0.

Как видно происходит системный вызов sys_socketcall:

net/socket.c:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
        asmlinkage long sys_socketcall(int call, unsigned long __user *args)
        {
                ...
                switch (call) {
                case SYS_SOCKET:
                        err = sys_socket(a0, a1, a[2]);
                        break;
                ...
                default:
                        err = -EINVAL;
                        break;
                }
                return err;
        }

Данная функция представляет собой в некотором роде «свитч» для системных вызовов. В переменной call устанавливается номер требуемой функции. Данные номера определены в файле include/linux/net.h:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
        ...
        #define SYS_SOCKET      1               /* sys_socket(2)        */
        #define SYS_BIND        2               /* sys_bind(2)          */
        #define SYS_CONNECT     3               /* sys_connect(2)       */
        #define SYS_LISTEN      4               /* sys_listen(2)        */
        #define SYS_ACCEPT      5               /* sys_accept(2)        */
        #define SYS_GETSOCKNAME 6               /* sys_getsockname(2)   */
        #define SYS_GETPEERNAME 7               /* sys_getpeername(2)   */
        #define SYS_SOCKETPAIR  8               /* sys_socketpair(2)    */
        #define SYS_SEND        9               /* sys_send(2)          */
        #define SYS_RECV        10              /* sys_recv(2)          */
        #define SYS_SENDTO      11              /* sys_sendto(2)        */
        #define SYS_RECVFROM    12              /* sys_recvfrom(2)      */
        #define SYS_SHUTDOWN    13              /* sys_shutdown(2)      */
        #define SYS_SETSOCKOPT  14              /* sys_setsockopt(2)    */
        #define SYS_GETSOCKOPT  15              /* sys_getsockopt(2)    */
        #define SYS_SENDMSG     16              /* sys_sendmsg(2)       */
        #define SYS_RECVMSG     17              /* sys_recvmsg(2)       */
        #define SYS_ACCEPT4     18              /* sys_accept4(2)       */
        ...

Как видно на данный момент (версия ядра 2.6.28) их всего 18, что соответствует количеству операторов case в sys_socketcall. В нашем случае в переменной call находится значение равное 1, что соответствует вызову SYS_SOCKET, то есть созданию сокета (при установлении соединения, как нетрудно догадаться, это значение будет соответствовать 3 — SYS_CONNECT).
Как выяснилось происходит вызов системного вызова sys_socket() с передачей трех аргументов, которые мы задали в функции soket() (о параметрах можно почитать на страницах справочного руководства man 2 socket или на сайте opennet). Функция sys_socket() определена в файле net/socket.c:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
        asmlinkage long sys_socket(int family, int type, int protocol)
        {
                int retval;
                struct socket *sock;
                ...
                retval = sock_create(family, type, protocol, &sock);
                if (retval < 0)
                        goto out;
                retval = sock_map_fd(sock, flags & (O_CLOEXEC | O_NONBLOCK));
                if (retval < 0)
                        goto out_release;
        out:
                /* It may be already another descriptor 8) Not kernel problem. */
                return retval;
        out_release:
                sock_release(sock);
                        return retval;
        }

Значение retval является тем самым дескриптором, который возвращает пользовательская функция soket(). Структура struct socket, описывающая сокет, определена следующим образом:

include/linux/net.h:
1
2
3
4
5
6
7
8
9
10
        struct socket {
                socket_state            state;
                short                   type;
                unsigned long           flags;
                const struct proto_ops  *ops;
                struct fasync_struct    *fasync_list;
                struct file             *file;
                struct sock             *sk;
                wait_queue_head_t       wait;
        };

Итак в функции sys_soсket() происходит вызов sock_create(), в которой заполняется структура struct soсket и выделяется память под сокет (sock_alloc). Функция sock_map_fd() связывает созданный сокет с дескриптором (тот, который мы используем в пользовательском приложении).
Таким образом выстраивается следующая (обобщенная) схема создания сокета:

soket() → sys_socketcall() → sys_socket() → sock_create() → sock_alloc()

P.S. Во-первых может возникнуть вопрос зачем приводится код клиента и сервера, когда для данной статьи можно было
обойтись всего лишь конструкцией вида:
1
2
3
4
#include <...>
void main() {
        int sd = soket(...);
}

Да можно было поступить и так, но мне показалось, что если статья не будет интересной (надеюсь это не так), то вы сможете
поиграться хотя бы с программой.
Во-вторых. Рассмотрено конечно же не все и не так подробно, как хотелось бы, но надеюсь, что для дальнейшего самостоятельного изучения этого достаточно.
И последнее, вот pdf (и тут).

Желаю успехов!


Теги:

zitryss 23.01.10 21:22 # +0
А можно pdf залить ещё куда-нибудь?
P.S. За статью респект!
dementiy 23.01.10 21:41 # +0
Сделано. А есть какие-то проблемы?
zitryss 23.01.10 22:29 # +0
Спасибо. Просто, нельзя было скачать без регистрации.
kurtis99 28.01.10 23:27 # +0
В server.c пришлось дописать объявление функции char *inet_ntoa(struct in_addr in). Без этого компилятор упорно думал что функция возвращает int, и при установке соединения, сервер падал. В принципе это логично, т.к. в С, если не описана функция, то считается что она возвращает int, но вроде подключены нужные заголовки, так что хз))

P.S. Спасибо за статьи, очень интересно, с нетерпением жду следующей))
P.P.S. Какие бумажные книжки стоит почитать по теме программирования под Linux? (Что-то по теме этой статьи)
dementiy 29.01.10 12:59 # +0
Еще статьи обязательно будут. На счет книг. Если интересует разработка сетевых приложений, то есть книга Стивенса, которая так и называется "Unix. Разработка сетевых приложений". Если что-то по программированию в Linux вообще, то Джонсон и Троан "Разработка приложений в среде Linux". А если проявляете интерес к ядру, то на моей памяти есть всего три книги, но покупать ни одну из них не советую, так как не очень хороший перевод.

Посты Комментарии
Последние посты
    Посты Комментарии
    Последние комментарии
      Посты Комментарии
      Изменения
        Посты Комментарии Изменения Черновики Избранное
        Черновики (все)
          Посты Комментарии Изменения Черновики Избранное
          Избранное (всё)
            Посты Комментарии Изменения Черновики Избранное
            Лучшие блоги (все 141)
            Топ пользователей Топ блогов
            Топ пользователей Топ блогов
            Элита (все 2825 из 215 городов)
            Топ пользователей Топ блогов
            welinux.ru
            лучшие казино Видеопокеры и кено бесплатно тут

            Смотреть видео онлайн

            Онлайн видео бесплатно


            Смотреть русское с разговорами видео

            Online video HD

            Видео скачать на телефон

            Русские фильмы бесплатно

            Full HD video online

            Смотреть видео онлайн

            Смотреть HD видео бесплатно

            School смотреть онлайн