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

Смотреть узбекский видео

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

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

dementiy 23.01.2010 19:45

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-&gt;sin_family = AF_INET;
server-&gt;sin_port = htons(echo_port);
server-&gt;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-&gt;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
43
#include <stdio.h>
#include <arpa/inet.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-&gt;sin_family = AF_INET;
server-&gt;sin_port = htons(echo_port);
server-&gt;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-&gt;sin_addr),
ntohs(client-&gt;sin_port));
printf("Numeric: %u\n", ntohl(client-&gt;sin_addr.s_addr));
while(1) {
bytes = read(clientfd, (void*)buf, 1000);
if (bytes &lt;= 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>
__SOCKADDR_COMMON_SIZE -
sizeof (in_port_t) -
sizeof (struct in_addr)>;
};</sizeof>


/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 &lt; 0)
goto out;
retval = sock_map_fd(sock, flags & (O_CLOEXEC | O_NONBLOCK));
if (retval &lt; 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 (и тут).

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


Тэги: kernel sockets
+ 11 -
Похожие Поделиться

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

P.S. Спасибо за статьи, очень интересно, с нетерпением жду следующей))
P.P.S. Какие бумажные книжки стоит почитать по теме программирования под Linux? (Что-то по теме этой статьи)
dementiy 29.01.2010 12:59 #
+ 0 -
Еще статьи обязательно будут. На счет книг. Если интересует разработка сетевых приложений, то есть книга Стивенса, которая так и называется "Unix. Разработка сетевых приложений". Если что-то по программированию в Linux вообще, то Джонсон и Троан "Разработка приложений в среде Linux". А если проявляете интерес к ядру, то на моей памяти есть всего три книги, но покупать ни одну из них не советую, так как не очень хороший перевод.
cyrus 01.12.2010 23:27 #
+ 0 -
Для работы функции inet_ntoa() нужно дописать в server.c
#include <arpa/inet.h>
Тогда сервер не будет сегфолтиться.

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

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


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

Online video HD

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

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

Full HD video online

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

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

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