Привет всем! У меня проблема. Написал программу (точнее 3 программы - одна координирующая, одна - принимающая пакеты, другая - отправляющая пакеты), которая передает звук, захваченный с микрофона, по сети этой же программе на другой машине. В общем, программа-телефон для локальной сети. Моя программа состоит из 3-х частей: 1) LANPhone (координирующая, интерфейс пользователю и все такое) 2) SendWave (ловит звук с микрофона и отправляет по сети звуковые буферы программе RecvWave на другой машине) 3) RecvWave (принимает звуковые буферы, посланные программой SendWave на удаленной машине, и воспроизводит звук) LANPhone может работать как в режиме клиента (когда мы "звоним" кому-то), так и в режиме сервера (когда до нас "дозванивается" кто-то). У меня стоит Kerio WinRoute Firewall, правила настроены верно. Так вот, когда фаер включен, SendWave на сервере почему-то сначала работает (посылает пакеты секунд 5-10), а потом завершается. Я выяснил, что это из-за того, что функция recv в SendWave завершается с ошибкой 10054 (Удаленный хост принудительно разорвал существующее подключение). Если пробовать с выключенным WinRoute, то все ОК. Нашел в Сети почти аналогичную программу lanic, она работает без проблем и с включенным WinRoute. Не пойму, почему фаер внезапно обрубает соединение. В настройках вроде никаких особых ограничений нет.
В этой ситуации можно что-то посоветовать лишь видя картину в деталях. Тоесть нужны либо исходники приемо-передатчиков, либо, что проще, закапчурите весь сетевой обмен(весь трафик) каким-нибудь Ethereal'ом(желательно, т.к. имею оное и смогу помочь) и приаттачьте логи сюда/либо в ЛС киньте на них линк.
Вот исходник SendWave. Исходник еще сырой и неоптимизированный, поэтому просьба не ругаться. Заметим, что флаг fStopSendWave устанавливается в TRUE, когда recv возвращается с ошибкой. Если fStopSendWave == TRUE, то происходит процесс завершения программы. Код (Text): BOOL fStopSendWave = FALSE; DWORD dwMainThreadId = 0; DWORD dwIPAddr = 0; WORD wPort = 0; HANDLE hSendWaveThread = INVALID_HANDLE_VALUE; HWND hMainWnd; BOOL fStartWasSended = FALSE; #define WM_USER_START WM_USER + 1024 #define WM_USER_STOP WM_USER + 1025 #define SampleRate 11025 #define BitsPerSample 8 #define Channels 1 #define BufferMS 500 #define Buffers 4 DWORD g_dwAllocatedBuffers; DWORD g_dwSendWaveThreadId; HWAVEIN g_hWaveIn; SOCKET g_Socket; BOOL OpenDevice() { MMRESULT Res; WAVEFORMATEX Format; g_dwAllocatedBuffers = 0; UINT BytesPerSample = (BitsPerSample + 7) / 8; Format.wFormatTag = WAVE_FORMAT_PCM; Format.nChannels = Channels; Format.wBitsPerSample = BitsPerSample; Format.nBlockAlign = (WORD)(Channels * BytesPerSample); Format.nSamplesPerSec = SampleRate; Format.nAvgBytesPerSec = Format.nSamplesPerSec * Format.nBlockAlign; Format.cbSize = 0; Res = waveInOpen(&g_hWaveIn, 0, &Format, g_dwSendWaveThreadId, 0, CALLBACK_THREAD); if (Res != MMSYSERR_NOERROR) { return FALSE; } UINT BufferSize = (Format.nSamplesPerSec * BufferMS / Buffers + 500) / 1000; if (!BufferSize) { BufferSize++; } BufferSize *= Format.nBlockAlign; for (int N = Buffers; N > 0; N--) { char *Buf = (char *)GlobalAlloc(GMEM_FIXED, BufferSize); WAVEHDR *Hdr = (WAVEHDR *)LocalAlloc(LMEM_FIXED, sizeof(*Hdr)); if (Buf && Hdr) { Hdr->lpData = Buf; Hdr->dwBufferLength = BufferSize; Hdr->dwFlags = 0; Hdr->dwLoops = 0; Hdr->dwUser = 0; waveInPrepareHeader (g_hWaveIn, Hdr, sizeof (*Hdr)); Res = waveInAddBuffer (g_hWaveIn, Hdr, sizeof (*Hdr)); } if (Buf && Hdr && Res == MMSYSERR_NOERROR) { g_dwAllocatedBuffers++; } else { if (Buf) { GlobalFree ((HGLOBAL)Buf); } if (Hdr) { LocalFree ((HLOCAL)Hdr); } return FALSE; } } return TRUE; } VOID CloseDevice() { waveInClose(g_hWaveIn); closesocket(g_Socket); SendMessage(hMainWnd, WM_CLOSE, 0, 0); } VOID ReleaseBuffer(WAVEHDR *Hdr) { GlobalFree((HGLOBAL)Hdr->lpData); LocalFree((HLOCAL)Hdr); g_dwAllocatedBuffers--; if (!g_dwAllocatedBuffers) { PostThreadMessage(g_dwSendWaveThreadId, WM_QUIT, 0, 0); } } DWORD WINAPI SendWaveThreadProc(LPVOID lpParam) { SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL); MSG msg; while (GetMessage(&msg, NULL, 0, 0) == TRUE) { switch (msg.message) { case MM_WIM_DATA: { WAVEHDR *Hdr = (WAVEHDR *)msg.lParam; waveInUnprepareHeader(g_hWaveIn, Hdr, sizeof(*Hdr)); if (fStopSendWave) { ReleaseBuffer(Hdr); break; } int bytesRecv; char recvbuf[32] = ""; if (!fStopSendWave) { if (send(g_Socket, (const char *)Hdr, sizeof(*Hdr), 0) == SOCKET_ERROR) { } bytesRecv = recv(g_Socket, recvbuf, sizeof(recvbuf), 0); if (bytesRecv < 0) { // Ошибка бывает здесь: WSAGetLastError() == 10054 waveInPrepareHeader(g_hWaveIn, Hdr, sizeof (*Hdr)); waveInAddBuffer(g_hWaveIn, Hdr, sizeof (*Hdr)); fStopSendWave = TRUE; break; } } if (!fStopSendWave) { if (send(g_Socket, (const char *)Hdr->lpData, Hdr->dwBufferLength, 0) == SOCKET_ERROR) { } bytesRecv = recv(g_Socket, recvbuf, sizeof(recvbuf), 0); if (bytesRecv < 0) { // Ошибка бывает здесь: WSAGetLastError() == 10054 waveInPrepareHeader(g_hWaveIn, Hdr, sizeof(*Hdr)); waveInAddBuffer(g_hWaveIn, Hdr, sizeof(*Hdr)); fStopSendWave = TRUE; break; } } if (!fStopSendWave) { waveInPrepareHeader(g_hWaveIn, Hdr, sizeof(*Hdr)); waveInAddBuffer(g_hWaveIn, Hdr, sizeof(*Hdr)); } break; } } } CloseDevice(); return 0; } DWORD WINAPI Start(LPVOID lpParam) { g_Socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); sockaddr_in clientService; clientService.sin_family = AF_INET; clientService.sin_addr.s_addr = dwIPAddr; clientService.sin_port = htons(wPort); while (connect(g_Socket, (SOCKADDR*)&clientService, sizeof(clientService)) == SOCKET_ERROR && !fStopSendWave) { Sleep(1); } if (fStopSendWave) { closesocket(g_Socket); SendMessage(hMainWnd, WM_CLOSE, 0, 0); return 0; } SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS); hSendWaveThread = CreateThread(NULL, 0, SendWaveThreadProc, NULL, CREATE_SUSPENDED, &g_dwSendWaveThreadId); if (hSendWaveThread) { if (OpenDevice()) { ResumeThread(hSendWaveThread); if (waveInStart(g_hWaveIn) == MMSYSERR_NOERROR) { CloseHandle(hSendWaveThread); hSendWaveThread = INVALID_HANDLE_VALUE; return 0; } else { CloseDevice(); } } } if (hSendWaveThread) { TerminateThread(hSendWaveThread, 0); CloseHandle(hSendWaveThread); } hSendWaveThread = INVALID_HANDLE_VALUE; return 0; } VOID Stop() { if (fStartWasSended) { fStopSendWave = TRUE; waveInReset(g_hWaveIn); closesocket(g_Socket); } else { SendMessage(hMainWnd, WM_CLOSE, 0, 0); } } LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch (uMsg) { case WM_USER_START: dwIPAddr = lParam; wPort = wParam; CloseHandle(CreateThread(NULL, 0, Start, NULL, 0, NULL)); fStartWasSended = TRUE; break; case WM_USER_STOP: Stop(); break; case WM_CLOSE: PostQuitMessage(0); break; } return DefWindowProc(hwnd, uMsg, wParam, lParam); } int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE, LPSTR lpCmdLine, int nCmdShow) { // Ограничиваем память процесса на всякий случай HANDLE hJob = CreateJobObject(NULL, NULL); AssignProcessToJobObject(hJob, GetCurrentProcess()); JOBOBJECT_EXTENDED_LIMIT_INFORMATION lim; lim.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_PROCESS_MEMORY; lim.ProcessMemoryLimit = 1024 * 1024 * 10; SetInformationJobObject(hJob, JobObjectExtendedLimitInformation, &lim, sizeof(lim)); // ------------------- WSADATA wsaData; int nWSARes = WSAStartup(MAKEWORD(2, 2), &wsaData); if (nWSARes != NO_ERROR) { return nWSARes; } WNDCLASS lpWndClass; lpWndClass.cbClsExtra = 0; lpWndClass.cbWndExtra = 0; lpWndClass.hbrBackground = 0; lpWndClass.hCursor = 0; lpWndClass.hIcon = 0; lpWndClass.hInstance = hInstance; lpWndClass.lpfnWndProc = WindowProc; lpWndClass.lpszClassName = _TEXT("SendWave"); lpWndClass.lpszMenuName = 0; lpWndClass.style = 0; RegisterClass(&lpWndClass); hMainWnd = CreateWindowEx(0, lpWndClass.lpszClassName, lpWndClass.lpszClassName, 0, 0, 0, 0, 0, HWND_MESSAGE, NULL, NULL, NULL); MSG msg; BOOL bRet; while ((bRet = GetMessage(&msg, hMainWnd, 0, 0)) != 0) { if (bRet == -1) { break; } DispatchMessage(&msg); } WSACleanup(); return 0; }
http://www.ethereal.com/download.html Нужно сделать полный лог, включающий моменты установления соединения между клиентом-сервером и до его "внезапного" обрыва. Плюс к этому желательно укажите IP адреса клиента и сервера, чтоб не тратить время.
192.168.1.1 - клиент (кто "дозванивался") 192.168.1.2 - сервер (куда "дозванивались") Порт 22221 используется программой LANPhone Порт 22222 используется RecvWave и SendWave При логе я поставил фильтр, чтобы отслеживались только эти IP адреса.
Хм, в конце лога все нормально, т.е. даже такое впечатление, что соединение не порвалось. По крайней мере нет ни FIN'ов, ни TCP ретрансмитта с какой-либо стороны в следствии неких тайм-аутов. Лог действительно создавался вплоть _до_ разрыва связи?(т.е. включая и разрыв) А вообще, у меня было предположение, что на каком-то этапе общения происходит рассинхронизация и одна из сторон "долбится" в уже закрытый порт.
Да, лог закрывался уже после того, как были закрыты обе программы. Рассинхронизация в принципе не может быть. Клиент посылает данные, а сервер отвечает. После того, как сервер ответил, клиент снова посылает и т.д. Тут почему-то файервол мешает. Без него все нормально.