socket 多客户端问题

时间:2020-12-20 23:54:39

我现在已经实现了socket客户端和服务端的通讯,但是多个客户端的就出现问题,因为在

onAccept函数中我是这样的
m_ClientSocket = accept(m_ServerSocket,(LPSOCKADDR)&client,&ClientLen);
所以最新的连接总是覆盖了以前的连接,所有服务端向客户端发消息是,只能发给最新连接的客户端。

我在网上看了,有人提议用数组或多线程。

多线程我没做过,一点都不懂。

服务端的流程:
WSAStartup,socket,bind,然后WSAAsyncSelect,调用自定义的消息OnAccept和OnReadClose,然后listen。
在OnAccept中调用m_ClientSocket = accept(m_ServerSocket,(LPSOCKADDR)&client,&ClientLen);
在onAccept里面还有一个OnReadClose自定义消息

还有一个button发送消息函数

15 个解决方案

#1


你用一个数组保存m_ClientSocket,每来一个,在数组中找一个空的SOCKET,就可以了

#2


最好是做个链表.这样删除,添加都比较方便.

#3


引用 1 楼 thirddata 的回复:
你用一个数组保存m_ClientSocket,每来一个,在数组中找一个空的SOCKET,就可以了


那服务端向客户端发消息,怎么判断是那个m_ClientSocket?

#4


对,多线程,还没有见到过其他的解决办法。

在服务器端建立监听主线程,如果来一个客户端的连接,就建立一个处理线程。同时把该连接的Socket传递到新的线程处理函数中去。因为同一进程的每一个线程都有自己的私有堆栈空间,所以你可以不用考虑你提的覆盖问题。

具体的情况还要看你的客户端是怎么连接的,你得考虑客户端如果有重复连接的情况下该怎么处理,还要考虑针对客户端连接建立的线程的退出问题等。如果有过多的客户端连接的话,而创建的线程都又一直运行的话,那么程序运行到最后会出问题的。

网上这方面的资料很多。

#5



能不能讲详细点,因为我对线程一点都不懂

#6


我讲的不够详细吗?!
在这里用文字给你说怎么都无法说明白。要想真的明白得自己看东西,写代码,调试最后才能真正的明白。

#7


嗯,我觉得一般网友提示你用多线程或者是数组方式可以解决问题的思路,你就应该自己去努力了,否则你无法进步。

#8


引用 4 楼 cftxlin 的回复:
对,多线程,还没有见到过其他的解决办法。

在服务器端建立监听主线程,如果来一个客户端的连接,就建立一个处理线程。同时把该连接的Socket传递到新的线程处理函数中去。因为同一进程的每一个线程都有自己的私有堆栈空间,所以你可以不用考虑你提的覆盖问题。

具体的情况还要看你的客户端是怎么连接的,你得考虑客户端如果有重复连接的情况下该怎么处理,还要考虑针对客户端连接建立的线程的退出问题等。如果有过多的客户…

已经说的很详细了,找些代码试试看

#9



我看也在网上看了一些,不过还是不明白,太菜了,呵呵

首先就是在accept后建立线程

m_ClientSocket = accept(m_ServerSocket,(LPSOCKADDR)&client,&ClientLen);
RECVPARAM   *pRecvParam=new   RECVPARAM; 
pRecvParam-> sock=m_ClientSocket; 
pRecvParam-> hwnd=m_hWnd;
HANDLE   hThread=CreateThread(NULL,0,RecvProc,(LPVOID)pRecvParam,0,NULL);

DWORD WINAPI CDogServerDlg::RecvProc(LPVOID lpParameter)
{
//取得参数客户端socket和当前窗口句柄 
SOCKET sock = ((RECVPARAM*)lpParameter)->sock;
HWND hwnd = ((RECVPARAM*)lpParameter)->hwnd;
delete lpParameter;//释放内存

char buf[100];
memset(buf,0,sizeof(buf));
if (sock == INVALID_SOCKET)
{
strcpy(buf,"socket连接失败!");
::PostMessage(hwnd,WM_RECVMESSAGE,0,(LPARAM)buf);
return 0L;
}
// SOCKADDR_IN clientAddr;
WSAAsyncSelect(sock,hwnd,WM_CLIENT_READCLOSE,FD_READ|FD_CLOSE); 
return 0L;
}

在WM_CLIENT_READCLOSE消息里面又创建一个线程来收客户端发来的消息

RECVPARAM   *pRecvParam=new   RECVPARAM; 
pRecvParam-> sock=sock; 
pRecvParam-> hwnd=m_hWnd;
HANDLE   hThread=CreateThread(NULL,0,ReadThreadProc,(LPVOID)pRecvParam,0,NULL); 
CloseHandle(hThread);

DWORD   WINAPI CDogServerDlg::ReadThreadProc(LPVOID pParam)
{
//取得参数客户端socket和当前窗口句柄 
SOCKET   sock=((RECVPARAM*)pParam)-> sock; 
HWND   hwnd=((RECVPARAM*)pParam)-> hwnd; 
delete   pParam; //释放内存的操作。

char   tempBuf[100];
memset(tempBuf,0,sizeof(tempBuf));
if (recv(sock,(char*)&tempBuf,sizeof(tempBuf),0) == SOCKET_ERROR)
{
strcpy(tempBuf, "socket接受数据错误! "); 
::PostMessage(hwnd,WM_RECVMESSAGE,0,(LPARAM)tempBuf); 
return   1;
}
//strcpy(tempBuf, "socket接受数据! "); 
::PostMessage(hwnd,WM_RECVMESSAGE,0,(LPARAM)tempBuf); 
 
// send(sock,(char *)&tempBuf,sizeof(tempBuf),0); 
return   0;
}


现在发送消息我是响应一个button按钮,然后又创建一个线程来发消息

RECVPARAM   *pRecvParam=new   RECVPARAM; 
pRecvParam-> sock=m_ClientSocket; 
pRecvParam-> hwnd=m_hWnd;
strcpy((char*)pRecvParam->msg,"服务端:"+m_Edit);
HANDLE   hThread=CreateThread(NULL,0,SendMessageToClient,(LPVOID)pRecvParam,0,NULL); 
CloseHandle(hThread);

DWORD WINAPI CDogServerDlg::SendMessageToClient(LPVOID pParam)
{
SOCKET   sock=((RECVPARAM*)pParam)-> sock; 
HWND   hwnd=((RECVPARAM*)pParam)-> hwnd;
delete   pParam; //释放内存的操作。

char tempBuf[100];
//tempBuf[100]=((RECVPARAM*)pParam)->msg;
strcpy(tempBuf,((RECVPARAM*)pParam)->msg);
send(sock,(char *)&tempBuf,sizeof(tempBuf),0); 
return   0;
}


这样写对吗?

#10



我看四楼的意思是每个连接accept只创建一个线程

#11


你的不对。
建议你看一下那个Socket编程的原理和线程编程的结构是怎么回事。
一个简单的结构如下,你参考一下:
//服务器端
listen(...)
while(1)
{
    if (select(....)) //用select()或WSAAsynSelect()可以获知何时有数据到达,也就是有连接新的客户端连接
     {
       Socket New_socket = accept(....);
      CreateThread(..New_socket.);
     }
}
//针对一个客户端连接的处理。
ThreadProc()
{
    //运用刚建立的Socket进行所需要的操作。
     while(1)
    {
       if (RevData(...))
       {
           }
    }
    Sleep(50);
     //Socket建立起来后,就相当于服务器端和客户端建立起了一条无形的能道。其中Socket就相当于这条通讯通道的标识。
}

这里的多线程处理就是指的是服务器端针对每一个连接的客户端来建立一个处理线程,而不是客户端的发送数据,要用一个线程,我觉得没有什么意义,建议你再了解一个什么是客户端/服务器的结构是怎么回事。

#12


onAccept函数中我是这样的 
m_ClientSocket = accept(m_ServerSocket,(LPSOCKADDR)&client,&ClientLen);

将m_ClientSocket换成一个List或是Array。
要不就这样

CSocket NewSock = accept(m_ServerSocket,(LPSOCKADDR)&client,&ClientLen);
启动新线程将NewSock传递到线程函数中。

或是:在ListenSocket中的OnAccept中New出ClientSocket,对每个客户端New一个CClientSocket。
当客户端断开连接时释放到这个ClientSocket。
void CListenSocket::OnAccept(int nErrorCode) 
{
CreateClientSocket();
CAsyncSocket::OnAccept(nErrorCode);
}


BOOL CListenSocket::CreateClientSocket()
{
CClientSocket* pClient = new CClientSocket(m_hwndParent);
if(Accept(*pClient)) {
m_listConnection.AddTail(pClient);
}
else {
delete pClient;
return FALSE;
}
return TRUE;
}

#13



我明白你的意思,有一个客户端连接请求,在服务端就建立一个线程。
然后对这个客户的处理都在这个线程里面进行处理,不管是接收消息还是发送消息。
是这样吗??

但是我的要求是要在服务端的button按钮事件中,把一个文本框中的消息发送到一个特定的客户端,
这样怎么处理呢???

button按钮事件和那个线程能够关系起来吗???

#14



我明白你的意思,有一个客户端连接请求,在服务端就建立一个线程。
然后对这个客户的处理都在这个线程里面进行处理,不管是接收消息还是发送消息。
是这样吗??

但是我的要求是要在服务端的button按钮事件中,把一个文本框中的消息发送到一个特定的客户端,
这样怎么处理呢???

button按钮事件和那个线程能够关系起来吗???

#15


用线程的句柄.每个客户端对应一个线程,那应该用个数组把这些线程的句柄保存起来,方便后面使用.

#1


你用一个数组保存m_ClientSocket,每来一个,在数组中找一个空的SOCKET,就可以了

#2


最好是做个链表.这样删除,添加都比较方便.

#3


引用 1 楼 thirddata 的回复:
你用一个数组保存m_ClientSocket,每来一个,在数组中找一个空的SOCKET,就可以了


那服务端向客户端发消息,怎么判断是那个m_ClientSocket?

#4


对,多线程,还没有见到过其他的解决办法。

在服务器端建立监听主线程,如果来一个客户端的连接,就建立一个处理线程。同时把该连接的Socket传递到新的线程处理函数中去。因为同一进程的每一个线程都有自己的私有堆栈空间,所以你可以不用考虑你提的覆盖问题。

具体的情况还要看你的客户端是怎么连接的,你得考虑客户端如果有重复连接的情况下该怎么处理,还要考虑针对客户端连接建立的线程的退出问题等。如果有过多的客户端连接的话,而创建的线程都又一直运行的话,那么程序运行到最后会出问题的。

网上这方面的资料很多。

#5



能不能讲详细点,因为我对线程一点都不懂

#6


我讲的不够详细吗?!
在这里用文字给你说怎么都无法说明白。要想真的明白得自己看东西,写代码,调试最后才能真正的明白。

#7


嗯,我觉得一般网友提示你用多线程或者是数组方式可以解决问题的思路,你就应该自己去努力了,否则你无法进步。

#8


引用 4 楼 cftxlin 的回复:
对,多线程,还没有见到过其他的解决办法。

在服务器端建立监听主线程,如果来一个客户端的连接,就建立一个处理线程。同时把该连接的Socket传递到新的线程处理函数中去。因为同一进程的每一个线程都有自己的私有堆栈空间,所以你可以不用考虑你提的覆盖问题。

具体的情况还要看你的客户端是怎么连接的,你得考虑客户端如果有重复连接的情况下该怎么处理,还要考虑针对客户端连接建立的线程的退出问题等。如果有过多的客户…

已经说的很详细了,找些代码试试看

#9



我看也在网上看了一些,不过还是不明白,太菜了,呵呵

首先就是在accept后建立线程

m_ClientSocket = accept(m_ServerSocket,(LPSOCKADDR)&client,&ClientLen);
RECVPARAM   *pRecvParam=new   RECVPARAM; 
pRecvParam-> sock=m_ClientSocket; 
pRecvParam-> hwnd=m_hWnd;
HANDLE   hThread=CreateThread(NULL,0,RecvProc,(LPVOID)pRecvParam,0,NULL);

DWORD WINAPI CDogServerDlg::RecvProc(LPVOID lpParameter)
{
//取得参数客户端socket和当前窗口句柄 
SOCKET sock = ((RECVPARAM*)lpParameter)->sock;
HWND hwnd = ((RECVPARAM*)lpParameter)->hwnd;
delete lpParameter;//释放内存

char buf[100];
memset(buf,0,sizeof(buf));
if (sock == INVALID_SOCKET)
{
strcpy(buf,"socket连接失败!");
::PostMessage(hwnd,WM_RECVMESSAGE,0,(LPARAM)buf);
return 0L;
}
// SOCKADDR_IN clientAddr;
WSAAsyncSelect(sock,hwnd,WM_CLIENT_READCLOSE,FD_READ|FD_CLOSE); 
return 0L;
}

在WM_CLIENT_READCLOSE消息里面又创建一个线程来收客户端发来的消息

RECVPARAM   *pRecvParam=new   RECVPARAM; 
pRecvParam-> sock=sock; 
pRecvParam-> hwnd=m_hWnd;
HANDLE   hThread=CreateThread(NULL,0,ReadThreadProc,(LPVOID)pRecvParam,0,NULL); 
CloseHandle(hThread);

DWORD   WINAPI CDogServerDlg::ReadThreadProc(LPVOID pParam)
{
//取得参数客户端socket和当前窗口句柄 
SOCKET   sock=((RECVPARAM*)pParam)-> sock; 
HWND   hwnd=((RECVPARAM*)pParam)-> hwnd; 
delete   pParam; //释放内存的操作。

char   tempBuf[100];
memset(tempBuf,0,sizeof(tempBuf));
if (recv(sock,(char*)&tempBuf,sizeof(tempBuf),0) == SOCKET_ERROR)
{
strcpy(tempBuf, "socket接受数据错误! "); 
::PostMessage(hwnd,WM_RECVMESSAGE,0,(LPARAM)tempBuf); 
return   1;
}
//strcpy(tempBuf, "socket接受数据! "); 
::PostMessage(hwnd,WM_RECVMESSAGE,0,(LPARAM)tempBuf); 
 
// send(sock,(char *)&tempBuf,sizeof(tempBuf),0); 
return   0;
}


现在发送消息我是响应一个button按钮,然后又创建一个线程来发消息

RECVPARAM   *pRecvParam=new   RECVPARAM; 
pRecvParam-> sock=m_ClientSocket; 
pRecvParam-> hwnd=m_hWnd;
strcpy((char*)pRecvParam->msg,"服务端:"+m_Edit);
HANDLE   hThread=CreateThread(NULL,0,SendMessageToClient,(LPVOID)pRecvParam,0,NULL); 
CloseHandle(hThread);

DWORD WINAPI CDogServerDlg::SendMessageToClient(LPVOID pParam)
{
SOCKET   sock=((RECVPARAM*)pParam)-> sock; 
HWND   hwnd=((RECVPARAM*)pParam)-> hwnd;
delete   pParam; //释放内存的操作。

char tempBuf[100];
//tempBuf[100]=((RECVPARAM*)pParam)->msg;
strcpy(tempBuf,((RECVPARAM*)pParam)->msg);
send(sock,(char *)&tempBuf,sizeof(tempBuf),0); 
return   0;
}


这样写对吗?

#10



我看四楼的意思是每个连接accept只创建一个线程

#11


你的不对。
建议你看一下那个Socket编程的原理和线程编程的结构是怎么回事。
一个简单的结构如下,你参考一下:
//服务器端
listen(...)
while(1)
{
    if (select(....)) //用select()或WSAAsynSelect()可以获知何时有数据到达,也就是有连接新的客户端连接
     {
       Socket New_socket = accept(....);
      CreateThread(..New_socket.);
     }
}
//针对一个客户端连接的处理。
ThreadProc()
{
    //运用刚建立的Socket进行所需要的操作。
     while(1)
    {
       if (RevData(...))
       {
           }
    }
    Sleep(50);
     //Socket建立起来后,就相当于服务器端和客户端建立起了一条无形的能道。其中Socket就相当于这条通讯通道的标识。
}

这里的多线程处理就是指的是服务器端针对每一个连接的客户端来建立一个处理线程,而不是客户端的发送数据,要用一个线程,我觉得没有什么意义,建议你再了解一个什么是客户端/服务器的结构是怎么回事。

#12


onAccept函数中我是这样的 
m_ClientSocket = accept(m_ServerSocket,(LPSOCKADDR)&client,&ClientLen);

将m_ClientSocket换成一个List或是Array。
要不就这样

CSocket NewSock = accept(m_ServerSocket,(LPSOCKADDR)&client,&ClientLen);
启动新线程将NewSock传递到线程函数中。

或是:在ListenSocket中的OnAccept中New出ClientSocket,对每个客户端New一个CClientSocket。
当客户端断开连接时释放到这个ClientSocket。
void CListenSocket::OnAccept(int nErrorCode) 
{
CreateClientSocket();
CAsyncSocket::OnAccept(nErrorCode);
}


BOOL CListenSocket::CreateClientSocket()
{
CClientSocket* pClient = new CClientSocket(m_hwndParent);
if(Accept(*pClient)) {
m_listConnection.AddTail(pClient);
}
else {
delete pClient;
return FALSE;
}
return TRUE;
}

#13



我明白你的意思,有一个客户端连接请求,在服务端就建立一个线程。
然后对这个客户的处理都在这个线程里面进行处理,不管是接收消息还是发送消息。
是这样吗??

但是我的要求是要在服务端的button按钮事件中,把一个文本框中的消息发送到一个特定的客户端,
这样怎么处理呢???

button按钮事件和那个线程能够关系起来吗???

#14



我明白你的意思,有一个客户端连接请求,在服务端就建立一个线程。
然后对这个客户的处理都在这个线程里面进行处理,不管是接收消息还是发送消息。
是这样吗??

但是我的要求是要在服务端的button按钮事件中,把一个文本框中的消息发送到一个特定的客户端,
这样怎么处理呢???

button按钮事件和那个线程能够关系起来吗???

#15


用线程的句柄.每个客户端对应一个线程,那应该用个数组把这些线程的句柄保存起来,方便后面使用.