LINUX.ORG.RU

Клиент-сервер (консольный чат) на сокетах

 , ,


1

2

Недавно начал пытаться писать программы в сторону сетевого программирования, прочитал пару статей, почитал о сокетах. В итоге могу написать чат между сервером и клиентом, но это касается только локального адреса (клиент и сервер на одной машине). Как написать клиент так, чтобы он мог подключиться к серверу, зная его глобальный ip (т.е. онлайн сервер). Задавал серверу и клиенту один и тот же глобальный ip адрес (т.к. на одной машине пока тестирую), порт можно указать любой, выше 1024, как я понял (и чтобы другая программа его не использовала). В итоге сервер запускается с заданным ip адресом и переходит в режим прослушивания, но клиент не хочет коннектиться к серверу.

Клиент:

#include <sys/socket.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

struct sockaddr_in client;

int main()
{
    int sock = socket(AF_INET, SOCK_STREAM, 0);
    if (sock == -1)
    {
        printf("error: invalid socket\n");
        return -1;
    }

    client.sin_addr.s_addr = inet_addr("94.180.93.163");//INADDR_ANY;
    client.sin_port = htons(1337);
    client.sin_family = AF_INET;

    printf("wait for the connect...\n");
    if (connect(sock, (struct sockaddr *)&client, sizeof(client)) == -1)
    {
        printf("error: failed connection\n");
        return -1;
    }
    else
        printf("connected\n");

    close(sock);
    return 0;
}

Сервер:


#include <sys/socket.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <stdlib.h>

struct sockaddr_in server, client;

int main()
{
    int sock = socket(AF_INET, SOCK_STREAM, 0);
    if (sock == -1)
    {
        printf("error: invalid socket (server)\n");
        return -1;
    }

    server.sin_addr.s_addr = inet_addr("94.180.93.163");//INADDR_ANY;
    server.sin_port = htons(1337);
    server.sin_family = AF_INET;

    if (bind(sock, (struct sockaddr *)&server, sizeof(server)) == -1)
    {
        printf("error: failed connection\n");
        return -1;
    }
    else
        printf("connected\n");

    listen(sock, 5);

    char *addr_server = inet_ntoa(server.sin_addr);
    int port_server = ntohs(server.sin_port);
    printf("ip-address of server: %s:%d\n", addr_server, port_server);

    int sock_client;
    int c = sizeof(struct sockaddr_in);
    char s_size_message[5];
    while (1)
    {
        sock_client = accept(sock, (struct sockaddr *)&client, (socklen_t *)&c);
        char *addr_client = inet_ntoa(client.sin_addr);
        int port_client = ntohs(client.sin_port);

        if (sock_client == -1)
        {
            //printf("error: invalid socket (client)\n");
            //return -1;
        }
        else
        {
            printf("connected with client\n");
            printf("ip-address of client: %s:%d\n", addr_client, port_client);
            //recv(sock_client, s_size_message, 3, 0);
            //printf("%s\n", s_size_message);
        }
    }

    return 0;
}

Как написать клиент так, чтобы он мог подключиться к серверу, зная его глобальный ip

Использовать DNS

Клиен, завалялся в закромах, не помню уже откуда

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>

void error(const char *msg)
{
    perror(msg);
    exit(0);
}

int main(int argc, char *argv[])
{
    int sockfd, portno, n;
    struct sockaddr_in serv_addr;
    struct hostent *server;

    char buffer[256];
    if (argc < 3) {
       fprintf(stderr,"usage %s hostname port\n", argv[0]);
       exit(0);
    }
    portno = atoi(argv[2]);
    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd < 0) 
        error("ERROR opening socket");
    server = gethostbyname(argv[1]);
    if (server == NULL) {
        fprintf(stderr,"ERROR, no such host\n");
        exit(0);
    }
    bzero((char *) &serv_addr, sizeof(serv_addr));
    serv_addr.sin_family = AF_INET;
    bcopy((char *)server->h_addr, 
         (char *)&serv_addr.sin_addr.s_addr,
         server->h_length);
    serv_addr.sin_port = htons(portno);
    if (connect(sockfd,(struct sockaddr *) &serv_addr,sizeof(serv_addr)) < 0) 
        error("ERROR connecting");
    printf("Please enter the message: ");
    bzero(buffer,256);
    fgets(buffer,255,stdin);
    n = write(sockfd,buffer,strlen(buffer));
    if (n < 0) 
         error("ERROR writing to socket");
    bzero(buffer,256);
    n = read(sockfd,buffer,255);
    if (n < 0) 
         error("ERROR reading from socket");
    printf("%s\n",buffer);
    close(sockfd);
    return 0;

Dron ★★★★★
()
Последнее исправление: Dron (всего исправлений: 2)

зная его глобальный ip

Тьфу блин, прочёл как не зная глобальный ip

Dron ★★★★★
()

А как вы отслеживаете, что сервер действительно слушает? tcpdump или wireshark - это показывают. С какой ошибкой отваливается клиент?

Silerus ★★★★
()

Код у тебя правильный. Правда, зря ты не проверяешь ошибки от listen(). Проверь, что нет другой программы, слушающей на том же порту. Проверь, что твой IP на машине действительно такой. Если у тебя интернет через роутер, внешний адрес будет у роутера, а не у твоего компьютера.

i-rinat ★★★★★
()
Ответ на: комментарий от i-rinat

Проверь, что нет другой программы, слушающей на том же порту

Чтобы это отсечь можно наверное флаг SO_REUSEPORT поставить

 int listen_socket = socket(AF_INET, SOCK_STREAM, 0);
    int one = 1;
setsockopt(listen_socket, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &one, sizeof(one));
Dron ★★★★★
()
Ответ на: комментарий от Dron

Чтобы это отсечь можно наверное флаг SO_REUSEPORT поставить

So long as the first server sets this option before binding its socket, then any number of other servers can also bind to the same port if they also set the option beforehand. The requirement that the first server must specify this option prevents port hijacking—the possibility that a rogue application binds to a port already used by an existing server in order to capture (some of) its incoming connections or datagrams.

И это логично.

i-rinat ★★★★★
()
Ответ на: комментарий от Silerus

Похоже я поторопился с выводами, ничего он не прослушивает при указании глобального ip адреса... Проверил только что на возвращение нуля от listen(). И при переборе портов в цикле while тоже ничего не меняется...

kennydzzze
() автор топика
Ответ на: комментарий от i-rinat

Да, через роутер, брал ip wan, вроде тот, что нужный. И разве ip адрес роутера может чем-то отличаться от ip адреса пк? Тогда я похоже вообще ничего не знаю о сетях и полез сюда) В таком случае как узнать ip адрес именно пк, а не роутера?

kennydzzze
() автор топика
Ответ на: комментарий от kennydzzze

И разве ip адрес роутера может чем-то отличаться от ip адреса пк?

Вкратце — да. Роутер это другой компьютер. Ты слушаешь на своём компьютере, а клиент коннектится к роутеру. Как роутер должен догадаться, что соединение к нему на 1337-й порт нужно проксировать к обратно к тебе?

В таком случае как узнать ip адрес именно пк, а не роутера?

ip a или ifconfig. Там в выводе будет написано. И да, у компьютера может быть несколько адресов. Это нормально.

i-rinat ★★★★★
()
Ответ на: комментарий от i-rinat

В wsl не удается использовать эти команды, можно ли в cmd провести аналогичную процедуру? ipconfig например? Только я не до конца понимаю теперь какой именно ip адрес мне нужен... Вообще, мне иногда приходилось создавать сервер для какой-либо онлайн игры, после его настройки я всегда использовал ip прописанный на странице роутера.

kennydzzze
() автор топика
Ответ на: комментарий от i-rinat

И да, я не правильно прочитал ваше сообщение, я использовал ip адрес тот, что у роутера, так что ip адрес указан правильно, но как я писал уже выше, при проверке возвращаемого значения функции listen() сервер не переходит в прослушивание, и подбор порта в цикле тоже не решает проблему... Похоже, код у меня не правльный все-таки.

kennydzzze
() автор топика
Ответ на: комментарий от kennydzzze

мне иногда приходилось создавать сервер для какой-либо онлайн игры, после его настройки я всегда использовал ip прописанный на странице роутера.

Такие программы пишутся с учётом работы за роутером, и они настраивают проброс порта через UPnP или NAT-PMP.

В wsl

Это который слой совместимости в Windows 10? Лучше поставить линукс в виртуалку и экспериментировать там. Если ты наткнёшься на какую-то особенность этого wsl, без опыта вряд ли сможешь понять, где ошибка, в системе или у тебя. И будешь долго биться головой об стенку.

Только я не до конца понимаю теперь какой именно ip адрес мне нужен

Тебе нужен адрес той машины, на которой ты экспериментируешь. Вроде бы, на Windows это действительно через ipconfig смотрится. Но я уже забыл детали.

i-rinat ★★★★★
()
Ответ на: комментарий от kennydzzze

я использовал ip адрес тот, что у роутера, так что ip адрес указан правильно

Представь, что ты отправляешь бумажные письма. Ты пишешь на конверте адреса, относишь конверты на отделение почты, они их отправляют дальше. Всё нормально, письма доходят до адресатов.

Затем ты решаешь узнать, каково это — получить письмо. Но так как никто не знает, что тебе нужно написать письмо, ты решаешь сделать это сам. Берёшь конверт, пишешь на нём в графе назначения адрес почтового отделения, относишь на почту. Затем возвращаешься домой и начинаешь ждать.

Как думаешь, сколько нужно будет ждать? Придёт ли письмо?

i-rinat ★★★★★
()
Ответ на: комментарий от i-rinat

Ну, если адрес указан изначально не мой, то письмо мне не придет. Но я не совсем понял, это просто пример к разнице между адресами пк и роутера? Или это выражение несогласия на примере с тем, что ip адрес все-таки указан верно?) Если первое, то я понял вас. Если второе, то наверно я выразился не правильно и вы меня не поняли, по локальному адресу роутера я увидел его внешний ip адрес, этот адрес и должен быть адресом сервера, чем он собственно в коде и является.

kennydzzze
() автор топика

94.180.93.163

Это видимо внешний (возможно даже и не белый) адрес который выдал тебе провайдер. У тебя скорее всего банально нет доступа к портам, т.к. ты сидишь за натом.

Простой и универсальный способ проверить tcp линк из коробки в 90% дистров:

telnet <адресс> <порт>

В общем либо пробрасывай порт, либо тестируй на локальных адрессах.

Выбери любой другой адрес из списка:

hostname -I

pon4ik ★★★★★
()
Ответ на: комментарий от kennydzzze

Ну, если адрес указан изначально не мой, то письмо мне не придет.

Адрес роутера это адрес роутера. Не твой. Поэтому когда ты пытаешься соединиться с роутером, ничего хорошего не происходит. Сервер-то ты запускаешь у себя на компьютере, а не на роутере.

i-rinat ★★★★★
()
Ответ на: комментарий от i-rinat

Только что указал ip адрес пк, вроде сервер запустился, listen() вернула 0. Как теперь клиенту обратиться к этому серверу? Через адрес роутера?

kennydzzze
() автор топика
Ответ на: комментарий от i-rinat

Чем-то это походит на работу локального сервера, а не онлайнового. С другого пк похоже вряд ли получится подключится к серверу. Точнее возможно получится, но только с того пк, который будет связан с этим роутером?

kennydzzze
() автор топика
Ответ на: комментарий от kennydzzze

Чем-то это походит на работу локального сервера, а не онлайнового.

Да, так и есть. Ты же запускаешь сервер и клиент на одном и том же компьютере, что ты ещё ожидал?

Точнее возможно получится, но только с того пк, который будет связан с этим роутером?

Ага.

Если ты хочешь принимать соединения извне, нужно как-то настроить проброс портов. Некоторые роутеры позволяют руками настраивать проброс портов. Если нужно автоматизировать, ищи описание UPnP.

i-rinat ★★★★★
()
Ответ на: комментарий от i-rinat

Ну, порт пробросить не проблема. Как после этого будет выглядеть адрес сервера? Это будет уже адрес роутера? И клиенту нужно будет к адресу роутера коннектиться?

kennydzzze
() автор топика
Ответ на: комментарий от kennydzzze

Это будет уже адрес роутера? И клиенту нужно будет к адресу роутера коннектиться?

Да. Коннектишься к роутеру, а он пакеты пересылает на твой адрес.

i-rinat ★★★★★
()
Ответ на: комментарий от i-rinat

Ну понятно что вредонос может перехватить/прослушать тогда ssl. Но я тут профан ничего утверждать не буду.

Dron ★★★★★
()
Ответ на: комментарий от Dron

Про SSL ты перегнул, конечно, но в остальном да. Возможность всем перехватывать чужой трафик была бы дырой в безопасности.

i-rinat ★★★★★
()
Ответ на: комментарий от kennydzzze

Если извне хочешь подключаться то нужно настроить проброс порта на роутере к твоему компу, а так получается что ты стучишься к роутеру, а он и не знает куда дальше передавать пакеты. В клиенте должен быть указан адрес роутера.

А запустить сервер не удается потому как порт занят, смотри не запущен ли он уже.

Frost ★★★
()
Ответ на: комментарий от kennydzzze

После открытия порта запустить сервер не удается.

На это сложно что-то сказать. Слишком много неизвестных факторов.

i-rinat ★★★★★
()
Ответ на: комментарий от i-rinat

http://www.epos.ua/view.php/infosecurity_https-ssl

Назначение

HTTPS/SSL Network Packet Forensics Device (HTTPS/SSL-перехватчик) – программно-аппаратный комплекс, предназначенный для расшифровки информации, передаваемой через Интернет по протоколу HTTPS/SSL. Система способна перехватывать имена пользователей и пароли авторизации на https-страницах, например, при входе в Google, Gmail, Yahoo, eBay и др., а также содержимое шифрованных страниц и электронные письма, отправленные с таких страниц (в т.ч. вложения).

Принцип работы

Система работает как proxy-сервер для наблюдаемого компьютера. При попытке установить шифрованное соединение HTTPS/SSL, система осуществляет перехват по принципу MITM (man in the middle, человек посередине), перехватывая и подменивая сертификат SSL-сервера. Таким образом, HTTPS/SSL-перехватчик встраивается в канал и может просматривать пересылаемую зашифрованную информацию.

anonymous
()
Ответ на: комментарий от Frost

Расшифровка происходит на лету, за счет подмены сертификата. По идее браузер должен выдать предупреждение, но кто его знает. Доверия к SSL нет после heartbleed (хотя это был бекдор в конкретной реализации).

anonymous
()
Ответ на: комментарий от Frost

0,1 мс на хендшейк. Для того, чтобы эти системы работали, на машины перехватываемых ставится дополнительный корневой сертификат. Не знаю, зачем аноним мне эту цитату привёл.

i-rinat ★★★★★
()
Ответ на: комментарий от i-rinat

Доступа к компу у них нет. Там происходит mitm.

anonymous
()
Ответ на: комментарий от anonymous

браузер должен выдать предупреждение

Предприятия полностью контролируют свои компьютеры, поэтому на них централизованно раскатывается корневой сертификат из этой железки.

i-rinat ★★★★★
()
Ответ на: комментарий от anonymous

Еще цитатка оттуда же.

Недостатком средства является то, что наблюдаемый пользователь может получить предупреждение о несоответствии сертификата. Этого можно избежать, пользуясь услугами центров сертификации. За дополнительной информацией обращайтесь к ответственному за направление.

anonymous
()
Ответ на: комментарий от i-rinat

Я не собирался конечно продвигать свой «чат» в массы, по этому о шифровании не задумывался. Ввообще, интересно было бы узнать об этом, почитать. Но не знаю что и где. Еще интересовал вопрос касаемо выбора порта, клиент должен знать порт по которому происходит связь с программой клиента, но что, если на стороне клиента этот порт занят? Можно ли как-то зарезирвировать порт? Тоже интересно про это почитать. Если можете посоветовать статьи/книги не откажусь.

kennydzzze
() автор топика
Ответ на: комментарий от i-rinat

Они сотрудничают не только с предприятиями. Такую штуку можно поставить кому угодно.

anonymous
()
Ответ на: комментарий от anonymous

Бабла хотя срубить побольше за дополнительные фичи, хотя эти фичи встроенными должны быть. У того же печально известного bluecoat об этом в брифах прямым текстом сказано.

i-rinat ★★★★★
()
Ответ на: комментарий от anonymous

если конечно в открытом виде пересылать, но можно еще дополнительно шифровать как многие делают, например rsa

Frost ★★★
()
Ответ на: комментарий от kennydzzze

От хороших статей и книг я бы и сам не отказался. А так работает простой алгоритм: сталкиваешься с проблемой — гуглишь, читаешь.

i-rinat ★★★★★
()
Ответ на: комментарий от Frost

Да я скорей всего даже и не пробросил его, ибо забыл, что помимо файервола на роутере у меня есть файервол антивируса, там то я ничего не настроил да и не смог проверить порт на доступность. Завтра погляжу еще.

kennydzzze
() автор топика

Охота же в C пердолиться, написал бы на джаве или шарпах или ноде вообще, на основе нормального фреймворка, который работу с сокетами скроет и выдаст тебе норм реквесты.

Xunnu ★★
()
Последнее исправление: Xunnu (всего исправлений: 1)

используй UDP если отправляешь только текст

по поводу NAT, чтобы избежать мучений с настройками роутера, просто используй IPv6(если он конечно есть)

RisuX3
()
Ответ на: комментарий от kennydzzze

Вроде открыл порт, но как я понял, этот порт нужен только для клиента? Потому что сервер запускается и без открытия порта, что странно. К тому же, если на стороне сервера проверить ip и порт присоединенного клиента, то там локальный ip роутера 192.168.1.1 и порт, отличный от заданного 7393 (задан был и проброшен 1337).

kennydzzze
() автор топика
Ответ на: комментарий от Xunnu

Для начала хотел понять как это все работает на си.

kennydzzze
() автор топика
Ответ на: комментарий от kennydzzze

В общем, проблемы следующие:

1. Могу не открывать порт 1337 для пк, где запущен сервер, но открыть этот порт на другом пк, где запущен клиент. При этом клиент соединяется с сервером, но при попытке отправить сообщение сервер не выводит информацию о клиенте (адрес, порт), т.е. не создается сокет клиента на сервере (sock_client в коде сервера).

2. Открыть порт на пк, где сервер и на другом, где клиент, одновременно не получается. Роутер не дает это сделать, говорит, что такая запись с портом уже есть, хотя ip адреса машин разные.

п.с. Оба пк соединены с одним роутером.

kennydzzze
() автор топика
Ответ на: комментарий от RisuX3

По поводу ipv6, его нужно на роутере настраивать? Если смотреть свойства сетевого адаптера, то там можно выставить галочку напротив этого протокола, после чего появляется локальный ipv6 адрес. Этого ведь недостаточно? Или достаточно? Если недостаточно, то все же нужно настраивать его еще в самом роутере? Если достаточно, то этот адрес нужно использовать для сервера или клиента? И как на этот адрес влияет сам провайдер? Может ли он его предоставлять? Или этот адрес зависит только от возможностей роутера, а провайдер изначально всем предоставляет возможность его использовать?

kennydzzze
() автор топика
Ответ на: комментарий от kennydzzze

ipv6 - технология, которую должен уметь предоставлять провайдер. Если он не дает, то тогда только в пределах локалки (любой компьютер более-менее современный (года так с 2010) поддерживает айпи6)

MikeWortin ★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.