由于使用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;//終止回圈退出程式
}
}
}
uj5u.com熱心網友回復:
開啟執行緒,專門select recv訊息。你這個OnCreate 完全是阻塞的,while死回圈啊。當然開執行緒,呼叫OnCreate 是可以的。那樣就不卡主界面 了
uj5u.com熱心網友回復:
socket select函式的詳細講解uj5u.com熱心網友回復:
不是說Select模型可以實作一對多通信嗎,難道單執行緒就無法實作我上面的程式嗎。
我又試了一下,上面的程式能夠接收到客戶端的訊息,但是只能接收到客戶端發的第一條訊息,然后客戶端就顯示“正常關閉連接”,應該是服務器端的Select模型觸發了客戶端的FD_CLOSE事件,我在網上看說Select模型會發送0位元組的心跳資料包給客戶端,難道是這個原因嗎。
而且服務器端啟動后,一直處于阻塞狀態,我在服務器端加ioctlsocket(newsock,FIONBIO,&nonBlock);仍然也是阻塞狀態。
而且上面的程式只能用AfxMessageBox( msgbuffer);接收訊息,卻不能用c_recvbuf.AddString( msgbuffer );將訊息添加到串列框中,不知道為啥。
uj5u.com熱心網友回復:
你OnCreate在主執行緒里呼叫.UI補uj5u.com熱心網友回復:
你OnCreate在主執行緒里呼叫.UI被阻塞了所以接收不到,只能再開一個執行緒uj5u.com熱心網友回復:
OnCreate是一個按鈕事件,有個ID是IDC_Create的按鈕。uj5u.com熱心網友回復:
你這個說法都是錯誤的. "select()模型是非阻塞的但并不是異步"非阻塞, 就是為了實作異步的.
只你跟你要求的異步不同, 你需要recv收到訊息后, 把訊息內容POST到主執行緒, 再添加到訊息框中.
解決辦法有幾種:
1. recv后, 直接把訊息內容通過PostMessage發送的UI執行緒(你的主視窗), 然后把訊息內容決議出來, 添加到訊息框中.
2. recv后, 添加到一個訊息佇列中. 另啟動一個執行緒,專門負責從訊息佇列中取出訊息, 然后添加到訊息框中
uj5u.com熱心網友回復:
基本懂了,Select模型如果要撰寫Windows界面程式必須使用多執行緒,否則就會把程式界面執行緒堵塞。Select是非阻塞模型,非阻塞并不一定是異步模型,但異步模型一定是非阻塞的。轉載請註明出處,本文鏈接:https://www.uj5u.com/gongcheng/57552.html
標籤:網絡編程
上一篇:matlab自帶timeit嗎
下一篇:SVM識別時間問題
