![]() |
[для новичков]сетевое программирование на си под unix
intro
Я не писатель а кодер. так что я в принципе знаю что с точки зрения творчества моя статья не шедевр. там могут быть разные неточности, ошибки итд итп. ну и поскольку я не русски то на русском я не умею очень четко излагать свои мысли. если будут хоть какие то предложения по поводу улучшения статьи я с радостью их приму и постараюсь сделать статью читабельной. сейчас я напишу про сервер. про клиент напишу несколько позднее (скорее всего через 1-2 дня). в разделе клиента напишу не только как создать простой консольный клиент, но и познакомлю читателя с библиотекой gtk+. потом мы напишем графический клиент к нашему серверу и все будет хорошо ((* <--------------------cut here--------------------> сервер желательно иметь некоторые знания в области программирования под си ибо самые самые основы (дескриптор файла, функции ввода вывода итп) я тут описивать не буду. ну из названия статьи само собой понятно что нужно иметь опыт общение с ОС UNIX для начала давайте решим что будет делать наш сервер. пусть когда к нему подключаются он выводит какой нить текст. "My daemonic greetings" например. ну и наш сервер не был бы настоящим если бы не был демоном. ну отсюда приблизительно понятно алгоритм действий нашего сервера: 1. становится демоном 2. открывает порт 3. слушает порт 4. принимает соединение 5. обрабатываем соединение 6. закрывает соединение с клиентом 7. возвращяется к пункту 4 начнем с пункта 1. как стать демоном? очень просто. при помощи системного вызова fork(2). Код:
pid_t fork(void);дочерний процесс имеет свой pid. дочернему процессу передаются все файловые дескрипторы родительского процесса. подробнее читать на man страницах. как открыть порт? вот тут уже не все так просто. для начала нужно создать сокет. сокет это специальный тип файла в который записивают свои сообщения клиент и сервер. тоесть когда наш сервер напишет "My daemonic greetings" то он напишет это не на компютер клиента а в сокет, потом клиент прочитает наше сообщение из сокета и выведет его на свой стандтартный вывод. сокет создается при помощи системного вызова socket: Код:
int socket(int domain, int type, int protocol)type - тип сокета. на даный момент поддерживаются SOCK_STREAM, SOCK_DGRAM, SOCK_RAW, SOCK_RDM, SOCK_SEQPACKET. SOCK_STREAM используется для протокола tcp, SOCK_DGRAM для протокола udp. с другими сокетами честно говоря не общялся и не знаю для чего они. кому интересно может погуглить. подробнее о типе сокета SOCK_STREAM. этот тип обеспечивает последовательный, надежный, ориентированный на установление двусторонней связи поток байтов. функция возвращяет дескриптор сокета. мы создали сокет. но он не привязан ни к ip адресу компютера ни к какому либо порту. для того чтобы привязать сокет мы воспользуемся системным вызовом bind Код:
int bind(int s, const struct sockaddr *addr, socklen_t addrlen)s - это дескриптор сокета который был создан вызовом socket addr - структура типа sockaddr (об этом ниже) addrlen - размер структуры ну с дескриптором сокета все понятно. а вот структура... различные функции для работы с сокетами предполагают использование адреса (указатель, в терминологии языка С) маленького участка памяти. различные объявления из файла sys/socket.h в качестве этого указателя используют адрес struct sockaddr. когда адресным семейством является AF_INET мы можем использовать struct sockaddr_in, определённую в netinet/in.h вместо sockaddr. структура sockaddr_in имеет поля: Код:
struct sockaddr_in {sin_port - порт который будет прослушиваться sin_addr - наш ip адрес чтобы привязать сокет к порту и адресу мы сначала должны заполнить структуру а затем дать ее вызову bind. как прослушивать порт? при помощи функции listen Код:
int listen(int s, int backlog)backlog - максимальное количество запросов на установление связи, которые могут стоять в очереди, ожидая обработки сервером. как принимать соединение? при помощи функции accept Код:
int accept(int s, struct sockaddr * restrict addr, socklen_t * restrict addrlen)addr - структура которая будет заполнена данными клиента (ip адрес клиента, порт с которого установлен соединение итп) addrlen - размер addr как обрабатывать соединение? при помощи функций read и write (можно также и recv и send но я этим не пользуюсь считая их ересью, я предпочитаю пользоватся функциями ((*) Код:
ssize_t read(int d, void *buf, size_t nbytes)buf - то что мы будем читать/записивать из/в файл (сокет) nbytes - сколько будем читать/записивать из/в файл(сокет) они (функции) возвращяют количество прочитаных/записаных байтов как закрывать соединение? функция close Код:
int close(int d)сейчас я приведу листинг сервера с коментариями. если будет что то не ясное спрашиваете я объясню. листинг сервера Код:
#include <stdio.h> //printf() |
клиент
клиент
теперь напишем консольную версию нашего клиента к нашему серваку. итак. определим что будет делать наш клиент. на стороне клиента все организовать очень очень легко. нужно сделать вот что. 1. задать параметры подключения 2. подключится к серверу 3. получить сообщение 4. закрыть соединение с сервером как задать параметры подключения? помните когда мы объявляли структуру типа sockaddr_in для сервера? там мы задавали параметры сервера. тобишь на каком айпишнике он будет, на каком порте будет работать... для клиента мы тоже должны объявить структуру. и там будут параметры подключения. на какой порт подключаться (поле sin_port), ip адрес сервера (поле sin_addr.s_addr), а поле sin_family остается тоже (AF_INET). думаю нет надобности объяснять почему. как подключится к серверу? функция connect(2). Код:
int connect(int s, const struct sockaddr *name, socklen_t namelen);как получить сообщения? функции read(2) и write(2). их я также описал в предыдущей статье. как закрыть соединение с сервером? a функция close(2). см. статью "сервер" <---------------------cut here------------------------> ну вот собственно и все. клиента мы описали. ниже приведу листинг клиента. Код:
#include <stdio.h> //printf()потом юзать функцию gethostbyname(3). а потом скопировать содержимое поля h_addr_list[0] структуры he в поле sin_addr структуры типа sockaddr_in. вот листинг этого участка кода: Код:
struct hostent *he; |
теперь немножко о копирах.
статью писал сам. примеры привел те на которых сам учился. когда было совсем туго (не мог нормально сформулировать мыслю) использовал "FreeBSD Developers' Handbook". брал и нагло переводил ((* графический клиент напишем через 1-2 дня (тема на самом деле очень большая) |
несколько раз пробовал написать статью. ничего не получалось... ну не могу я статьи писать ((*
поэтому я решил делать так. я приведу листинг кода с подробными коментами. если че нибудь будет не понятно стучитесь в асю попробую объяснить. листинг: Код:
#include <sys/socket.h>gcc -o выходной_файл входной_файл `gtk-config --cflags --libs` |
неплохая статья , сделай ещё цикл видео по теме будет большим плюсом
|
а можно по подробнее? мне все равно делать нечего, чем нить хоть бы займусь ((*
|
За старание + , но не ново - rsdn.ru/article/unix/sockets.xml
А ещё лучше - forum.antichat.ru/thread59197.html - Сетевой боевой софт |
не вижу никакого смысла в подобных статьях.. за знаниями такого рода лучше обращаться к основательно и толково написанным книгам..
|
Не вижу ничего плохого, что чел сел и разобрался с сетевым кодингом в никсах. Тока вот заметка по статейке - fork(2) не делает приложение демоном (как написано в каментах к коду), fork(2) порождает дочерний процесс
Цитата:
|
А на чём писать этот код? точнее на каком Компиляторе? имеется ввиду если писать под Linux
|
| Время: 20:16 |