收录日期:2020/10/29 06:39:07 时间:2016/05/12 05:44:44 标签:基础类
请问大家是如何处理server端的(TCP)?

因为接受到一个connect后,我要用recv()接受客户端的数据,没有数据时,
就会停留在recv()上,等待数据的到来,为了能够接受多个客户的请求,我用
了一个线程,但总不能一个连接开一个线程吧?

各位高人,请大家谈谈处理的方案??
WINSOCK是消息驱动的啊,在收到FD_READ的时候才去RECV嘛,那就不会阻塞了。
我怎么得到FD_READ?我用的是api函数,也是消息驱动的吗??
简单的方法是使用多线程,每个连接使用一个线程来管理,这样程序比较清晰,而且效率也不会太差。适合于较少client使用(300以下我认为都可以)

再复杂点的可以使用select来查询socket状态,这样可以在一个线程里面解决,但是程序比较难写一些。对比度线程的,系统资源消耗比较小(少建立了很多线程)

另外还有完成端口等等,程序部分的结构和select的相似,不过完成端口只有在nt/2k可以用。
NonBlock = 1;
if(ioctlsocket(m_sClient, FIONBIO, &NonBlock) == SOCKET_ERROR)
{
iErrorNo = WSAGetLastError();
swprintf(szErrMsg, L"ioctlsocket() failed with error %d", iErrorNo);
MessageBox(m_hWnd, szErrMsg, L"Socket error", MB_OK|MB_ICONSTOP);
return FALSE;
}

FD_SET  ReadSet;
timeval  TimeOut;

if(iRequestType == 0xB1) TimeOut.tv_sec = 5;
else TimeOut.tv_sec = 2;
TimeOut.tv_usec = 0;
while(1)
{
FD_ZERO(&ReadSet);
FD_SET(m_sClient, &ReadSet);
if((iRet = select(0, &ReadSet, NULL, NULL, &TimeOut)) == SOCKET_ERROR)
{
iErrorNo = WSAGetLastError();
swprintf(szErrMsg, L"select() returned with error %d\n", iErrorNo);
MessageBox(m_hWnd, szErrMsg, L"Socket error", MB_OK|MB_ICONSTOP);
return -1;
}
if(iRet>0 && FD_ISSET(m_sClient, &ReadSet))
{
memset(m_RecvBuf, 0, DEFAULT_BUFSIZE);
iRet = recv(m_sClient, (char*)m_RecvBuf, DEFAULT_BUFSIZE, 0);
if(iRet == 0) break;
else if(iRet == SOCKET_ERROR)
{
iErrorNo = WSAGetLastError();
if (iErrorNo != WSAEWOULDBLOCK)
{
swprintf(szErrMsg, L"Failed to receive message from server! Error No: %d", iErrorNo);
MessageBox(m_hWnd, szErrMsg, L"Socket error", MB_OK|MB_ICONSTOP);
return -1;
}
}
else
{
MessageBox(m_hWnd, L"Time out or some unexcepted error occur!",
L"Socket error", MB_OK|MB_ICONINFORMATION);
return -1;
}
......
具体可以参考MSDN...
Forever兄说的是WINSOCK的WSAEventSelect模型,您也可以使用MFC使用的WSAAsyncSelect模型(这个模型是基于窗口消息的),或者重叠IO模型,或者完成端口模型。当然得看您的具体需要,如性能考虑,平台考虑等。
你可以使用CSocket的消息驱动模型。建立一个监听的socket(从CSocket派生)类,然后建立一个处理数据的socket(从CSocket派生)类。当监听的socket类接受到一个新客户端的连接时,new一个处理数据类,将所有数据的交给它去处理,监听的socket继续监听。这样可以同时处理多个客户端连接,每个客户端由一个数据处理类处理,结构非常清晰。

具体过程参考MSDN中的例子程序chatter和chatsrvr程序。
wVerReq = MAKEWORD( 2, 2 );
err = WSAStartup( wVerReq, &wsaData );
GROUP g = 0;
m_hListen = WSASocket( AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, g, WSA_FLAG_OVERLAPPED );
SOCKADDR_IN IAddr;
IAddr.sin_family = AF_INET;
IAddr.sin_addr.S_un.S_addr = INADDR_ANY;
IAddr.sin_port = htons( LISTEN_PORT );
err = bind( pFrame->m_hListen, (SOCKADDR*)&IAddr, sizeof(IAddr) );
    err = WSAAsyncSelect( pFrame->m_hListen, pFrame->m_hWnd, 
SOCKET_MSG, FD_READ|FD_CLOSE );
然后你可以通过pFrame->m_hWnd这个窗口接收SOCKET_MSG消息,
消息的lParam=FD_READ,wparam=socket.
SOCKET_MSG是我自定义的消息。
采用多线程,用一个线程来监听,如果收到消息,就新建一个线程来接收数据
建立一个监听的socket(从CSocket派生)类,然后建立一个处理数据的socket(从CSocket派生)类。当监听的socket类接受到一个新客户端的连接时,就建一个接收数据的新的socket和它连接,监听的socket继续监听,一般还要建立一个队列保存建立的接收数据的socket
如服务器与客户机只是传送很短的命令,而非大量的数据(如FTP),
那么只用一个线程就够了,也就是只用CSocket的消息驱动(在主线程运行的);
如传送大量的数据,就会影响主线程的稳定了(消息驱动太慢了就使窗口失灵);
这时可以开几个线程来处理;
我用的是多线程,在客户端开1000个连接,只是连接的时候基本上没什么问题,我再让他们发送数据,在线程结束的时候有时就会出错了,是不是线程
设计的问题??还是用这种方法达不到这样的要求???
学习
补充一点,发送的时候问题也不是很大,就是在关闭套接字的时候出的问题,
有时还会有堵塞,接收数据不完整等。
10038错误,
void CMySocket::SocketInit( HWND hWnd )
{
WSAStartup(0x202,&wsaData) ;
temp_Sock = socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
if( temp_Sock == SOCKET_ERROR)
{
exit( 0 ) ;
}

LocalAddr.sin_addr.S_un.S_addr = inet_addr(LOCAL_IP);
LocalAddr.sin_family = AF_INET;
LocalAddr.sin_port = htons(LISTEN_PORT);

stuLen = sizeof( LocalAddr ) ;
rt = bind( temp_Sock, ( const sockaddr* )&LocalAddr, stuLen );
if( rt == SOCKET_ERROR )
{
exit( 0 ) ;
}

if( listen( temp_Sock, 20 ) == SOCKET_ERROR )
{
exit( 0 ) ;
}
WSAAsyncSelect( temp_Sock, hWnd, WM_MYMESSAGE, FD_ACCEPT ) ;

}

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
int wmId, wmEvent;
PAINTSTRUCT ps;
HDC hdc;        
TCHAR szHello[MAX_LOADSTRING];
LoadString(hInst, IDS_HELLO, szHello, MAX_LOADSTRING);
      
switch (message) 
{              
case WM_COMMAND:
wmId    = LOWORD(wParam); 
wmEvent = HIWORD(wParam); 
// Parse the menu selections:
switch (wmId)
{      
case IDM_ABOUT:
   DialogBox(hInst, (LPCTSTR)IDD_ABOUTBOX, hWnd, (DLGPROC)About);
   break;
case IDM_EXIT:
   DestroyWindow(hWnd);
   break;
default:
   return DefWindowProc(hWnd, message, wParam, lParam);
}    
break; 
case WM_PAINT:
hdc = BeginPaint(hWnd, &ps);
// TODO: Add any drawing code here...
RECT rt;
GetClientRect(hWnd, &rt);
DrawText(hdc, szHello, strlen(szHello), &rt, DT_CENTER);
EndPaint(hWnd, &ps);
break;
case WM_DESTROY:
PostQuitMessage(0);
case WM_CREATE:
break;
case WM_MYMESSAGE:
switch (LOWORD(lParam))
{
case FD_ACCEPT:
g_server.ScoketAccept( hWnd ) ;
break ;
case FD_CLOSE:
g_server.ScoketClose( wParam ) ;
break ;
case FD_READ:
g_server.ScoketRecv( wParam ) ;
break ;
}
default:
return DefWindowProc(hWnd, message, wParam, lParam);
   }
   return 0;
}
我都是用SDK写的测试类,如看不明白,给我邮箱,我给你发一份。
DWORD WINAPI ThreadAcceptProc(LPVOID lpParameter)
{
printf("one thread start\n");
SOCKET sock = *(SOCKET*)lpParameter;

struct linger li;
li.l_linger = 10;
li.l_onoff = 10;
setsockopt(sock, SOL_SOCKET, SO_LINGER, (char*)&li, sizeof(struct linger));
char nType[2];
char* pmsg;
char buf[1024];
int nLen, offset;

int re;
do
{
offset = 0;
re = recv(sock, nType, 1, 0);
if(re == 0)
break;
//return 0;
switch(nType[0])
{
case ADD_USER:
printf("add user\n");
c ++;
nLen = sizeof(struct CUserInfo);
break;
default:
continue;
break;
}
pmsg = new char[nLen];
re = recv(sock, buf, nLen, 0);
memcpy(pmsg, buf, re);
offset += re;
while(nLen != re)
{
nLen -= re;
re = recv(sock, buf, nLen, 0);
memcpy(pmsg + offset, buf, re);
offset += re;
}

//PeekMsg(nType[0], pmsg);
delete pmsg;
}while(re > 0);

if(closesocket(sock)!=0) ///////这儿
{
int err = WSAGetLastError();
printf("close thread error:%d : err %d \n", c, err);
}
return 0;
}

上面是我的线程,为什么关闭的时候会有错。。。
,?,
找点资料或书籍来看看吧
有好处的
准备接贴

偶完全装的MSDN,为什么没例子?fuck!!!!!!!!!!!!!!!!!!!!!! 这个世道变了,水园变了,再也不见幽默的取笑和夸张的表现! 如何在flash网页中打开一个新窗口? oracle的date型数据与odbc的问题? 请教:listBox中怎么样用索引号取的条目的内容? 想开个软件公司,没有行业背景又没有关系,能不能成? 我的箱子里有许多发论工信,有什么软件可以拒收吗。 请问谁有sygate注册号啊? cgi 好烦!·! why no reply? 版主多九换一次列? 我在客户端运行有些applet时,总是什么也不显示!是否需要安装一个插件?如何配置?谢了!给分 一个很简单的问题。最好是高手看看。 EJB新手,请教几个概念。 哪位大侠救我呀 完全不会VB,请问各位高手我应该从那学起? 网络速度在98,,,,比windows2000快很多,,why !!1?? 请问现在mcsd 认证怎么样啊。有没有人重视它啊。我现在想读一下。但钱不多。给点建议可以吗? 关于注释行的问题 oracle日志处理 什么涵数可以检测下载的真实熟读? 关于const的问题! 潭好强的书哪儿有?马上结帐! 请教:哪里有警报声的 .wav文件? 了解一下深圳程序员的薪资情况. 怎样才可以改变3ds max4.0的默认字体? 水知道怎么判断socket的状态? 求SQLSERVER 7 的安装密码,或者NT4的安装密码! VB有直接与SQL-SERVER连接的接口吗? 怎样在win2000下修改分辨率!!