我需要从 TCP 服务器发送简单数据到环回连接的多个客户端。 前两个客户端的发送命令成功,但当我尝试与第三个客户端连接时,发送命令失败并出现错误 10038。
代码非常简单,在第一个连接上创建一个线程,然后对于其他连接,将打开的套接字添加到全局数组中。 该线程向所有连接的客户端(通过本地主机)发送相同的数据(字符串“hello”)。
我犯了一个小错误吗?有什么我不知道的吗?为什么它只能在两个连接下工作?
编辑:
我想强调一下,我发布的代码只是一个示例,它并不是为了成为真正的服务器而编写的,它只想显示一个我不明白的奇怪症状,我的意思是,为什么当第三个客户端时连接到服务器发送失败并出现错误 10038 (WSAENOTSOCK)?
我的测试(在同一台机器上)发生的情况是:
- 我运行服务器
- 我在端口 5000 上打开 telnet 连接,它 显示带有“Hello”的无限循环,这正是我所期望的。
- 我在端口 5000 上打开第二个 telnet 连接,它显示无限 循环使用“Hello”,这就是我所期望的。
- 我打开了第三个 在端口 5000 上 telnet 连接,连接完全失败 立即,服务器在第三个套接字上显示“错误 10038”。
为什么第三次连接失败?我究竟做错了什么?
我的代码:
#include <winsock2.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/timeb.h>
int nclients = 0;
SOCKET lClient[10];
CRITICAL_SECTION m_cs;
DWORD WINAPI ClientThread(LPVOID lpParam)
{
int i;
char buffer[20] = "Hello\n";
while(1) {
EnterCriticalSection(&m_cs);
for (i=0;i<nclients;i++) {
if (lClient[i] != INVALID_SOCKET) {
if( send(lClient[i], buffer , strlen(buffer) , 0) < 0) {
printf("socket() failed: %d\n", WSAGetLastError());
nclients--;
}
}
}
LeaveCriticalSection(&m_cs);
Sleep(200);
}
return 0;
}
int main(int argc, char **argv) {
WSADATA wsd;
SOCKET sListen, sClient;
int iAddrSize;
HANDLE hThread;
DWORD dwThreadId;
struct sockaddr_in local, client;
int i,iPort;
InitializeCriticalSection(&m_cs);
for (i=0;i<10;i++) lClient[i] = INVALID_SOCKET;
iPort = 5000;
if (WSAStartup(MAKEWORD(2,2), &wsd) != 0)
{
printf("Failed to load Winsock!\n");
return 1;
}
// Create our listening socket
//
sListen = socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
if (sListen == SOCKET_ERROR)
{
printf("socket() failed: %d\n", WSAGetLastError());
return 1;
}
// Select the local interface and bind to it
//
local.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
local.sin_family = AF_INET;
local.sin_port = htons(iPort);
if (bind(sListen, (struct sockaddr *)&local,
sizeof(local)) == SOCKET_ERROR)
{
printf("bind() failed: %d\n", WSAGetLastError());
return 1;
}
listen(sListen, 8);
//
// In a continous loop, wait for incoming clients. Once one
// is detected, create a thread and pass the handle off to it.
//
while (1)
{
iAddrSize = sizeof(client);
sClient = accept(sListen, (struct sockaddr *)&client,&iAddrSize);
if (sClient == INVALID_SOCKET)
{
printf("accept() failed: %d\n", WSAGetLastError());
break;
}
printf("Accepted client: %s:%d\n",inet_ntoa(client.sin_addr), ntohs(client.sin_port));
if (nclients == 0) {
hThread = CreateThread(NULL, 0, ClientThread, 0, 0, &dwThreadId);
EnterCriticalSection(&m_cs);
lClient[nclients] = sClient;
nclients++;
LeaveCriticalSection(&m_cs);
}
else {
EnterCriticalSection(&m_cs);
lClient[nclients] = sClient;
nclients++;
LeaveCriticalSection(&m_cs);
}
if (hThread == NULL)
{
printf("CreateThread() failed: %d\n", GetLastError());
break;
}
CloseHandle(hThread);
}
closesocket(sListen);
WSACleanup();
return 0;
}
最佳答案
您的线程循环在遇到错误时未正确更新数组。并且您的主代码没有处理所有客户端断开连接然后新客户端连接的可能性,因此它可能会创建多个线程。
试试这个:
bool keepRunning = true;
DWORD WINAPI ClientThread(LPVOID lpParam)
{
const char buffer[] = "Hello\n";
const int buflen = strlen(buffer) ;
while (keepRunning)
{
EnterCriticalSection(&m_cs);
int i = 0;
while ((i < nclients) && (keepRunning))
{
if (send(lClient[i], buffer, buflen, 0) == SOCKET_ERROR)
{
int err = WSAGetLastError();
closesocket(lClient[i]);
if (err != WSAENOTSOCK)
printf("Failed to send data to a client! Error: %d\n", err);
for (int j = i+1; j < nclients; ++j)
lClient[j-1] = lClient[j];
--nclients;
}
else
{
++i;
}
}
LeaveCriticalSection(&m_cs);
Sleep(200);
}
return 0;
}
int main(int argc, char **argv)
{
WSADATA wsd;
SOCKET sListen, sClient;
int iAddrSize;
HANDLE hThread = NULL;
DWORD dwThreadId = 0;
struct sockaddr_in local, client;
int iPort;
InitializeCriticalSection(&m_cs);
for (int i = 0; i < 10; ++i) lClient[i] = INVALID_SOCKET;
iPort = 5000;
int ret = WSAStartup(MAKEWORD(2,2), &wsd);
if (ret != 0)
{
printf("Failed to load Winsock! Error: %d\n", ret);
return 1;
}
// Create our listening socket
//
sListen = socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
if (sListen == SOCKET_ERROR)
{
printf("Failed to create listening socket! Error: %d\n", WSAGetLastError());
WSACleanup();
return 1;
}
// Select the local interface and bind to it
//
local.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
local.sin_family = AF_INET;
local.sin_port = htons(iPort);
if (bind(sListen, (struct sockaddr *)&local, sizeof(local)) == SOCKET_ERROR)
{
printf("Failed to bind listening socket! Error: %d\n", WSAGetLastError());
closesocket(sListen);
WSACleanup();
return 1;
}
// Start listening for clients
//
if (listen(sListen, 8) == SOCKET_ERROR)
{
printf("Failed to listen for clients! Error: %d\n", WSAGetLastError());
closesocket(sListen);
WSACleanup();
return 1;
}
//
// In a continuous loop, wait for incoming clients. Once
// one is detected, create a thread to process clients.
//
while (keepRunning)
{
iAddrSize = sizeof(client);
sClient = accept(sListen, (struct sockaddr *)&client, &iAddrSize);
if (sClient == INVALID_SOCKET)
{
printf("Unable to accept a client! Error: %d\n", WSAGetLastError());
break;
}
EnterCriticalSection(&m_cs);
if (nclients == 10)
{
LeaveCriticalSection(&m_cs);
closesocket(sClient);
printf("Ignoring client: %s:%d, too many clients already connected\n", inet_ntoa(client.sin_addr), ntohs(client.sin_port));
continue;
}
lClient[nclients] = sClient;
++nclients;
LeaveCriticalSection(&m_cs);
printf("Accepted client: %s:%d\n", inet_ntoa(client.sin_addr), ntohs(client.sin_port));
if (!hThread)
{
hThread = CreateThread(NULL, 0, &ClientThread, 0, 0, &dwThreadId);
if (hThread == NULL)
{
printf("Unable to create thread! Error: %u\n", GetLastError());
break;
}
}
}
keepRunning = false;
if (hThread != NULL)
{
WaitForSingleObject(hThread, INFINITE);
CloseHandle(hThread);
}
for (int i = 0; i < nclients; ++i)
closesocket(lClient[i]);
closesocket(sListen);
WSACleanup();
return 0;
}
关于c - 从第三个连接开始Winsock错误10038,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24116628/