我想写一个 TCP 一对多通信的程序,就是服务器端利用多线程技术(不使用Select等任何模型), 服务器端能将消息群发给所有已连接的客户端, 我参考了其他一些程序,发现基本思路 是将每个线程中 accept函数返回的套接字 都保存到一个套接字数组中,套接字地址也保存到一个数组中。然后在“群发”的 按钮消息中,用for循环 遍历套接字数组,循环执行 send函数将消息发送给套接字数组中所有的套接字。 可我用send()函数发送就产生SOCKET_ERROR错误,输出错误代码是10038,无效的套接字,说明套接字根本没保存到套接字数组中去,用list结构保存套接字也是这样,不行,请问这是为什么,我的关键代码如下:。
BOOL CListWinDlg::OnInitDialog()
{
CDialog::OnInitDialog(); SetIcon(m_hIcon, TRUE); // Set big icon
SetIcon(m_hIcon, FALSE); // Set small icon

WSADATA wsaData;  
    WORD sockVersion=MAKEWORD(2,2);  
  
sockSer=socket(AF_INET,SOCK_STREAM,0);
addrSer.sin_family=AF_INET;
addrSer.sin_port=htons(5566);
addrSer.sin_addr.S_un.S_addr=inet_addr("127.0.0.1");
int len =sizeof(SOCKADDR);
bind(sockSer,(SOCKADDR*) &addrSer,len);
listen(sockSer,5);

RECVPARAM *pRecvParam=new RECVPARAM;
pRecvParam->sock=sockSer;
pRecvParam->hwnd=m_hWnd;
HANDLE hThread=CreateThread(NULL,0,WaitProc,(LPVOID)pRecvParam,0,NULL);
CloseHandle(hThread);
return 0;
}
 
DWORD WINAPI CListWinDlg::WaitProc(LPVOID lpParameter)  //接受连接线程
{  
    CListWinDlg *pServer=new CListWinDlg;
SOCKET sock=((RECVPARAM*)lpParameter)->sock;
HWND hwnd=((RECVPARAM*)lpParameter)->hwnd;
SOCKET sockConn;  
SOCKADDR_IN addrCli;
int len =sizeof(SOCKADDR);
int num=0;
while(1)  
{  
Sleep(10);  //这句有什么用?
sockConn=accept(sock,(SOCKADDR*) &addrCli,&len);


if(INVALID_SOCKET == sockConn)  
{  
CString  strNotice = "accept()失败,再次尝试 ...... ";  
::AfxMessageBox(strNotice);  
continue;  
}
else{ AfxMessageBox("一个客户端已成功连接");

// pServer->m_listConn.push_back(addrCli);
// pServer->m_sockConn.push_back(sockConn);
pServer->m_Clients[num]=sockConn; //将套接字保存到数组中,
pServer->addrFrom[num]=addrCli;
num++;
} //将套接字地址保存到数组中
// int num2=sizeof(pServer->m_Clients)/sizeof(pServer->m_Clients[0]);
} return TRUE; 
}
void CListWinDlg::OnSend() 
{
char buff[200];
char * ct;
CTime time = CTime::GetCurrentTime(); //获取当前时间
CString t = time.Format("        %H:%M:%S"); //设置时间显示格式
ct=(char*)t.GetBuffer(0); //cstring 转 char*
c_sendbuf.GetWindowText(buff,200);
c_sendbuf.SetWindowText(NULL);
CString Ser="服务器: >";
strcat(buff,ct);
int iSendFalseCount=0;
    CListWinDlg *pServer=new CListWinDlg;
// for (sockiterator = m_sockConn.begin(); sockiterator != m_sockConn.end(); ++sockiterator)
// for (list<SOCKET>::iterator itr=pServer->m_sockConn.begin();itr!=pServer->m_sockConn.end();itr++)
for(int i=0;i<3;i++)
{

int vetcal=send(pServer->m_Clients[i],buff,strlen(buff)+1,0);
if (vetcal==SOCKET_ERROR)
iSendFalseCount++;
}
iSendFalseCount=WSAGetLastError();
char strtemp[5];
itoa(iSendFalseCount, strtemp, 10);
AfxMessageBox(strtemp);
}
头文件中定义的数组如下:
struct RECVPARAM
{
SOCKET sock;
HWND hwnd;
};
class CListWinDlg : public CDialog
{
// Construction
public:
CListWinDlg(CWnd* pParent = NULL); // standard constructor SOCKET sockSer,sockConn;
SOCKET  m_Clients[10]; //保存套接字的数组
SOCKADDR_IN addrFrom[10];    list<SOCKET> m_sockConn;
list<SOCKADDR_IN> m_listConn;
list<SOCKET>::iterator sockiterator;
SOCKADDR_IN addrSer;
list<string> test;list<string>::iterator testiterator;
static DWORD WINAPI WaitProc(LPVOID lpParameter);
static DWORD WINAPI RecvProc(LPVOID lpParameter);

解决方案 »

  1.   

    有客户端连接到么? send时的SOCKET参数是多少?   TRACE 日志或设置断点查看
      

  2.   

    你在保存和使用的时候分别
    CListWinDlg *pServer=new CListWinDlg;所以你保存和使用的不是同一个对象实例,这两个对象的数据是不共享的
    你应该使用同一个,或者使用静态数组
      

  3.   

    客户端能连接成功,服务器都能接收任何客户端发送的消息,只是服务器不能向客户端发送消息,send时的SOCKET参数是多少?   TRACE 日志或设置断点查看 这些我不知道怎么做
      

  4.   

           CListWinDlg *pServer=new CListWinDlg;
    是一个 与 原 对话框 无关的 新对象 !WaitProc(LPVOID lpParameter)// 传 对话框 窗口 对话框 定义 
     RECVPARAM RecvParam;或者
       RECVPARAM *pRecvParam=new RECVPARAM;
    放 类外
      

  5.   

    知道问题所在了,把保存套接字的数组定义在 chatDlg.cpp中就可以了,原来是定义在chatDlg.h中就不行,听说是为解决连接时重复定义的问题,可为什么放在*dlg.cpp文件中就解决了重复定义的问题呢?还是不明白。
    //*******本来在chatDlg.h中定义的,现在移到这里,为解决连接时重复定义问题
      SOCKET * m_Clients[10]; //保存套接字的数组
    SOCKADDR_IN addrCli[10];
    int num;
    //****************************************************
      

  6.   

    这是已经写好的程序,完全可正常运行。
    https://download.csdn.net/download/wuxia2118/10946379