- •Практичне заняття 10
- •Сокети (sockets) в unix і основи роботи з ними.
- •Системні виклики sendto() і recvfrom()
- •Визначення ip-адрес для обчислювального комплексу
- •Приклад програми udp-клієнта
- •Приклад програми udp-серверу
- •Організація зв'язку між процесами за допомогою установки логічного з'єднання
- •Встановлення логічного з'єднання. Системний виклик connect()
- •Приклад програми tcp-клієнта
- •Як відбувається встановлення віртуального з'єднання
- •Системний виклик listen()
- •Системний виклик accept()
- •Приклад простого tcp-серверу
- •Вживання інтерфейсу мережних викликів для інших сімейств протоколів. Unix Domain протоколи. Файли типу "сокет"
Системний виклик accept()
Системний виклик accept() дозволяє серверу одержати інформацію про повністю встановлені з'єднання. Якщо черга повністю встановлених з'єднань не порожня, то він повертає дескриптор для першого приєднаного сокета в цій черзі, одночасно видаляючи його з черги. Якщо черга порожня, то виклик чекає появи повністю встановленого з'єднання. Системний виклик також дозволяє серверу взнати повну адресу клієнта, що встановив з'єднання. У виклику є три параметри: дескриптор слухаючого сокета, через який очікується встановлення з'єднання; покажчик на структуру, в яку при необхідності буде занесена повна адреса сокета клієнта, що встановив з'єднання; покажчик на цілу змінну, що містить максимально допустиму довжину цієї адреси. Як і у разі виклику recvfrom(), останній параметр є тим, що модернізується, а якщо нас не цікавить, хто з нами з'єднався, то замість другого і третього параметрів можна вказати значення NULL.
Системний виклик accept()
Прототип системного виклику
#include <sys/types.h>
#include <sys/socket.h>
int accept(int sockd
struct sockaddr *cliaddr
int *clilen);
Опис системного виклику
Системний виклик асcept використовується сервером, орієнтованим на встановлення зв'язку шляхом віртуального з'єднання, для прийому повністю встановленого з'єднання.
Параметр sockd є дескриптором створеного і набудованого сокета, попереднього переведеного в пасивний (слухаючий) режим за допомогою системного виклику listen().
Системний виклик асcept вимагає попередньої настройки адреси сокета за допомогою системного виклику bind().
Параметр cliaddr служить для отримання адреси клієнта, що встановив логічне з'єднання, і повинен містити покажчик на структуру, в яку буде занесена ця адреса.
Параметр clilen містить покажчик на цілу змінну, яка після повернення з виклику міститиме фактичну довжину адреси клієнта. Помітимо, що перед викликом ця змінна повинна містити максимально допустиме значення такої довжини. Якщо параметр cliaddr має значення NULL, то і параметр clilen може мати значення NULL.
Значення, що повертається
Системний виклик повертає при нормальному завершенні дескриптор приєднаного сокета, створеного при встановленні з'єднання для подальшого спілкування клієнта і серверу, і значення -1 при виникненні помилки.
Приклад простого tcp-серверу
Розглянемо програму 15–16-4.c, реалізовуючу простий TCP-сервер для сервісу echo.
/* Приклад простого TCP-серверу для сервісу echo */
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
#include <unistd.h>
void main()
{
int sockfd, newsockfd; /* Дескриптори для слухаючого і приєднаного сокетів */
int clilen; /* Довжина адреси клієнта */
int n; /* Кількість прийнятих символів */
char line[1000]; /* Буфер для прийому інформації */
struct sockaddr_in servaddr, cliaddr; /* Структури для розміщення повних адрес серверу і клієнта */
/* Створюємо TCP-сокет */
if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0){
реrror(NULL);
exit(1);
}
/* Заповнюємо структуру для адреси серверу: сімейство протоколів TCP/IP, мережний інтерфейс – будь-хто, номер порту 51000. Оскільки в структурі міститься додаткове не потрібне нам поле, яке повинне бути нульовим, зануляємо її всю перед заповненням */
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family= AF_INET;
servaddr.sin_port= htons(51000);
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
/* Настроюємо адресу сокета */
if(bind(sockfd (struct sockaddr *) &servaddr
sizeof(servaddr)) < 0){
реrror(NULL);
close(sockfd);
exit(1);
}
/* Переводимо створений сокет в пасивний (слухаючий) стан. Глибину черги для встановлених з'єднань описуємо значенням 5 */
if(listen(sockfd, 5)< 0){
реrror(NULL);
close(sockfd);
exit(1);
}
/* Основний цикл серверу */
while(1){
/* В змінну clilen заносимо максимальну довжину очікуваної адреси клієнта */
clilen = sizeof(cliaddr);
/* Чекаємо повністю встановленого з'єднання на слухаючому сокеті. При нормальному завершенні у нас в структурі cliaddr лежатиме повний адреса клієнта, що встановив з'єднання, а в змінної clilen – його фактична довжина. Виклик же поверне дескриптор приєднаного сокета, через який відбуватиметься спілкування з клієнтом. Помітимо, що інформація про клієнта у нас в подальшому ніяк не використовується, тому замість другого і третього параметрів можна було поставити значення NULL. */
if((newsockfd = accept(sockfd
(struct sockaddr *) &cliaddr &clilen)) < 0){
реrror(NULL);
close(sockfd);
exit(1);
}
/* В циклі приймаємо інформацію від клієнта до тих пір, поки не відбудеться помилки (виклик read() поверне негативне значення) або клієнт не закриє з'єднання (виклик read() поверне значення 0). Максимальну довжину однієї порції даних від клієнта обмежимо 999 символами. В операціях читання і запису користуємося дескриптором приєднаного сокета, тобто значенням, яке повернув виклик accept().*/
while((n = read(newsockfd, line, 999)) > 0){
/* Прийняті дані відправляємо назад */
if((n = write(newsockfd, line
strlen(line)+1)) < 0){
реrror(NULL);
close(sockfd);
close(newsockfd);
exit(1);
}
}
/* Якщо при читанні виникла помилка – завершуємо роботу */
if(n < 0){
реrror(NULL);
close(sockfd);
close(newsockfd);
exit(1);
}
/* Закриваємо дескриптор приєднаного сокета і
йдемо чекати нового з'єднання */
close(newsockfd);
}
}
Лістинг 15-16.4. Програма 15–16-4.c . Приклад простого TCP-серверу для сервісу echo.
Наберіть і відкомпілюйте програму. Запустіть її на виконання. Модифікуйте текст програми TCP-клієнта (программа 15–16-3.c), замінивши номер порту з 7 на 51000. Запустіть клієнта з іншого віртуального терміналу або з іншого комп'ютера і переконаєтеся, що клієнт і сервер взаємодіють коректно.
