Добрый день.
Я переписал из учебника простейший сервер:
int sock, listener;
struct sockaddr_in addr;
char buf[1024];
int bytes_read;
listener = socket(AF_INET, SOCK_STREAM, 0);
if(listener < 0)
{
perror("socket");
exit(1);
}
addr.sin_family = AF_INET;
addr.sin_port = htons(3425);
addr.sin_addr.s_addr = htonl(INADDR_ANY);
if(bind(listener, (struct sockaddr *)&addr, sizeof(addr)) < 0)
{
perror("bind");
exit(2);
}
listen(listener, 5);
while(1)
{
sock = accept(listener, NULL, NULL);
if(sock < 0)
{
perror("accept");
exit(3);
}
while(1)
{
bytes_read = recv(sock, buf, 1024, 0);
if(bytes_read <= 0) break;
cout << buf << endl;
}
close(sock);
}
Проблема в том, что первое соединение ловится и корректно обрабатывает всё принимаемое, а вот после разрыва соединения клиентом заново соединиться уже не получается. Судя по логам, сервер после первого дисконнекта снова повисает в "sock = accept(listener, NULL, NULL);", но уже новые соединения не видит.
Как бы мне сделать так, чтобы сервер последовательно (!) обрабатывал подключения клиентов? Т.е. как только один клиент закончил соединение, сервер сразу же мог бы соединиться с новым клиентом.
Спасибо.
А точно подвисает на accept? Я бы предположил, что ты некорректно закрываешь соединение на клиенте и и з-за этого recv блокируется. Если это винда и все навтройки выстроены по умолчанию (не изменено время ожидания и соединение не отслеживает таймаут), то несколько минут винда будет думать, что соединение живое и recv не вернет 0.
Здравствуйте, stbzh, Вы писали:
S>А точно подвисает на accept? Я бы предположил, что ты некорректно закрываешь соединение на клиенте и и з-за этого recv блокируется. Если это винда и все навтройки выстроены по умолчанию (не изменено время ожидания и соединение не отслеживает таймаут), то несколько минут винда будет думать, что соединение живое и recv не вернет 0.
Сервер под Debian etch4, клиент под Windows XP, связь через локалку. Настройки везде умолчальные. Сервер на 99% висит в accept — я логи после каждого действия в cout вывожу (выше я их постирал, чтоб не засоряли код). Да, такая деталь — сервер крутится не в основном потоке программы, а в отдельно созданном параллельном main'у.
Я и сам подозреваю, что проблема в клиенте, но вот где именно?..
Вот код клиента:
pSocket = new CSocket();
CString Str;
Str = "192.168.0.22";
int nPort = 3425;
pSocket->Create(nPort, SOCK_STREAM, NULL);
pSocket->Connect(Str, nPort);
Str = "SESSION BEGIN AB5643F8865CD0\n";
pSocket->Send(Str.GetBuffer(Str.GetLength()), Str.GetLength(), 0);
Str = "GET STATE AB5643F8865CD0\n";
pSocket->Send(Str.GetBuffer(Str.GetLength()), Str.GetLength(), 0);
pSocket->Close();
delete pSocket;
pSocket = NULL;
Вроде всё по учебнику, а не работает.
A_>A_> close(sock);
A_>
Это в общем случае UB, надо closesocket делать
TarasCo:
TC>Это в общем случае UB, надо closesocket делать
Простите, но что есть "UB" и где взять функцию closesocket? В каталоге <include/*.h> её нету, и компилятор её не знает.
--------------------------------------------------------
andreytsb:
A>В идеальных условиях (на локальной машине) даже при одновременном подключении нескольких клиентов (с помощью telnet) сервер обработал всех поочередно: как только отсоединялся текущий — обрабатывалось следующее соединение).
A>Проблема очень интересная. Чтобы полностью разобратся нужно лицезреть полный код клиента и сервера.
Вот код сервера, собран с помощью GNU GCC под Debian Etch4:
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <iostream>
int main()
{
int sock, listener;
struct sockaddr_in addr;
char buf[1024];
int bytes_read;
listener = socket(AF_INET, SOCK_STREAM, 0);
if (listener < 0)
{
return 1;
}
addr.sin_family = AF_INET;
addr.sin_port = htons(3425);
addr.sin_addr.s_addr = htonl(INADDR_ANY);
if (bind(listener, (struct sockaddr *)&addr, sizeof(addr)) < 0)
{
return 2;
}
while (1)
{
listen(listener, 1);
while(1)
{
sock = accept(listener, NULL, NULL);
if (sock < 0)
{
return 3;
}
while(1)
{
bytes_read = recv(sock, buf, 1024, 0);
if (bytes_read <= 0) break;
send(sock, buf, bytes_read, 0);
std::cout << buf << std::endl;
}
close(sock);
}
}
return 0;
}
Клиент, который собственно не работает. Собран под MSVC6.0 + MFC в виде простейшего диалога с кнопкой. Код выполняется по нажатию на эту кнопку. Первое нажатие отрабатывает как положено, сервер всё принимает/отсылает/печатает, второе и последующие нажатия — никакой реакции сервера. В отладчике видно, что pUM_Socket->Connect() возвращает 0.
void CUmEmulatorDlg::OnButtonConnect()
{
if (pUM_Socket) // переменная объявлена как CSocket* CUmEmulatorDlg::pUM_Socket;
{
AfxMessageBox("Already connected!");
return;
}
pUM_Socket = new CSocket();
CString Str;
Str = "192.168.0.22";
int nPort = 3425;
pUM_Socket->Create(nPort, SOCK_STREAM, NULL);
pUM_Socket->Connect(Str, nPort);
Str = "SESSION BEGIN AB5643F8865CD0\n";
pUM_Socket->Send(Str.GetBuffer(Str.GetLength()), Str.GetLength(), 0);
pUM_Socket->Receive(Str.GetBuffer(Str.GetLength()), Str.GetLength(), 0);
Str = "GET STATE AB5643F8865CD0\n";
pUM_Socket->Send(Str.GetBuffer(Str.GetLength()), Str.GetLength(), 0);
pUM_Socket->Receive(Str.GetBuffer(Str.GetLength()), Str.GetLength(), 0);
pUM_Socket->Close();
delete pUM_Socket;
pUM_Socket = NULL;
}
Для проверки написал простенький аналог этого клиента под Debian, но запустить его, к сожалению, могу лишь локально с сервером, а по реальной сети запускать его не на чем. Этот клиент работает корректно, сколько бы раз его ни запускали, при том, что он сам по себе делает пяток независимых соединений:
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <iostream>
char message[] = "Hello there!\n";
char buf[sizeof(message)];
int main()
{
int sock;
struct sockaddr_in addr;
for (int i=0; i<5; i++)
{ sock = socket(AF_INET, SOCK_STREAM, 0);
if(sock < 0)
{
return 1;
}
addr.sin_family = AF_INET;
addr.sin_port = htons(3425);
addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
if(connect(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0)
{
return 2;
}
send(sock, message, sizeof(message), 0);
recv(sock, buf, sizeof(message), 0);
std::cout << buf << std::endl;
close(sock);
}
return 0;
}
======================================================
Пока писал этот текст, провёл несложный эксперимент:
Запустил сервер. Подключился удалённым клиентом несколько раз. Как и раньше, получилось лишь единожды. После этого, не выключая сервер, запустил локальный клиент и сервер корректно обработал его запросы.
Очевидно, что либо клиент под MFC работает некорректно, либо сервер как-то иначе обрабатывает именно loopback-соединения (вообще-то он и знать об этом не должен, но мало ли...).
Может у кого-нибудь есть мысли, где тут собака порылась?..
Здравствуйте, andreytsb, Вы писали:
A>Попробуй переписать код клиента без использования сокетов MFC.
A>Мне кажется, что с ними не все чисто.
Странно всё это и непонятно. Раньше я писал сервер-клиенты под MFC и они прекрасно работали, а тут впервые полез писать под линукс и такая непонятка.