Потоки и Сокеты
sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
connect(sock, ...);
...
//--Создается поток в котором обрабатываются данные приходящие от сервера
SECURITY_ATTRIBUTES sSecurAttrib;
sSecurAttrib.nLength = sizeof(SECURITY_ATTRIBUTES);
sSecurAttrib.bInheritHandle = TRUE;
sSecurAttrib.lpSecurityDescriptor = NULL;
CreateThread(&sSecurAttrib, 0, pThread_Proc, pParam, 0, &nThread_ID);
...
Суть проблемы в том что создаваемый поток не может, по неизвестным мне причинам (:, читать, писать в сокет в отличие от потока в котором собственно сокет создавался. Можно конечно и создание сокета запихнуть в поток с обработкой поступающих данных, но в моем случае необходимо иметь доступ к сокету из двух потоков, т.к. все это дело реализованно в классе и вызываются методы из основной программы которые работают с сокетом. Искал в инете решение этой проблемы, единственное на что натыкался так это на то что не наследуются дескрипторы потока.. хотя не уверен что это имеет здесь место. Темболее что sSecurAttrib.bInheritHandle = TRUE; как раз разрешает такое наследование если я не ошибаюсь.... Помогите разрулить проблемму плиз.
А так не робит?
А что вообще поток делает с сокетом и как получает его хэндл?
Он и так можно сказать глобальный он же в классе определен.
Зачем мьютексы? Если ты по поводу thread safe, то это я все контролирую при помощи CriticalSection, проблема то не в этом, а в том что не читает и не пишет в сокет из других потоков, кроме как из того в котором он (сокет) создан.
А так не робит?
А что вообще поток делает с сокетом и как получает его хэндл?
Нет с NULL в Secur Attribut'ах ниче не меняется.
В качестве параметра передаю указатель на класс.
Вот собственно как выглядит частичное описание класса.
{
protected:
SOCKET sockClient;
...
static DWORD __stdcall CommandThread(CClient_Base *_this);
public:
...
};
Ну и естественно в теле потока получаю доступ к сокету так.
{
...
recv([COLOR="Blue"]_this->sockClient[/COLOR], bfr, ...);
...
}
recv ничего не получает, он просто тупо ждет пока пакет придет, хотя там приходит куча просто.., send тоже ничего не отправляет.. проверенно сниффером (:
Возможно, проблема в привидении типов. CICQ_Client от каких классов наследуется, в каком порядке, и в каком классе создается сам поток?
Возможно, проблема в привидении типов. CICQ_Client от каких классов наследуется, в каком порядке, и в каком классе создается сам поток?
CClient_Base сам является базовым классом, в нем же и создается сокет и запускается тред. Проверил хендлы по значению - одинаковы.
Попробую разобраться. MSDN мне в помощь :)
Кстати остерегайтесь при межпотоковой работе передавать указатели на наследуемые объекты...
Значит где-то неправильно вызываются winsock-функции.
А вообще, судить по этим обрывкам могут только телепаты, а их думаю здесь нет. :)
А телепатов тут действительно нет, иначи бы мы тут не сидели :)))
А вообще, судить по этим обрывкам могут только телепаты, а их думаю здесь нет. :)
Думаю в правильности вызова функций сомневаться не стоит, да и WSAGetLastError это подтверждает.
Кстати остерегайтесь при межпотоковой работе передавать указатели на наследуемые объекты...
Решение нашел, случайно :), нужно в методе создающем поток в конце вставить Sleep(1000). Но это и решением назвать нельзя, т.к. почему так происходит не понимаю.. а следовательно может это у меня работает, а на другом компе не будет. Вот более полный листинг.
Класс реализующий работу с тредом
class CThread
{
private:
PROC_THREAD pThread_Proc;
PVOID pThread_Handle;
DWORD nThread_ID;
public:
CThread();
CThread(PROC_THREAD _pThread_Proc);
virtual ~CThread();
bool Start(PVOID _pParam, bool bSuspended = false);
bool Pause();
bool Resume();
bool Join();
bool Stop();
bool SetPriority(int _nPriority);
int GetPriority();
void SetThread(PROC_THREAD _pThread_Proc, PVOID _pThread_Handle, DWORD _nThread_ID);
void GetThread(PROC_THREAD & _pThread_Proc, PVOID & _pThread_Handle, DWORD & _nThread_ID);
};
{
pThread_Proc = NULL;
pThread_Handle = NULL;
nThread_ID = 0;
};
//-------------------------------------------------------------------------
CThread::CThread(PROC_THREAD _pThread_Proc)
{
pThread_Proc = _pThread_Proc;
pThread_Handle = NULL;
nThread_ID = 0;
};
//-------------------------------------------------------------------------
CThread::~CThread()
{
//-- Stop();
};
//-------------------------------------------------------------------------
bool CThread::Start(PVOID _pParam, bool bSuspended)
{
if((pThread_Handle = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)pThread_Proc, _pParam, bSuspended ? CREATE_SUSPENDED : 0, &nThread_ID)) == NULL)
{
return false;
}
return true;
};
//-------------------------------------------------------------------------
bool CThread::Pause()
{
return (SuspendThread(pThread_Handle) != -1);
};
//-------------------------------------------------------------------------
bool CThread::Resume()
{
return (ResumeThread(pThread_Handle) != -1);
};
//-------------------------------------------------------------------------
bool CThread::Join()
{
if(WaitForSingleObject(pThread_Handle, INFINITE) != WAIT_OBJECT_0)
{
return false;
}
return true;
};
//-------------------------------------------------------------------------
bool CThread::Stop()
{
if(TerminateThread(pThread_Handle, 0))
{
CloseHandle(pThread_Handle);
pThread_Handle = NULL;
nThread_ID = 0;
return true;
}
return false;
};
//-------------------------------------------------------------------------
bool CThread::SetPriority(int _nPriority)
{
return (SetThreadPriority(pThread_Handle, _nPriority) != 0);
};
//-------------------------------------------------------------------------
int CThread::GetPriority()
{
return GetThreadPriority(pThread_Handle);
};
//-------------------------------------------------------------------------
void CThread::SetThread(PROC_THREAD _pThread_Proc, PVOID _pThread_Handle, DWORD _nThread_ID)
{
pThread_Proc = _pThread_Proc;
pThread_Handle = _pThread_Handle;
nThread_ID = _nThread_ID;
};
//-------------------------------------------------------------------------
void CThread::GetThread(PROC_THREAD & _pThread_Proc, PVOID & _pThread_Handle, DWORD & _nThread_ID)
{
_pThread_Proc = pThread_Proc;
_pThread_Handle = pThread_Handle;
_nThread_ID = nThread_ID;
};
Код выполняемый в теле класса клиента
{
SOCKADDR_IN addrClient;
PHOSTENT pHost;
DWORD nAddrInet;
EnterCriticalSection(&mutexConnection);
memset(&addrClient, 0x00, sizeof(SOCKADDR_IN));
if((sockClient = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == SOCKET_ERROR)
{
LeaveCriticalSection(&mutexConnection);
return -1;
}
if(isalpha(*_pszHostName))
{
pHost = gethostbyname(_pszHostName);
}
else
{
nAddrInet = inet_addr(_pszHostName);
pHost = gethostbyaddr((const char*)&nAddrInet, sizeof(DWORD), AF_INET);
}
if(pHost != NULL) addrClient.sin_addr.s_addr = *((PDWORD)pHost->h_addr);
else addrClient.sin_addr.s_addr = inet_addr((const char *)_pszHostName);
addrClient.sin_family = AF_INET;
addrClient.sin_port = htons(_nPort);
if(connect(sockClient, (PSOCKADDR)&addrClient, sizeof(SOCKADDR_IN)) == SOCKET_ERROR)
{
closesocket(sockClient);
sockClient = SOCKET_ERROR;
LeaveCriticalSection(&mutexConnection);
return -2;
}
LeaveCriticalSection(&mutexConnection);
return 0;
};
//-------------------------------------------------------------------------
//-- [COLOR="#008000"]Тот самый метод который все и запускает... :)[/COLOR]
bool CClient_Base::Connect_Auth(const char *_pszHostName, WORD _nPort, const char *_pszLogin, const char * _pszPwd)
{
if(sockClient != SOCKET_ERROR) return false;
//--
...
strncpy(szLGN, _pszLogin, MAX_LEN_LOGIN);
strncpy(szPWD, _pszPwd, MAX_LEN_PASSWORD);
//--
int nErrCode;
//-- _THROW_EVENT - просто вызывает функцию обработчик событий
_THROW_EVENT(EF_CONNECTION, EC_CONNECT_AUTH, NULL);
//-- Подключаемся к указонному хосту
if((nErrCode = this->Connect(_pszHostName, _nPort)) != 0)
{
_THROW_EVENT(EF_ERROR, (nErrCode == -1) ? EE_WSA_SOCKET : EE_WSA_HOST, NULL);
_THROW_EVENT(EF_CONNECTION, EC_DISCONNECT, NULL);
return false;
}
//-- cthrCommandLoop - экземпляр класса CThread
//-- Запускаем поток обработчик входящих данных
if(!cthrCommandLoop.Start(this))
{
_THROW_EVENT(EF_ERROR, EE_THREAD_COMMAND, NULL);
Disconnect();
return false;
}
//-- Если ЗДЕСЬ вставить Sleep не меньше 1 сек то все работает... но почему!?!
[COLOR="Red"] Sleep(1000);[/COLOR]
return true;
};
//-------------------------------------------------------------------------
//-- [COLOR="Green"]Собственно сам поток[/COLOR]
DWORD __stdcall CClient_Base::CommandThread(CClient_Base *_this)
{
...
while((bytesRecv = recv(_this->sockClient, ...)) != SOCKET_ERROR)
{
...
}
...
};
Sleep(1000);
return true;
};
Пока вижу одно объяснение: сокет убивется после возвращения из этой функции и до того как создаваемый поток начинает из него что-то ждать.
Куда передается выполнение после возвращения из функции? Может там сокет убивается?
Sleep(1000);
return true;
};
Пока вижу одно объяснение: сокет убивется после возвращения из этой функции и до того как создаваемый поток начинает из него что-то ждать.
Куда передается выполнение после возвращения из функции? Может там сокет убивается?
Т.к. тестю эту функцию то собственно после ее вызова просто бесконечный цикл, который ничего не делает
BOOL bRet;
while( (bRet = GetMessage( &msg, NULL, 0, 0 )) != 0)
{
if (bRet != -1)
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
Не думаю что сокет убивается, т.к. если с задержкой в секунду то дальше все работает нормально и читает и пишет из сокета в другом потоке, а вот если без задержки то сокет не работоспособным становится, хотя WSAGetLastError после send возвращает 0... в чем дело не пойму...
Точно не скажу, т.к. эмпирических данных по этому вопросу нет, а в документации этот вопрос не встречал, но думаю что если один поток ждет данные с сокета, а другой его убивает, то он не убъется.
Если разгадка не в этом, то сдаюсь. Ищите полтергейст. :)
P.S. И небольшой оффтоп. Определять что в строке: ip адрес или имя домена по первому символу - неправильно.
Если разгадка не в этом, то сдаюсь. Ищите полтергейст. :)
Зх буду думать...
P.S. И небольшой оффтоп. Определять что в строке: ip адрес или имя домена по первому символу - неправильно.
Почему же в MSDN везде так и делают (: Хотя конечно доменное имя может начинаться с цифры...
да точно не возникает, нашел еще одно решение.. вместо Sleep(1000) в самом начале потока вызвать WSAStartup... хотя опять таки не понятно, ведь в самом начале приложения в основном потоке я ее вызываю.. или ее нужно в каждом новом?.. вроде бы нет, она же просто библиотеки какието подгружает и все..
Вызываешь до запуска второго потока или после?
.. или ее нужно в каждом новом?..
Нет
Первоначально вызываю в самом начале WinMain, и второй раз в начале самого второго потока который обрабатывает данные с сокета.. тогда все работает
Но это чисто мое решение...
PS. Кстати если внимательно почитать "Программирование в сетях Microsoft Windows" то можно очень быстро наткнуться на следующее - при работе клиентских приложений с определенными протоколами bind() обязателен!!! Хотя прописано это не явно и надо читать все описание. А у нас как правило это делать не любят. (я кстати тоже не очень любил, но вметро делать часто нечего вот и приучился) Мои клиенты (те кто пользуются разработками) уж точно никогда ничего не читают...
Особенно мне нравяться фразы - а мы ничего не делали...
За оффтоп извиняюсь :)
bind не обязателен на клиенте... просто будет так называемый unnamed socket..
то что ты везде пишешь bind это неправильно, если у человека 2 интерфейса (читай ИП) откуда ты знаешь с какого работать?.. я думаю ты биндишь на 127.0.0.1 либо на дефолтный ип.. так зачем это делать ?! это и так делается автоматически, ты лишь повторяешь это, не продуктивно однако...
[QUOTE=MSDN]
Примечания
Воспользуйтесь методом Bind, если необходимо использовать заданную локальную конечную точку. Требуется вызвать метод Bind перед тем, как появится возможность обращения к методу Listen. Не нужно вызывать метод Bind перед применением метода Connect, если не требуется использовать определенную локальную конечную точку. Метод Bind можно использовать как для протоколов без установления соединения, так и для протоколов, работающих с предварительным установлением соединения.
[/QUOTE]