由于使用select()模型可以实现TCP的一对多通信,所以我想使用select()模型制作一个群聊软件,服务器端可以接收所有客户端发来的消息,并将接收的消息显示到列表框中。可问题是select()模型没法自动接收消息,因为select()模型是非阻塞的但并不是异步的。
我从书上看了一个控制台界面的服务器端程序,由于控制台程序接收到消息后会自动弹出,所以不存在上面的问题,可我想把它改成MFC版的程序,就出现了上面的问题,要怎么样让任意客户端发来消息后,服务器端都会自动显示消息在列表框中呢。下面是我的程序,要怎么修改好啊。void CSelwinsDlg::OnCreate()
{
/***初始化winsock2.DLL***/
WSADATA wsaData;
WORD wVersionRequested=MAKEWORD(2,2); //生成版本号2.2
if(WSAStartup(wVersionRequested,&wsaData)!=0)
{
c_recvbuf.AddString("加载winsock.dll失败!\n");}
/***创建套接字***/
if ((sock_server = socket(AF_INET,SOCK_STREAM,0))<0)
{
c_recvbuf.AddString("创建套接字失败!\n");
WSACleanup();}
/***填写要绑定的本地地址***/
int addr_len = sizeof(struct sockaddr_in);
memset((void *)&addr,0,addr_len);
addr.sin_family =AF_INET;
addr.sin_port = htons(PORT);
addr.sin_addr.s_addr = htonl(INADDR_ANY);//允许套接字使用本机的任何IP
/***给监听套接字绑定地址***/
if(bind(sock_server,( struct sockaddr *)&addr,sizeof(addr))!=0)
{
c_recvbuf.AddString("地址绑定失败!\n");
closesocket(sock_server);
WSACleanup();}
/***将套接字设为监听状态****/
if(listen(sock_server,0)!=0)
{
c_recvbuf.AddString("listen函数调用失败!\n");
closesocket(sock_server);
WSACleanup();
}
else
c_recvbuf.AddString("listenning......\n");
FD_ZERO(&fdsock);//初始化fdsock
FD_SET(sock_server, &fdsock);//将监听套接字加入到套接字集合fdsock
/***循环:接收连接请求并收发数据***/
while(true)
{
FD_ZERO(&fdread);//初始化fdread
fdread=fdsock;//将fdsock中的所有套接字添加到fdread中
if(select(0, &fdread, NULL, NULL, NULL)>0)
{
for(int i=0;i<fdsock.fd_count;i++)
{
if (FD_ISSET(fdsock.fd_array[i], &fdread))
{
if(fdsock.fd_array[i]==sock_server)
{ //有客户连接请求到达,接收连接请求
newsock=accept (sock_server, (struct sockaddr *) &client_addr, &addr_len);
if(newsock==INVALID_SOCKET)
{ //accept出错终止所有通信,结束程序
c_recvbuf.AddString("accept函数调用失败!\n");
for(int j=0;j<fdsock.fd_count;j++)
closesocket(fdsock.fd_array[j]); //关闭所有套接字
WSACleanup();//注销WinSock动态链接库
}
else
{
c_recvbuf.AddString(inet_ntoa(client_addr.sin_addr));
send(newsock,msg,sizeof(msg),0) ;//发送一段信息
FD_SET(newsock, &fdsock);//将新套接字加入fdsock
}
}
else
{ //有客户发来数据,接收数据
memset((void *) msgbuffer,0, sizeof(msgbuffer));//缓冲区清零
int size=recv(fdsock.fd_array[i],msgbuffer,sizeof(msgbuffer),0);
if(size<0) //接收信息
c_recvbuf.AddString("接收信息失败!");
else if(size==0)
c_recvbuf.AddString("对方已关闭!\n");
else
{ //显示收到信息
getpeername(fdsock.fd_array[i], (struct sockaddr *)&client_addr, &addr_len); //获取对方IP地址
c_recvbuf.AddString( msgbuffer );
}
closesocket(fdsock.fd_array[i]); //关闭套接字
FD_CLR(fdsock.fd_array[i],&fdsock);//清除已关闭套接字
}
}
}
}
else
{
c_recvbuf.AddString("Select调用失败!");
break;//终止循环退出程序
}
}
}
我从书上看了一个控制台界面的服务器端程序,由于控制台程序接收到消息后会自动弹出,所以不存在上面的问题,可我想把它改成MFC版的程序,就出现了上面的问题,要怎么样让任意客户端发来消息后,服务器端都会自动显示消息在列表框中呢。下面是我的程序,要怎么修改好啊。void CSelwinsDlg::OnCreate()
{
/***初始化winsock2.DLL***/
WSADATA wsaData;
WORD wVersionRequested=MAKEWORD(2,2); //生成版本号2.2
if(WSAStartup(wVersionRequested,&wsaData)!=0)
{
c_recvbuf.AddString("加载winsock.dll失败!\n");}
/***创建套接字***/
if ((sock_server = socket(AF_INET,SOCK_STREAM,0))<0)
{
c_recvbuf.AddString("创建套接字失败!\n");
WSACleanup();}
/***填写要绑定的本地地址***/
int addr_len = sizeof(struct sockaddr_in);
memset((void *)&addr,0,addr_len);
addr.sin_family =AF_INET;
addr.sin_port = htons(PORT);
addr.sin_addr.s_addr = htonl(INADDR_ANY);//允许套接字使用本机的任何IP
/***给监听套接字绑定地址***/
if(bind(sock_server,( struct sockaddr *)&addr,sizeof(addr))!=0)
{
c_recvbuf.AddString("地址绑定失败!\n");
closesocket(sock_server);
WSACleanup();}
/***将套接字设为监听状态****/
if(listen(sock_server,0)!=0)
{
c_recvbuf.AddString("listen函数调用失败!\n");
closesocket(sock_server);
WSACleanup();
}
else
c_recvbuf.AddString("listenning......\n");
FD_ZERO(&fdsock);//初始化fdsock
FD_SET(sock_server, &fdsock);//将监听套接字加入到套接字集合fdsock
/***循环:接收连接请求并收发数据***/
while(true)
{
FD_ZERO(&fdread);//初始化fdread
fdread=fdsock;//将fdsock中的所有套接字添加到fdread中
if(select(0, &fdread, NULL, NULL, NULL)>0)
{
for(int i=0;i<fdsock.fd_count;i++)
{
if (FD_ISSET(fdsock.fd_array[i], &fdread))
{
if(fdsock.fd_array[i]==sock_server)
{ //有客户连接请求到达,接收连接请求
newsock=accept (sock_server, (struct sockaddr *) &client_addr, &addr_len);
if(newsock==INVALID_SOCKET)
{ //accept出错终止所有通信,结束程序
c_recvbuf.AddString("accept函数调用失败!\n");
for(int j=0;j<fdsock.fd_count;j++)
closesocket(fdsock.fd_array[j]); //关闭所有套接字
WSACleanup();//注销WinSock动态链接库
}
else
{
c_recvbuf.AddString(inet_ntoa(client_addr.sin_addr));
send(newsock,msg,sizeof(msg),0) ;//发送一段信息
FD_SET(newsock, &fdsock);//将新套接字加入fdsock
}
}
else
{ //有客户发来数据,接收数据
memset((void *) msgbuffer,0, sizeof(msgbuffer));//缓冲区清零
int size=recv(fdsock.fd_array[i],msgbuffer,sizeof(msgbuffer),0);
if(size<0) //接收信息
c_recvbuf.AddString("接收信息失败!");
else if(size==0)
c_recvbuf.AddString("对方已关闭!\n");
else
{ //显示收到信息
getpeername(fdsock.fd_array[i], (struct sockaddr *)&client_addr, &addr_len); //获取对方IP地址
c_recvbuf.AddString( msgbuffer );
}
closesocket(fdsock.fd_array[i]); //关闭套接字
FD_CLR(fdsock.fd_array[i],&fdsock);//清除已关闭套接字
}
}
}
}
else
{
c_recvbuf.AddString("Select调用失败!");
break;//终止循环退出程序
}
}
}
解决方案 »
- 请问在用Hook的时候,如何判断鼠标是否在标题栏上?
- 学孙老师例子发生的问题
- [提问]如何实现控件的显示输出!!(急急急)
- 调用WSASocket()函数出错,为什么啊?
- Web页面中的ActiveX控件怎样知道浏览器是中文还是英文环境的呢?
- 怎样打开CSDN电子版
- 请问如何将coreldraw嵌入我们的程序中
- 请问在对话框显示位图怎样才可以分层啊?(可否用Picture控件啊)
- 在没有office的机器上不能生成word文件,提示创建word程序失败.怎样解决?
- 求助:VC++6.0中new的BUG问题的解决方案?
- MFC picture无法显示 HBITMAP
- windbg的attach to process不能用总是灰色的怎么回事
你这个OnCreate 完全是阻塞的,while死循环啊。当然开线程,调用OnCreate 是可以的。那样就不卡主界面 了
不是说Select模型可以实现一对多通信吗,难道单线程就无法实现我上面的程序吗。
我又试了一下,上面的程序能够接收到客户端的消息,但是只能接收到客户端发的第一条消息,然后客户端就显示“正常关闭连接”,应该是服务器端的Select模型触发了客户端的FD_CLOSE事件,我在网上看说Select模型会发送0字节的心跳数据包给客户端,难道是这个原因吗。而且服务器端启动后,一直处于阻塞状态,我在服务器端加ioctlsocket(newsock,FIONBIO,&nonBlock);仍然也是阻塞状态。而且上面的程序只能用AfxMessageBox( msgbuffer);接收消息,却不能用c_recvbuf.AddString( msgbuffer );将消息添加到列表框中,不知道为啥。
非阻塞, 就是为了实现异步的.只你跟你要求的异步不同, 你需要recv收到消息后, 把消息内容POST到主线程, 再添加到消息框中.
解决办法有几种:
1. recv后, 直接把消息内容通过PostMessage发送的UI线程(你的主窗口), 然后把消息内容解析出来, 添加到消息框中.
2. recv后, 添加到一个消息队列中. 另启动一个线程,专门负责从消息队列中取出消息, 然后添加到消息框中