【发布时间】:2014-06-09 08:44:02
【问题描述】:
我需要从 TCP 服务器发送简单数据到环回连接的多个客户端。 前两个客户端的发送命令成功,但当我尝试连接第三个客户端时它失败并出现错误 10038。
代码很简单,在第一个连接上创建一个线程,然后对于其他连接,打开的套接字被添加到一个全局数组中。 该线程将相同的数据(字符串“hello”)发送到所有连接的客户端(通过 localhost)。
我犯了一个小错误吗?有什么我不知道的吗?为什么它只适用于两个连接?
编辑:
我想强调一下,我发布的代码只是一个示例,它并不是写成一个真正的服务器,它只是想显示一个我不明白的奇怪症状,我的意思是,为什么当第三个客户端连接到服务器发送失败,错误 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;
}
【问题讨论】:
-
您是否考虑过查找 Winsock 错误 10038 的含义?
-
我会使用一个线程池,将一个新连接定向到下一个可用线程。当一个线程完成时,它会返回到池中而不是杀死它。这可以节省大量时间,因为不必重复创建/销毁线程。你可能想看看:softpixel.com/~cwright/programming/threads/threads.c.php 和 cs.wustl.edu/~schmidt/PDF/C++-report-col6.pdf
标签: c windows sockets localhost