Не так давно понадобилось написать маленький сервер, и вот что меня смущает. В спецификации по сокетам пишут, что вызов accept(s, cln_addr, cln_addrlen) создает сокет с такими же параметрами как и слушающий сокет s. То есть, как я понимаю, оба сокета теперь привязаны к одному локальному порту. Но как же так? Получается что если юзеров подконнективается много -> для каждого из них делаем отдельный поток -> в каждом потоке свой сокет, и _все сокеты одинаковые_. То есть от разных юзеров все пакеты едут к одному и тому же сокету на сервере. В таком случае как реализуется то, что каждый юзер работает со своим потоком?
Разъясните плиз. как это работает.
Здравствуйте raeen, Вы писали:
R>То есть от разных юзеров все пакеты едут к одному и тому же сокету на сервере.
Нет. Слушающий сокет предназначен для подключения клиентов. Когда клиент подключается функция accept позволяет создать для него новый сокет.И теперь вся связь с клиентом (передача и получение данных) будет осуществлятся именно по этому, новому сокету. А под одинаковыми параметрами похоже подразумевается, что новый сокет будет такого же типа и с таким же протоколом, как и слушающий (т.е. то, что передается в функцию socket()).
Да, и кстати, вроде все работает примерно так.
Слушаем порт A. Подключаемся клиентом к A (порт клиента B). После вызова accept, создается сокет с портом C. И теперь клиент работает уже с новым сокетом, т.е. C<->B.
Здравствуйте raeen, Вы писали:
R>Не так давно понадобилось написать маленький сервер, и вот что меня смущает. В спецификации по сокетам пишут, что вызов accept(s, cln_addr, cln_addrlen) создает сокет с такими же параметрами как и слушающий сокет s. То есть, как я понимаю, оба сокета теперь привязаны к одному локальному порту. Но как же так? Получается что если юзеров подконнективается много -> для каждого из них делаем отдельный поток -> в каждом потоке свой сокет, и _все сокеты одинаковые_. То есть от разных юзеров все пакеты едут к одному и тому же сокету на сервере. В таком случае как реализуется то, что каждый юзер работает со своим потоком? R>Разъясните плиз. как это работает.
TCP соединение характеризуется четырьмя параметрами: локальный адрес, локальный порт, внешний адрес, внешний порт. Так вот, сокет s2 = accept(s, ...) "наследует" именно локальные адрес и порт от s. Внешние же адрес и порт равны соответствующим параметрам клиента. Это объясняет и исправляет модель многоклиентского подключения описанного тобой.
Практическое занятие
1) Запускаем твою программку сервер (на которой я подразумеваю наличие находящегося в режиме прослушивания сокета s). Запускаем утилиту netstat:
netstat -p TCP -a
Ищем находящееся в слушающем решиме подключение у которого внешний алрес и порт нулевые.
2) Запускаем клиента и соединяемся с сервером. На машине сервера опять делаем тот же вызов netstat'а и видим тот же слушающий s, и новое соединение (сокет s2) у которого внешний адрес — адрес клиента, а внешний порт — порт клиента.
3) Запускай и присоединяй новых клиентов и продолжай наблюдать.
Здравствуйте DS, Вы писали:
DS>Здравствуйте raeen.
DS>Да, и кстати, вроде все работает примерно так. DS>Слушаем порт A. Подключаемся клиентом к A (порт клиента B). После вызова accept, создается сокет с портом C.
Не так. У этого сокета (соединения) внутренний порт A, а внешний B.
Здравствуйте vladsm, Вы писали:
V>Здравствуйте raeen, Вы писали:
[skipped] V>TCP соединение характеризуется четырьмя параметрами: локальный адрес, локальный порт, внешний адрес, внешний порт. Так вот, сокет s2 = accept(s, ...) "наследует" именно локальные адрес и порт от s. Внешние же адрес и порт равны соответствующим параметрам клиента. Это объясняет и исправляет модель многоклиентского подключения описанного тобой.
Вот что мне ответил netstat:
Active Internet connections (including servers)
Proto Recv-Q Send-Q Local Address Foreign Address State
tcp 0 0 localhost:1100 localhost:1024 ESTABLISHED
tcp 0 0 localhost:1024 localhost:1100 ESTABLISHED
tcp 0 0 *:1100 *:* LISTEN
(сервер и клиент запущены на одной машине. сервер port=1100)
Все работает правильно. Accept() каждый раз возвращает новый сокет, аналогичный слушающему.
Но вот, что меня смущает: когда произойдет несколько соединений -> наш accept() вернет, соответственно, несколько одинаковых сокетов (верно?) (они будут в разных потоках). Предположим, от юзера A, который соеденился к серверу к сокету s1, едет пакет по адресу IP_СЕРВАКА:PORT_СОКЕТА_s1, от юзера B — IP_СЕРВАКА:PORT_СОКЕТА_s2 и т.д. s3,s4,s5...sn. Но так как наши сокеты s1,s2...sn имеют один и тот же локальный адрес (они все "породились" от слушающего s), то фактически во всех пакетах от разных подключенных юзеров будет _один и тот же_ адрес получателя и порт.
Вопрос: При условии, что каждый новый сокет создается в отдельном потоке. как сервер определит в какой поток от какого юзера кидать пакет, если в каждом пакете один и тот же адрес (адрес слушающего сокета)?.
Здравствуйте raeen, Вы писали:
R>Предположим, от юзера A, который соеденился к серверу к сокету s1, едет пакет по адресу IP_СЕРВАКА:PORT_СОКЕТА_s1, от юзера B — IP_СЕРВАКА:PORT_СОКЕТА_s2 и т.д. s3,s4,s5...sn. Но так как наши сокеты s1,s2...sn имеют один и тот же локальный адрес (они все "породились" от слушающего s), то фактически во всех пакетах от разных подключенных юзеров будет _один и тот же_ адрес получателя
Да
R> и порт.
Нет. Почему порт то один и тот же? Ты же сам пишешь: от юзера A едет пакет по адресу IP_СЕРВАКА:PORT_СОКЕТА_s1, от юзера B — IP_СЕРВАКА:PORT_СОКЕТА_s2 ....
R>Вопрос: При условии, что каждый новый сокет создается в отдельном потоке. как сервер определит в какой поток от какого юзера кидать пакет, если в каждом пакете один и тот же адрес (адрес слушающего сокета)?.
При срабатывании функции
s1 = accept(s, ...) // код на сервере
создается новое соединение. Вот оно в твоем случае:
tcp 0 0 localhost:1100 localhost:1024 ESTABLISHED // для одной машины (сервера) в общем случае
tcp 0 0 localhost:1024 localhost:1100 ESTABLISHED // а это для другой (клиента)
Конечными точками этого соединения являются сокет s1 (на стороне сервера) и тот сокет sClientA, который передавался в функцию connect на стороне клиента. Сокет s1 имеет внешний адрес IP_КЛИЕНТА_A:PORT_СОКЕТА_sClientA, а sClientA — IP_СЕРВАКА:PORT_СОКЕТА_s1. Пусть сокетом s1 занимается отдельный поток. Клиент A, посылая данные серверу, направляет их через свой сокет sClientA. Они идут прямиком к серверу и "вылетают" через сокет s1, то есть непосредственно в тот самый отдельный поток.
Сокет s, тем временем, продолжает находиться в режиме ожидания. И если а) на сервер придет запрос на подключение от нового клиента, б) для сокета s будет после этого вызвана функция accept, все повторится.
[skipped]
то фактически во всех пакетах от разных подключенных юзеров будет _один и тот же_ адрес получателя V>Да R>> и порт. V>Нет. Почему порт то один и тот же? Ты же сам пишешь: от юзера A едет пакет по адресу IP_СЕРВАКА:PORT_СОКЕТА_s1, от юзера B — IP_СЕРВАКА:PORT_СОКЕТА_s2 ....
Вот оно! На самом деле получается, что порт один и тот же. Смотри как выглядит результат netstat'а с несколькими клиентами:
tcp 0 0 localhost:1100 localhost:1028 ESTABLISHED
tcp 0 0 localhost:1028 localhost:1100 ESTABLISHED //клиент подключен к 1100
tcp 0 0 localhost:1100 localhost:1027 ESTABLISHED
tcp 0 0 localhost:1027 localhost:1100 ESTABLISHED //клиент подключен к 1100
tcp 0 0 localhost:1100 localhost:1025 ESTABLISHED
tcp 0 0 localhost:1025 localhost:1100 ESTABLISHED //клиент подключен к 1100
tcp 0 0 localhost:1100 localhost:1024 ESTABLISHED
tcp 0 0 localhost:1024 localhost:1100 ESTABLISHED //клиент подключен к 1100
tcp 0 0 *:1100 *:* LISTEN //сервер слушает на 1100
Вывод: для каждого клиента удаленный адрес такой: <адрес сервака> (все ок), но номер порта везде 1100! Каждый клиент "знает" удаленный порт — 1100. Получается какой бы пакет и от какого бы юзера не пришел, везде будет один и тот же порт в адресе получателя. То есть с точки зрения адреса получателя все пакеты от подключенных юзеров одинаковые.
Как быть теперь?
Здравствуйте raeen, Вы писали:
R>Здравствуйте vladsm, Вы писали:
R>[skipped] R>то фактически во всех пакетах от разных подключенных юзеров будет _один и тот же_ адрес получателя V>>Да R>>> и порт. V>>Нет. Почему порт то один и тот же? Ты же сам пишешь: от юзера A едет пакет по адресу IP_СЕРВАКА:PORT_СОКЕТА_s1, от юзера B — IP_СЕРВАКА:PORT_СОКЕТА_s2 ....
R>Вот оно! На самом деле получается, что порт один и тот же. Смотри как выглядит результат netstat'а с несколькими клиентами: R>
R>tcp 0 0 localhost:1100 localhost:1028 ESTABLISHED
R>tcp 0 0 localhost:1028 localhost:1100 ESTABLISHED //клиент подключен к 1100
R>tcp 0 0 localhost:1100 localhost:1027 ESTABLISHED
R>tcp 0 0 localhost:1027 localhost:1100 ESTABLISHED //клиент подключен к 1100
R>tcp 0 0 localhost:1100 localhost:1025 ESTABLISHED
R>tcp 0 0 localhost:1025 localhost:1100 ESTABLISHED //клиент подключен к 1100
R>tcp 0 0 localhost:1100 localhost:1024 ESTABLISHED
R>tcp 0 0 localhost:1024 localhost:1100 ESTABLISHED //клиент подключен к 1100
R>tcp 0 0 *:1100 *:* LISTEN //сервер слушает на 1100
R>
R>Вывод: для каждого клиента удаленный адрес такой: <адрес сервака> (все ок), но номер порта везде 1100! Каждый клиент "знает" удаленный порт — 1100. Получается какой бы пакет и от какого бы юзера не пришел, везде будет один и тот же порт в адресе получателя. То есть с точки зрения адреса получателя все пакеты от подключенных юзеров одинаковые. R>Как быть теперь?
С точки зрения адреса получателя — одинаковые. Но ведь есть еще адрес и порт отправителя. Вот по ним "отбор", видимо, и происходит.
Здравствуйте vladsm, Вы писали:
V>Здравствуйте raeen, Вы писали:
V>С точки зрения адреса получателя — одинаковые. Но ведь есть еще адрес и порт отправителя. Вот по ним "отбор", видимо, и происходит.
Получается, что так. Но как-то кривовато, ведь каждый пакет надо проверять и маршрутизировать... Собственно говоря вот это меня и смущает, не могу найти информацию о том, как именно происходит этот "отбор". Ведь тогда нужна некая область памяти, где будет храниться инфа о том, какой сокет для какого юзера и может что-то еще.
Есть мысли где инфу об этом найти можно?
Re[7]: Cокеты изнутри?
От:
Аноним
Дата:
03.09.02 14:04
Оценка:
я до конца не вкурил но
в моем понимании не в порте дело а в pair<port,socket>
хоть и уникальность у каждого своя
а поток данных привязан к их сочетанию
V>>С точки зрения адреса получателя — одинаковые. Но ведь есть еще адрес и порт отправителя. Вот по ним "отбор", видимо, и происходит.
R>Получается, что так. Но как-то кривовато, ведь каждый пакет надо проверять и маршрутизировать... Собственно говоря вот это меня и смущает, не могу найти информацию о том, как именно происходит этот "отбор". Ведь тогда нужна некая область памяти, где будет храниться инфа о том, какой сокет для какого юзера и может что-то еще. R>Есть мысли где инфу об этом найти можно?
Смотрим описание accept():
This routine extracts the first connection on the queue of pending connections on s, creates a new socket with the same properties as s and returns a handle to the new socket.
Получаем новый хендл и ставим ему в соответствие полученный accept-ом же адрес отправителя. Все. Мы знаем какой сокет (хендл) сервера какому клиенту соответствует. В чем беда-то? Зачем проверять каждый пакет?
Не совсем понятна задача. Что необходимо сделать-то?
Здравствуйте vladsm, Вы писали:
V>Здравствуйте Wonder, Вы писали:
W>>Не совсем понятна задача. Что необходимо сделать-то?
V>Человек хочет разобраться как все это работает. Работает что? Как работает WinSock и как конкретное приложение осуществляет идентификацию клиентов — "две большие разницы". По сути, вопрос идентификации клиентов — это вопрос алгоритмов. А понять его можно легко, взяв исходники какого-нибудь несложного чата для локальной сети.
Здравствуйте Wonder, Вы писали:
W>Здравствуйте vladsm, Вы писали:
V>>Здравствуйте Wonder, Вы писали:
W>>>Не совсем понятна задача. Что необходимо сделать-то?
V>>Человек хочет разобраться как все это работает. W> Работает что? Как работает WinSock и как конкретное приложение осуществляет идентификацию клиентов — "две большие разницы". По сути, вопрос идентификации клиентов — это вопрос алгоритмов. А понять его можно легко, взяв исходники какого-нибудь несложного чата для локальной сети.
Ты совсем не понял о чем тут идет речь. Я не спрашиваю как работает програмный интерфейс сокетов (я с этим давно уже разобрался). И спецификацию я прочел не один раз. Но там нету ответа на _мой вопрос_.
Здравствуйте raeen, Вы писали:
R>Ты совсем не понял о чем тут идет речь. Я не спрашиваю как работает програмный интерфейс сокетов (я с этим давно уже разобрался). И спецификацию я прочел не один раз. Но там нету ответа на _мой вопрос_.
Вот оригинал вопроса:
R>В таком случае как реализуется то, что каждый юзер работает со своим потоком? Разъясните плиз. как это работает.
О каких "юзерах" идет речь? Из контекста можно догадаться, что на самом деле речь идет о клиентах в терминологии TCP/IP. В таком случае фраза "каждый юзер работает со своим потоком" вообще лишена смысла. Клиент работает с сервером, а не с потоками. Если я не понял вопроса, то объясни поподробнее, что конкретно тебя интересует?
Здравствуйте Wonder, Вы писали:
W>Здравствуйте raeen, Вы писали:
R>>Ты совсем не понял о чем тут идет речь. Я не спрашиваю как работает програмный интерфейс сокетов (я с этим давно уже разобрался). И спецификацию я прочел не один раз. Но там нету ответа на _мой вопрос_.
W>Вот оригинал вопроса:
R>>В таком случае как реализуется то, что каждый юзер работает со своим потоком? Разъясните плиз. как это работает.
W>О каких "юзерах" идет речь? Из контекста можно догадаться, что на самом деле речь идет о клиентах в терминологии TCP/IP. В таком случае фраза "каждый юзер работает со своим потоком" вообще лишена смысла. Клиент работает с сервером, а не с потоками. Если я не понял вопроса, то объясни поподробнее, что конкретно тебя интересует?
Ок, вольно =)
Как видишь, ты меня почти правильно понял. Зачем нам лишние дебри терминологии — и так ясно про что говорим, не так ли?
Хорошо, далее я не буду трогать "юзеров", используя "правильную" речь, ласкающую слух =)
Итак есть сервер. Есть сокет, который "слушает" некий порт. Далее несколько господ с удаленных хостов соединяются с сервером, для каждого соединения создаем отдельный процесс в котором остается один живой сокет — хэндлер которого вернула accept() при соединении и который идентичен слушающему (из спец.). Получается, что для каждого хоста в данном соединении удаленный адрес и порт одинаковые => все пакеты, от этих хостов, имеют один и тот же адрес (и порт) назначения. Но эти пакеты должны как-то маршрутизироваться когда приходят на сервер, ведь каждый удаленный хост фактически обслуживается в отдельном потоке (для него отдельный сокет). То есть, как я понимаю, чтобы была возможность такой маршр-ции пакетов, при соединении должна сохранятся некая инфа об удаленном хосте (например его адрес), по которой его можно будет однозначно идентифицировать и, следовательно, отправить нужному сокету. Вопрос: где храниться эта инфа? Как программно можно получить к ней доступ.
А вообще-то просто интересно как это работает изнутри (см. сабж).
Здравствуйте raeen, Вы писали:
R>Здравствуйте Wonder, Вы писали:
W>>Здравствуйте raeen, Вы писали:
R>>>Ты совсем не понял о чем тут идет речь. Я не спрашиваю как работает програмный интерфейс сокетов (я с этим давно уже разобрался). И спецификацию я прочел не один раз. Но там нету ответа на _мой вопрос_.
W>>Вот оригинал вопроса:
R>>>В таком случае как реализуется то, что каждый юзер работает со своим потоком? Разъясните плиз. как это работает.
W>>О каких "юзерах" идет речь? Из контекста можно догадаться, что на самом деле речь идет о клиентах в терминологии TCP/IP. В таком случае фраза "каждый юзер работает со своим потоком" вообще лишена смысла. Клиент работает с сервером, а не с потоками. Если я не понял вопроса, то объясни поподробнее, что конкретно тебя интересует?
R>Ок, вольно =) R>Как видишь, ты меня почти правильно понял. Зачем нам лишние дебри терминологии — и так ясно про что говорим, не так ли?
Не дебри, а азы. Практика показывает, что при черезмерном использовании жаргона "и так ясно" бывает далеко не всегда.
R>Хорошо, далее я не буду трогать "юзеров", используя "правильную" речь, ласкающую слух =)
Сарказм, ИМХО, неуместен.
R>Итак есть сервер. Есть сокет, который "слушает" некий порт. Далее несколько господ с удаленных хостов соединяются с сервером, для каждого соединения создаем отдельный процесс в котором остается один живой сокет — хэндлер которого вернула accept() при соединении и который идентичен слушающему (из спец.). Получается, что для каждого хоста в данном соединении удаленный адрес и порт одинаковые => все пакеты, от этих хостов, имеют один и тот же адрес (и порт) назначения. Но эти пакеты должны как-то маршрутизироваться когда приходят на сервер, ведь каждый удаленный хост фактически обслуживается в отдельном потоке (для него отдельный сокет).
"Процесс" и "поток" — разные вещи. Или ты опять скажешь, что это "дебри терминологии"?
R>То есть, как я понимаю, чтобы была возможность такой маршр-ции пакетов, при соединении должна сохранятся некая инфа об удаленном хосте (например его адрес), по которой его можно будет однозначно идентифицировать и, следовательно, отправить нужному сокету.
Абсолютно правильно понимаешь.
R>Вопрос: где храниться эта инфа?
Не вдаваясь в подробности: в системе.
R>Как программно можно получить к ней доступ.
===8<-- MSDN ---
The Windows Sockets getsockname function retrieves the local name for a socket.
The Windows Sockets getpeername function retrieves the name of the peer to which a socket is connected.
------- MSDN ---
R> А вообще-то просто интересно как это работает изнутри (см. сабж).
Если интересно — советую внимательнее читать MSDN как минимум.
Здравствуйте raeen, Вы писали:
R>Ок, вольно =)
Полегче, а то сам построю
R>Как видишь, ты меня почти правильно понял. Зачем нам лишние дебри терминологии — и так ясно про что говорим, не так ли?
Выше ты утверждал, что я ничего не понял...
R>Хорошо, далее я не буду трогать "юзеров", используя "правильную" речь, ласкающую слух =)
Вот удружил. Большое, я бы сказал огромное, тебе спасибо за это!
R>Далее несколько господ с удаленных хостов соединяются с сервером,
Что за "господа" такие?
R>для каждого соединения создаем отдельный процесс
И чего, мы сами, вот так вот запросто, берем и создаем отдельный (от кого?) процесс?
R>в котором остается один живой сокет — хэндлер которого вернула accept() при соединении и который идентичен слушающему (из спец.).
ОК. Один живой. А сколько там мертвых сокетов? Да и handler и handle есть разные весчи...
R>Получается, что для каждого хоста в данном соединении удаленный адрес и порт одинаковые => все пакеты, от этих хостов, имеют один и тот же адрес (и порт) назначения.
Точно.
R>Но эти пакеты должны как-то маршрутизироваться когда приходят на сервер, ведь каждый удаленный хост фактически обслуживается в отдельном потоке (для него отдельный сокет).
Маршрутизация — понятие сетевое. Ну приходят они на сервер, а куда после маршрутизации уходят?
R>То есть, как я понимаю, чтобы была возможность такой маршр-ции пакетов, при соединении должна сохранятся некая инфа об удаленном хосте (например его адрес), по которой его можно будет однозначно идентифицировать и, следовательно, отправить нужному сокету.
Как можно идентифицировать клиентов я тебе уже написал выше, после чего ты сказал, что я ничего не понял...
[skipped] R>>То есть, как я понимаю, чтобы была возможность такой маршр-ции пакетов, при соединении должна сохранятся некая инфа об удаленном хосте (например его адрес), по которой его можно будет однозначно идентифицировать и, следовательно, отправить нужному сокету. W>Как можно идентифицировать клиентов я тебе уже написал выше, после чего ты сказал, что я ничего не понял...
Да ладно, извени за сарказм
Думаю, что все уже по теме сказали.