Форум программистов
 

Восстановите пароль или Зарегистрируйтесь на форуме, о проблемах и с заказом рекламы пишите сюда - alarforum@yandex.ru, проверяйте папку спам!

Вернуться   Форум программистов > C/C++ программирование > Visual C++
Регистрация

Восстановить пароль
Повторная активизация e-mail

Купить рекламу на форуме - 42 тыс руб за месяц

Ответ
 
Опции темы Поиск в этой теме
Старый 21.07.2011, 17:45   #1
btf
Пользователь
 
Регистрация: 03.12.2009
Сообщений: 25
По умолчанию Многопоточный чат на winsock

Всем привет. Мне необходимо написать многопоточный чат. При написании, у меня возникла проблема. Я пишу по примеру из "Самоучитель игры на WinSock2". В книге приведен пример эхо-сервера, т.е. написать на его примере сервер для чата - не должно составить особой проблемы.
Сейчас я нахожусь на стадии написания сервера. Возникла проблема с многопоточностью. В книге все просто: клиент коннектится к серверу а далее вызывается функция CreateThread с одним из параметров SexToClient, где SexToClient - функция, обслуживающая клиента. Вот код данной функции:
Код:
DWORD WINAPI SexToClient(LPVOID client_socket)
{
SOCKET my_sock;
my_sock=((SOCKET *) client_socket)[0];
char buff[20*1024];
#define sHELLO "Hello, Sailor\r\n"
// отправляем клиенту приветствие
send(my_sock,sHELLO,sizeof(sHELLO),0);
// цикл эхо-сервера: прием строки от клиента и возвращение ее клиенту
while( (int bytes_recv=recv(my_sock,&buff[0],sizeof(buff),0)) &&
bytes_recv !=SOCKET_ERROR)
send(my_sock,&buff[0],bytes_recv,0);
// если мы здесь, то произошел выход из цикла по причине
// возращения функцией recv ошибки – соединение с клиентом разорвано
nclients; // уменьшаем счетчик активных клиентов
printf("disconnect\n"); PRINTNUSERS
// закрываем сокет
closesocket(my_sock);
return 0;
}
По этой функции у меня возникло несколько вопросов:
1) в строке "my_sock=((SOCKET *) client_socket)[0];" что значит [0]?
2) что значит DWORD WINAPI? Прошу не отправлять меня читать про WinApi. Я представляю что это такое, как и для чего используется. Просто прошу пояснить, что значат эти два слова перед описанием функции.
3) Ну и самый главный вопрос, который у меня возник. Как отправлять сообщение пользователя не ему самому, а всем пользователям сразу?
Я так понимаю, что надо как-то создать массив типа SOCKET, в котором будут хранится все сокеты, по которым подключены клиенты, и в цикле отправлять это сообщение всем, но как это сделать в потоке? Или может есть еще какие-нибудь варианты?

Подскажите, пожалуйста, ответы на эти вопросы.
btf вне форума Ответить с цитированием
Старый 21.07.2011, 19:33   #2
counter
Участник клуба
 
Регистрация: 18.10.2008
Сообщений: 1,409
По умолчанию

1) это массив элементов SOCKET (сишный синтаксис иногда пугает )
2) DWORD - возвращаемое значение функции
WINAPI = _stdcall - cоглашение вызова функций
http://ru.wikipedia.org/wiki/Соглаше...dcall.2Fwinapi
3) смотри 1)
counter вне форума Ответить с цитированием
Старый 21.07.2011, 19:58   #3
pproger
C++ hater
СтарожилДжуниор
 
Аватар для pproger
 
Регистрация: 19.07.2009
Сообщений: 3,333
По умолчанию

2counter
Цитата:
это массив элементов SOCKET
был бы это массив, в SexToClient передавался бы его размер. а так человек просто разыменовал указатель по-индусски
Код:
SOCKET my_sock = *((SOCKET *) client_socket);
Цитата:
сишный синтаксис иногда пугает
дык не надо пугаться, надо его понять
I invented the term Object-Oriented, and I can tell you I did not have C++ in mind. (c)Alan Kay

My other car is cdr.

Q: Whats the object-oriented way to become wealthy?
A: Inheritance
pproger вне форума Ответить с цитированием
Старый 21.07.2011, 20:00   #4
btf
Пользователь
 
Регистрация: 03.12.2009
Сообщений: 25
По умолчанию

То есть, как я понял, я из этого потока могу получить доступ ко всем, подключенным к серверу, клиентам? И по сути, что бы отправить сообщение всем клиентам, достаточно использовать следующий код:
Код:
DWORD WINAPI SexToClient(LPVOID client_socket)
{
SOCKET my_sock;
my_sock=((SOCKET *) client_socket)[0];
char buff[20*1024];
#define sHELLO "Hello, Sailor\r\n"
// отправляем клиенту приветствие
send(my_sock,sHELLO,sizeof(sHELLO),0);
// цикл эхо-сервера: прием строки от клиента и возвращение ее клиенту
while( (int bytes_recv=recv(my_sock,&buff[0],sizeof(buff),0)) &&
bytes_recv !=SOCKET_ERROR)
for (int i=0; i<usersCount; i++) {
my_sock=((SOCKET*)client_socket)[i];
send(my_sock,&buff[0],bytes_recv,0);
}
// если мы здесь, то произошел выход из цикла по причине
// возращения функцией recv ошибки – соединение с клиентом разорвано
nclients; // уменьшаем счетчик активных клиентов
printf("disconnect\n"); PRINTNUSERS
// закрываем сокет
closesocket(my_sock);
return 0;
}
так?

По поводу пунктов 1 и 2 спасибо.
btf вне форума Ответить с цитированием
Старый 21.07.2011, 21:27   #5
veniside
Старожил
 
Регистрация: 03.01.2011
Сообщений: 2,508
По умолчанию

Код:
my_sock=((SOCKET*)client_socket)[i];
включив режим телепатии, можно сказать, что поток создаётся для одного сокета (клиента), так что [i], скорей-всего, не прокатит.

Если у вас клиентов мало (меньше 50), можно оставить код как есть, запускайте 50 потоков, только вместо эхо-цикла крутите пустой цикл, который будет ожидать данных для посылки клиенту (ну и принимать данные от клиента тоже полезно иногда).

Если клиентов много, система загнётся создавать на каждого юзвера отдельный поток. Надо думать, как одним потоком обрабатывать несколько клиентов.

И да, глобальный массив клиентов совсем не помешает, не забыть только обернуть его в критическую секцию, иначе в потоках будут чудеса происходить.
"Когда приходит положенное время, человек перестаёт играть в пинбол. Только и всего."
veniside вне форума Ответить с цитированием
Старый 21.07.2011, 21:32   #6
Сыроежка
Форумчанин
 
Регистрация: 01.07.2011
Сообщений: 423
По умолчанию

Цитата:
Сообщение от pproger Посмотреть сообщение
2counter

был бы это массив, в SexToClient передавался бы его размер. а так человек просто разыменовал указатель по-индусски
Код:
SOCKET my_sock = *((SOCKET *) client_socket);
На самом деле это может быть и массивом, так как не обязательно передавать его размерность. Просто при передачи, так как это С код, массив преобразуется в указатель на его первый элемент. Автор кода может быть как раз и хотел этим показать, что это некий массив.
Со мной можно встретиться на www.clipper.borda.ru
Сыроежка вне форума Ответить с цитированием
Старый 21.07.2011, 21:55   #7
pproger
C++ hater
СтарожилДжуниор
 
Аватар для pproger
 
Регистрация: 19.07.2009
Сообщений: 3,333
По умолчанию

2Сыроежка
Цитата:
Просто при передачи, так как это С код, массив преобразуется в указатель на его первый элемент.
умоляю, освободи меня от своих нравоучений

Цитата:
На самом деле это может быть и массивом
от массива нет никакого толку, если не знаешь его размер

пс. на самом деле я не читал код дальше строки
Код:
my_sock=((SOCKET *) client_socket)[0];
сейчас вижу, что он обращается к этому указателю со смещением, отличным от нулевого. так или иначе, в таких случаях НУЖНО передавать размерность. сегодня ему хочется отправить сообщение всем клиентам, завтра - выборочным
I invented the term Object-Oriented, and I can tell you I did not have C++ in mind. (c)Alan Kay

My other car is cdr.

Q: Whats the object-oriented way to become wealthy?
A: Inheritance

Последний раз редактировалось pproger; 21.07.2011 в 22:10.
pproger вне форума Ответить с цитированием
Старый 21.07.2011, 22:07   #8
Пепел Феникса
Старожил
 
Аватар для Пепел Феникса
 
Регистрация: 28.01.2009
Сообщений: 21,000
По умолчанию

Цитата:
массив преобразуется в указатель на его первый элемент.

и давно сам массив преобразуется?

наверно уж имелось в виду что имя массива есть указатель на его первый элемент.
Хорошо поставленный вопрос это уже половина ответа. | Каков вопрос, таков ответ.
Программа делает то что написал программист, а не то что он хотел.
Функции/утилиты ждут в параметрах то что им надо, а не то что вы хотите.
Пепел Феникса вне форума Ответить с цитированием
Старый 21.07.2011, 23:09   #9
btf
Пользователь
 
Регистрация: 03.12.2009
Сообщений: 25
По умолчанию

veniside, для каждого подключившегося клиента создается отдельный поток. Максимально допустимое число клиентов меньше 50.
pproger, скорее всего
Код:
my_sock=((SOCKET *) client_socket)[0];
разывменование указателя. Только зачем тут используется индекс?

Только я все равно не понимаю, как реализовать передачу, полученной сервером, строки всем пользователям, а не только тому, от которого он получил.

Объясните пожалуйста.

P.S. если надо скинуть весь код - скажите, я скину.
btf вне форума Ответить с цитированием
Старый 22.07.2011, 01:19   #10
Сыроежка
Форумчанин
 
Регистрация: 01.07.2011
Сообщений: 423
По умолчанию

Цитата:
Сообщение от pproger Посмотреть сообщение
2Сыроежка

умоляю, освободи меня от своих нравоучений


от массива нет никакого толку, если не знаешь его размер

пс. на самом деле я не читал код дальше строки
Код:
my_sock=((SOCKET *) client_socket)[0];
сейчас вижу, что он обращается к этому указателю со смещением, отличным от нулевого. так или иначе, в таких случаях НУЖНО передавать размерность. сегодня ему хочется отправить сообщение всем клиентам, завтра - выборочным
Во-первых, информация о размере массива может содержаться в самом массиве. Во-вторых, может быть какая-нибудь глобальная переменная, содержащая размер массива. В-третьих, эттот масив может иметь фиксированную длину, которая объявлена где-нибудь в заголовочном файле как манифестная константа. Так что не надо тделать скоропалительных выводов!
Со мной можно встретиться на www.clipper.borda.ru

Последний раз редактировалось Сыроежка; 22.07.2011 в 01:22.
Сыроежка вне форума Ответить с цитированием
Ответ


Купить рекламу на форуме - 42 тыс руб за месяц

Опции темы Поиск в этой теме
Поиск в этой теме:

Расширенный поиск


Похожие темы
Тема Автор Раздел Ответов Последнее сообщение
Чат через winsock Jasper92 Помощь студентам 0 23.11.2010 14:48
Многопоточный сервер FAiver Работа с сетью в Delphi 18 31.03.2008 08:51
многопоточный сервер мандарин Работа с сетью в Delphi 6 24.04.2007 07:22
Многопоточный Ping Квэнди Работа с сетью в Delphi 0 18.12.2006 15:01