socket通信可不可以Server端设成非阻塞socket方式,Client端设成阻塞模式?

sponsored links
精通WindowsSocket网络开发基于VC++实现第五章——非阻塞模式开发
套接字的非阻塞模式是指套接字在执行操作时,调用的函数不管操作是否完成都会立即返回的工作模式。非阻塞套接字在处理同时建立的多个连接,发送和接收的数据量不均,时间不定等方面具有明显的优势。但这种套接字在使用上存在一定难度。本章讲述套接字的非阻塞模式及其一个远程算数运算套接字程序。
套接字的非阻塞模式
所有windows平台都支持套接字以阻塞模式和非阻塞模式的方式工作。
非阻塞模式
把套接字设置为非阻塞模式,即通知系统内核:在调用WindowsSocketsAPI时,不要让线程随眠,而应该让函数理解返回。在返回时,该函数返回一个错误代码。一个非阻塞模式套接字多次调用recv()的过程如上。前3次调用recv()函数时,内核函数还没有准备好。因此,该函数理解返回WSAEWOULDBLOCK错误代码。第4次调用recv()函数时,数据已经准备好,被负责到应用程序的缓冲区中,recv()返回成功指示,应用程序开始处理数据。
设置套接字的非阻塞模式
当使用socket(),WSASocket()创建套接字时,默认都是阻塞的。在创建套接字之后,通过调用ioctlsocket(),将该套接字设置为非阻塞模式。
套接字设置为非阻塞模式后,在调用WindowsSocketsAPI函数时,调用函数会立即返回。大多数情况下,这些函数调用都会调用“失败”,并返回WSAEWOULDBLOCK错误代码。说明请求的操作在调用期间内没有时间完成。通常,应用程序需要重复调用该函数,直到获得成功返回代码。
WSAEWOULDBLOCK的含义
accept(),WSAAcept()
应用程序没有接收到连接请求
recv(),WSARecv(),recvfrom(),WSARecvfrom()
接收缓冲区没有收到数据
send(),WSASend(),sendto(),WSASendto()
发送缓冲区此时不可用
connect(),WSAConnect()
连接未能立即完成
closesocket()
通常情况下意味着应用程序使用SO_LINGER选项并且设置一个非零的超时值,调用了setsocketopt()
需要说明的是并非所有的WindowsSocketsAPI在非阻塞模式下调用,都会返回WSEWOULDBLOCK错误。eg:bind(),listen()。
要将套接字设置为非阻塞模式,除了使用ioctlsocket()函数之外,还可以使用WSAAsyncSelect()(第7章讲解),WSAEventselect()(第8章讲解)。当调用该函数时,套接字会自动地设置为非阻塞方式。
非阻塞模式套接字的优势和不足&
由于使用非阻塞套接字在调用函数时,会经常返回WSAEWOULDBLOCK错误。所以在任何时候,都应仔细检查返回代码,并做好应对“失败”的准备。应用程序连续不断的调用这个函数,直到它返回成功指示为止。本章程序清单中,在While循环体内不断的调用recv(),以读入1024个字节的数据。这种做法很浪费系统资源。
有人使用MSG_PEEK标志调用recv()查看缓冲区中是否有数据可读。同样,这种方法也不好,因为该做法对系统造成的开销是很大的,并且应用程序至少要调用recv()两次,才能实际地读入数据。较好的做法是,使用套接字的“I/O模型”(第6,7,8,9,10章来讲解)来判断非阻塞套接字是否可读可写。
非阻塞模式套接字与阻塞套接字相比,使用较复杂。使用非阻塞模式套接字,需要编写更多的代码,以便在每个WindowsSocketAPI函数调用中,对收到的WSAEWOULDBLOCK错误进行处理。因此,非阻塞套接字便显得有些难于使用。
但是,非阻塞套接字在控制建立的多个连接,在数据的收发量不均,时间不定等方面,明显具有优势。通常情况下,可考虑使用套接字的“I/O模型”,它有助于应用程序通过异步方法,同时对一个或多个套接字的通信加以管理。
远程算数运算程序
本程序实现一个可以同时为多个客户端提供服务的远程算数运算程序。客户端和服务器端都使用非阻塞套接字。
NoBlockServer
[cpp]&view
#pragma&once&&
#include&&WinSock2.h&&&
#define&&TIMEFOR_THREAD_CLIENT&500//线程睡眠时间&&
#define&&MAX_NUM_CLIENT&10//接受的客户端连接最多数量&&
#define&&MAX_NUM_BUF&48//缓冲区的最大长度&&
#define&&INVALID_OPERATOR&1&//无效的操作符&&
#define&&INVALID_NUM&2&//分母为0&&
typedef&struct&_head//数据包包头结构,该结构在win32下位4byte&&
&&&&char&//类型&&
&&&&unsigned&short&//数据包长度(包括头的长度)&&
#define&&HEADERLEN&(sizeof(hdr))//头长度&&
typedef&struct&_data//数据包中数据结构&&
&&&&char&buf[MAX_NUM_BUF];//数据&&
}DATABUF,*pDataB&&
//数据包:包头(类型[2byte]+长度[2byte])+数据;&&
class&Client&&
&&&&Client(const&SOCKET&sClient,const&sockaddr_in&&addrClient);&&
&&&&virtual&~Client(void);&&
&&&&BOOL&StartRunning();//创建和接收数据线程&&
&&&&void&HandleData(const&char*&pExpr);//计算表达式&&
&&&&BOOL&IsConning()//是否连接存在&&
&&&&&&&&return&m_bC&&
&&&&void&DisConning()//断开与客户端的连接&&
&&&&&&&&m_bConning=FALSE;&&
&&&&BOOL&IsExit()//发送和接收线程是否已经退出&&
&&&&&&&&return&m_bE&&
&&&&static&DWORD&_stdcall&RecvDataThread(void*&pParam);//接收客户端数据&&
&&&&static&DWORD&_stdcall&SendDataThread(void*&pParam);//向客户端发送数据&&
&&&&SOCKET&&m_//套接字&&
&&&&sockaddr_in&m_//接收客户端的地址&&
private:&&
&&&&DATABUF&m_//客户端数据;接收线程和发送线程公用该数据变量&&
&&&&HANDLE&m_hE//事件对象。当接收线程完成算数表达式的计数后使用该事件对象,通知发送线程发送数据&&
&&&&HANDLE&m_hThreadS//发送数据线程句柄&&
&&&&HANDLE&m_hThreadR//接收数据线程句柄&&
&&&&CRITICAL_SECTION&m_//临界区对象。确保接收数据线程和发送数据线程对m_data数据成员的互斥访问&&
&&&&BOOL&m_bC//客户端连接状态&&
&&&&BOOL&m_bE//线程退出&&
client.cpp&
[cpp]&view
#include&&StdAfx.h&&&
#include&&process.h&&&
#include&&Client.h&&&
Client::Client(const&SOCKET&sClient,const&sockaddr_in&&addrClient)&&
&&&&m_hThreadRecv=NULL;&&
&&&&m_hThreadSend=NULL;&&
&&&&m_socket=sC&&
&&&&m_addr=addrC&&
&&&&m_bConning=FALSE;&&
&&&&m_bExit=FALSE;&&
&&&&memset(m_data.buf,0,MAX_NUM_BUF);&&
&&&&m_hEvent=CreateEvent(NULL,FALSE,FALSE,NULL);//自动设置信号状态,初始化为无信号状态&&
&&&&InitializeCriticalSection(&m_cs);//初始化临界区&&
Client::~Client(void)&&
&&&&closesocket(m_socket);&&
&&&&m_socket=INVALID_SOCKET;&&
&&&&DeleteCriticalSection(&m_cs);//释放临界区&&
&&&&CloseHandle(m_hEvent);//释放事件对象&&
BOOL&Client::StartRunning()//创建和接收数据线程&&
&&&&m_bConning=TRUE;&&
&&&&if&((m_hThreadRecv=CreateThread(NULL,0,RecvDataThread,this,0,NULL))&==&NULL)&&
&&&&&&&&return&FALSE;&&
&&&&else&&
&&&&&&&&CloseHandle(m_hThreadRecv);//成功创建线程后,调用closeHandle()将该线程句柄的引用记数减1&&
&&&&if&((m_hThreadSend&=&CreateThread(NULL,0,SendDataThread,this,0,NULL))&==&NULL)&&
&&&&&&&&return&FALSE;&&
&&&&else&&
&&&&&&&&CloseHandle(m_hThreadSend);&&
&&&&return&TRUE;&&
DWORD&Client::RecvDataThread(void*&pParam)//接收客户端数据线程&&
&&&&Client*&pClient=(Client*)pP//客户端对象指针&&
&&&&int&reV//返回值&&
&&&&char&temp[MAX_NUM_BUF];//临时变量&&
&&&&memset(temp,0,MAX_NUM_BUF);&&
&&&&for&(;pClient-&m_bC)&&
&&&&&&&&reVal&=&recv(pClient-&m_socket,temp,MAX_NUM_BUF,0);//接收数据&&
&&&&&&&&//处理错误返回值&&
&&&&&&&&if&(reVal&==&SOCKET_ERROR)&&
&&&&&&&&{&&
&&&&&&&&&&&&int&nErrCode=WSAGetLastError();&&
&&&&&&&&&&&&if&(WSAEWOULDBLOCK&==&nErrCode)//接受数据缓冲区不可用&&
&&&&&&&&&&&&{&&
&&&&&&&&&&&&&&&&continue;//继续循环&&
&&&&&&&&&&&&}&&
&&&&&&&&&&&&else&if(&WSAENETDOWN&==&nErrCode&||&WSAETIMEDOUT&==&nErrCode&||WSAECONNRESET&==&nErrCode)//客户端关闭了连接&&
&&&&&&&&&&&&{&&
&&&&&&&&&&&&&&&&break;//线程退出&&
&&&&&&&&&&&&}&&
&&&&&&&&}&&
&&&&&&&&if(&reVal&==&0)//客户端关闭了连接&&
&&&&&&&&{&&
&&&&&&&&&&&&break;&&
&&&&&&&&}&&
&&&&&&&&//接收数据&&
&&&&&&&&if&(reVal&&&HEADERLEN)&&
&&&&&&&&{&&
&&&&&&&&&&&&printf(&server&recv:%s\n&,temp);&&
&&&&&&&&&&&&pClient-&HandleData(temp);//处理数据&&
&&&&&&&&&&&&SetEvent(pClient-&m_hEvent);//通知发送数据线程&&
&&&&&&&&&&&&memset(temp,0,MAX_NUM_BUF);//清空临时变量&&
&&&&&&&&}&&
&&&&&&&&Sleep(TIMEFOR_THREAD_CLIENT);//线程睡眠&&
&&&&pClient-&m_bConning=FALSE;//与客户端的连接断开&&
&&&&SetEvent(pClient-&m_hEvent);//通知发送线程退出&&
&&&&return&0;//线程退出&&
DWORD&Client::SendDataThread(void*&pParam)//向客户端发送数据线程&&
&&&&Client*&pClient=(Client*)pP&&
&&&&for&(;pClient-&m_bC)//连接状态&&
&&&&&&&&//收到事件通知&&
&&&&&&&&if&(WAIT_OBJECT_0&==&WaitForSingleObject(pClient-&m_hEvent,INFINITE))//该函数只有当接收到发送数据线程的m_hEvent事件通知时,才会返回。当该函数返回时,说明接收数据线程已经完成了数据的计算。&&
&&&&&&&&{&&
&&&&&&&&&&&&//当客户端的连接断开时,接收数据线程先退出,然后该线程后退出,并设置退出标志&&
&&&&&&&&&&&&if&(!pClient-&m_bConning)&&
&&&&&&&&&&&&{&&
&&&&&&&&&&&&&&&&pClient-&m_bExit=TRUE;&&
&&&&&&&&&&&&&&&&break;&&
&&&&&&&&&&&&}&&
&&&&&&&&&&&&//进入临界区&&
&&&&&&&&&&&&EnterCriticalSection(&pClient-&m_cs);&&
&&&&&&&&&&&&//发送数据&&
&&&&&&&&&&&&phdr&pHeander=(phdr)pClient-&m_data.&&&
&&&&&&&&&&&&printf(&server&send&client:[%d]%s\n&,pHeander-&len,pClient-&m_data.buf);&&
&&&&&&&&&&&&int&val&=&send(pClient-&m_socket,pClient-&m_data.buf,pHeander-&len,0);&&
&&&&&&&&&&&&//处理返回错误&&
&&&&&&&&&&&&if&(SOCKET_ERROR&==&val)&&
&&&&&&&&&&&&{&&
&&&&&&&&&&&&&&&&int&nErrCode=WSAGetLastError();&&
&&&&&&&&&&&&&&&&if&(nErrCode&==&WSAEWOULDBLOCK)//发送数据缓冲区不可用&&
&&&&&&&&&&&&&&&&{&&
&&&&&&&&&&&&&&&&&&&&continue;&&
&&&&&&&&&&&&&&&&}&&
&&&&&&&&&&&&&&&&else&if(WSAENETDOWN&==&nErrCode&||&WSAETIMEDOUT&==&nErrCode&||&WSAECONNRESET&==&nErrCode)//客户端关闭连接&&
&&&&&&&&&&&&&&&&{&&
&&&&&&&&&&&&&&&&&&&&//离开临界区&&
&&&&&&&&&&&&&&&&&&&&LeaveCriticalSection(&pClient-&m_cs);&&
&&&&&&&&&&&&&&&&&&&&pClient-&m_bConning=FALSE;//连接断开&&
&&&&&&&&&&&&&&&&&&&&pClient-&m_bExit=TRUE;//线程退出&&
&&&&&&&&&&&&&&&&&&&&break;&&
&&&&&&&&&&&&&&&&}&&
&&&&&&&&&&&&&&&&else&&
&&&&&&&&&&&&&&&&{&&
&&&&&&&&&&&&&&&&&&&&//离开临界区&&
&&&&&&&&&&&&&&&&&&&&LeaveCriticalSection(&pClient-&m_cs);&&
&&&&&&&&&&&&&&&&&&&&pClient-&m_bConning=FALSE;//连接断开&&
&&&&&&&&&&&&&&&&&&&&pClient-&m_bExit=TRUE;//线程退出&&
&&&&&&&&&&&&&&&&&&&&break;&&
&&&&&&&&&&&&&&&&}&&
&&&&&&&&&&&&}&&
&&&&&&&&&&&&//成功发送数据&&
&&&&&&&&&&&&//离开临界区&&
&&&&&&&&&&&&LeaveCriticalSection(&pClient-&m_cs);&&
&&&&&&&&&&&&//设置事件为无信号状态&&
&&&&&&&&&&&&ResetEvent(&pClient-&m_hEvent);&&
&&&&&&&&}&&
&&&&return&0;&&
void&Client::HandleData(const&char*&pExpr)//接收表达式,打包数据&&
&&&&memset(m_data.buf,0,MAX_NUM_BUF);&&
&&&&//如果是“byebye”或者“Byebye”&&
&&&&if&('B'&==&((phdr)pExpr)-&type)&&
&&&&&&&&EnterCriticalSection(&m_cs);&&
&&&&&&&&phdr&pHeaderSend=(phdr)m_data.//发送的数据&&
&&&&&&&&pHeaderSend-&type&='B';//单词类型&&
&&&&&&&&pHeaderSend-&len=HEADERLEN+strlen(&OK&);//数据包长度&&
&&&&&&&&memcpy(m_data.buf+HEADERLEN,&OK&,strlen(&OK&));//数据到m_data&&
&&&&&&&&LeaveCriticalSection(&m_cs);&&
&&&&else//算数表达式&&
&&&&&&&&int&nFirNum,nSecNum,nR&&
&&&&&&&&char&cO&&
&&&&&&&&//格式化读入数据&&
&&&&&&&&sscanf(pExpr+HEADERLEN,&%d%c%d&,&nFirNum,&cOper,&nSecNum);&&
&&&&&&&&//计算&&
&&&&&&&&switch&(cOper)&&
&&&&&&&&{&&
&&&&&&&&case&'+':&&
&&&&&&&&&&&&{&&
&&&&&&&&&&&&&&&&nResult=nFirNum+nSecN&&
&&&&&&&&&&&&&&&&break;&&
&&&&&&&&&&&&}&&
&&&&&&&&case&'-':&&
&&&&&&&&&&&&{&&
&&&&&&&&&&&&&&&&nResult=nFirNum-nSecN&&
&&&&&&&&&&&&&&&&break;&&
&&&&&&&&&&&&}&&
&&&&&&&&case&'*':&&
&&&&&&&&&&&&{&&
&&&&&&&&&&&&&&&&nResult=nFirNum*nSecN&&
&&&&&&&&&&&&&&&&break;&&
&&&&&&&&&&&&}&&
&&&&&&&&case&'/':&&
&&&&&&&&&&&&{&&
&&&&&&&&&&&&&&&&if(nSecNum&==&0)&&
&&&&&&&&&&&&&&&&&&&&nResult=INVALID_NUM;&&
&&&&&&&&&&&&&&&&else&&
&&&&&&&&&&&&&&&&&&&&nResult=nFirNum/nSecN&&
&&&&&&&&&&&&&&&&break;&&
&&&&&&&&&&&&}&&
&&&&&&&&default:&&
&&&&&&&&&&&&nResult=INVALID_OPERATOR;&&
&&&&&&&&}&&
&&&&&&&&//将算数表达式和计算的结果写入字符数组中&&
&&&&&&&&char&temp[MAX_NUM_BUF];&&
&&&&&&&&char&cEqu='=';&&
&&&&&&&&sprintf(temp,&%d%c%d%c%d&,nFirNum,cOper,nSecNum,cEqu,nResult);&&
&&&&&&&&//打包数据&&
&&&&&&&&EnterCriticalSection(&m_cs);&&
&&&&&&&&phdr&pHeaderSend=(phdr)m_data.//发送的数据&&
&&&&&&&&pHeaderSend-&type='E';&//数据类型为算数表达式&&
&&&&&&&&pHeaderSend-&len=HEADERLEN+strlen(temp);//数据包的长度&&
&&&&&&&&memcpy(m_data.buf+HEADERLEN,temp,strlen(temp));//数据到m_data&&
&&&&&&&&LeaveCriticalSection(&m_cs);&&
NoBlockServer.cpp&
[cpp]&view
//&NoBlockServer.cpp&:&定义控制台应用程序的入口点。&&
//服务器同时为多个客户端提供服务,实现算术运算和向客户端发送数据的功能&&
服务器设计以多线程方式工作:&
1.建立一个用于接受客户端请求的线程,等待客户端的连接&
2.为每一个接受的客户端都建立一个接收数据线程和一个发送数据线程。当接收线程收到数据后,先计算算数表达式的结果,然后使用事件通知发送数据线程。发送数据线程接收到事件后,将计算结果发送给客户端&
3.建立一个资源清理线程,用于及时对服务器的资源进行清理。当客户端的套接字关闭之后,需要清理为该客户端创建的线程和分配的内存空间&
4.服务器的主线程用于接收用户的输入和显示信息&
5.服务器仅显示启动和退出的界面。在子线程中完成对客户端数据的处理,不在服务器界面上显示客户端的数据。&
6.所有子线程都必须自动退出,不调用线程结束函数。在所有子线程都退出后,主线程最后退出&
#include&&stdafx.h&&&
#include&&WinSock2.h&&&
#include&&iostream&&&
#include&&list&&&
#include&&Client.h&&&
using&namespace&&&
#pragma&comment(lib,&ws2_32.lib&)&&
#define&&TIMEFOR_THREAD_EXIT&5000&//主线程睡眠时间&&
#define&&TIMEFOR_THREAD_HELP&1500&&//清理资源线程退出时间&&
#define&&TIMEFOR_THREAD_SLEEP&500&//等待客户端请求线程睡眠时间&&
typedef&list&Client*&&CLIENTLIST;//链表;管理客户端连接的链表。把每个连接的客户端作为链表的一个节点。使用该链表对客户端的接入和退出进行管理。&&
CLIENTLIST&clientL//管理连接的链表&&
HANDLE&hThreadA//接受客户端连接线程句柄&&
HANDLE&hThreadH//释放资源线程句柄&&
SOCKET&sS//监听套接字&&
BOOL&bServerR//服务器的工作状态&&
HANDLE&hServerE//关闭服务器事件对象&&
CRITICAL_SECTION&csClientL//保护链表的临界区对象&&
BOOL&InitServer();//初始化&&
BOOL&StartService();//启动服务&&
void&StopService();//停止服务&&
BOOL&CreateHelperAndAcceptThread();//创建客户端连接线程&&
void&ExitServer();//服务器退出&&
void&InitMember();//初始化全局变量&&
BOOL&InitSocket();//初始化SOCKET&&&
DWORD&_stdcall&HelperThread(void&*pParam);//释放资源&&
DWORD&_stdcall&AcceptThread(void&*pParam);//接受客户端连接线程&&&
void&main()&&
&&&&if&(!InitServer())&&
&&&&&&&&ExitServer();&&
&&&&&&&&return;&&
&&&&if&(!StartService())&&
&&&&&&&&ExitServer();&&
&&&&&&&&return;&&
&&&&StopService();&&
&&&&ExitServer();&&
&&&&system(&pause&);&&
&&&&return;&&
BOOL&InitServer()//初始化&&
&&&&InitMember();&&
&&&&if&(!InitSocket())&&
&&&&&&&&return&FALSE;&&
&&&&return&TRUE;&&
void&InitMember()//初始化全局变量&&
&&&&InitializeCriticalSection(&csClientList);//初始化临界区&&
&&&&//为了便于控制服务器退出事件对象的状态,将该事件对象设置为手动模式。&&
&&&&hServerEvent&=&CreateEvent(NULL,TRUE,FALSE,NULL);//手动设置事件,初始化为无信号状态&&
&&&&hThreadAccept=NULL;&&
&&&&hThreadHelp=NULL;&&
&&&&sServer=INVALID_SOCKET;&&
&&&&bServerRunning=FALSE;&&
&&&&clientList.clear();//清空链表&&
BOOL&InitSocket()//初始化SOCKET&&
&&&&WSAData&wsD&&
&&&&if&(WSAStartup(MAKEWORD(2,2),&wsData)&!=&0)&&
&&&&&&&&return&FALSE;&&
&&&&if&((sServer&=&socket(AF_INET,SOCK_STREAM,0))&==&INVALID_SOCKET)&&
&&&&&&&&return&FALSE;&&
&&&&//设置套接字为非阻塞模式&&
&&&&unsigned&long&ul=1;&&
&&&&if&(ioctlsocket(sServer,FIONBIO,&ul)&==&SOCKET_ERROR)&&
&&&&&&&&return&FALSE;&&
&&&&SOCKADDR_IN&addrS&&
&&&&addrServ.sin_family=AF_INET;&&
&&&&addrServ.sin_addr.s_addr=INADDR_ANY;&&
&&&&addrServ.sin_port=htons(5000);&&
&&&&if&(bind(sServer,(SOCKADDR*)&addrServ,sizeof(addrServ))&==&SOCKET_ERROR)&&
&&&&&&&&return&FALSE;&&
&&&&if&(listen(sServer,SOMAXCONN)&==&SOCKET_ERROR)&&
&&&&&&&&return&FALSE;&&
&&&&printf(&start&listen...\n&);&&
&&&&return&TRUE;&&
BOOL&StartService()//启动服务&&
&&&&return&CreateHelperAndAcceptThread();//创建清理资源和接收客户端连接请求的线程&&&
/*&服务器退出&
当关闭服务器时,服务器向清理资源线程和接收客户端请求连接线程发送退出信号。接收客户端线程将自动退出。&
清理资源线程向所有正在于客户端进行通讯的接收和发送数据线程,发送服务器退出消息,使这些线程自动退出。当这些线程退出后,清理资源线程再向主线程发送消息,然后改线程退出。主线程在接收到清理资源线程发送的消息后,最后退出。&
void&StopService()//停止服务,等待用户输入退出服务器命令。当用户确定退出服务器时,有清理资源线程停止客户端的服务,所有子线程退出。&&
&&&&printf(&tip---&e(E):Exit&server.&please&input:\n&);&&&
&&&&char&cI&&
&&&&for&(;bServerR)&&
&&&&&&&&cin&&&&cI&&
&&&&&&&&if&(cInput&==&'E'&||&cInput&==&'e')&&
&&&&&&&&{&&
&&&&&&&&&&&&if&(IDOK&==&MessageBox(NULL,L&Are&you&sure?&,L&Server&,MB_OKCANCEL))&//等待用户确认退出的消息框&&
&&&&&&&&&&&&{&&
&&&&&&&&&&&&&&&&break;//跳出循环体&&
&&&&&&&&&&&&}&&
&&&&&&&&&&&&else&&
&&&&&&&&&&&&{&&
&&&&&&&&&&&&&&&&Sleep(TIMEFOR_THREAD_EXIT);//线程睡眠&&
&&&&&&&&&&&&}&&
&&&&&&&&}&&
&&&&&&&&else&&
&&&&&&&&{&&
&&&&&&&&&&&&Sleep(TIMEFOR_THREAD_EXIT);//线程睡眠&&
&&&&&&&&}&&
&&&&bServerRunning=FALSE;&&
&&&&cout&&&Server&exit&...&&&//显示服务器退出信息&&
&&&&Sleep(TIMEFOR_THREAD_EXIT);//给其他线程时间退出&&
&&&&WaitForSingleObject(hServerEvent,INFINITE);//等待清理资源线程发送事件;该函数直到收到清理资源线程发送的服务器退出事件后才返回&&
BOOL&CreateHelperAndAcceptThread()//创建客户端连接线程&&
&&&&bServerRunning=TRUE;&&
&&&&//创建释放资源线程&&
&&&&if&((hThreadHelp=CreateThread(NULL,0,HelperThread,NULL,0,NULL))&==&NULL)//第5个参数:0表示新创建的线程将立即运行各种的线程函数&&
&&&&&&&&bServerRunning&=FALSE;&&
&&&&&&&&cout&&&Server&failed!&&&&&
&&&&&&&&return&FALSE;&&
&&&&else&&
&&&&&&&&CloseHandle(hThreadHelp);&&
&&&&//创建接收客户端请求线程&&
&&&&if&((hThreadAccept=CreateThread(NULL,0,AcceptThread,NULL,0,NULL))&==&NULL)&&
&&&&&&&&bServerRunning&=FALSE;&&
&&&&&&&&cout&&&Server&failed!&&&&&
&&&&&&&&return&FALSE;&&
&&&&else&&
&&&&&&&&CloseHandle(hThreadAccept);&&
&&&&cout&&&Server&succeded!&&&&&
&&&&return&TRUE;&&
void&ExitServer()//服务器退出&&
&&&&DeleteCriticalSection(&csClientList);//释放临界区对象&&
&&&&CloseHandle(hServerEvent);//释放事件对象句柄&&
&&&&closesocket(sServer);//关闭SOCKET&&
&&&&WSACleanup();//卸载WindowsSocketDLL&&
DWORD&_stdcall&HelperThread(void&*pParam)//释放资源&&
&&&&/*&客户端退出&
&&&&当接收数据线程获知客户端请求结束时,向发送数据线程的线程发送消息使其退出,然后,该线程退出线程。同时清理资源线程清理该线程的占用资源。&
&&&for&(;bServerR)//服务器正在运行&&
&&&&&&&EnterCriticalSection(&csClientList);//进入临界区&&
&&&&&&&//清理已经断开的连接客户端内存空间&&
&&&&&&&CLIENTLIST::iterator&iter=clientList.begin();&&
&&&&&&&for&(;iter&!=&clientList.end();)&&
&&&&&&&{&&
&&&&&&&&&&&Client*&pClient=(Client*)*&&
&&&&&&&&&&&if&(pClient-&IsExit())//客户端线程已经退出&&
&&&&&&&&&&&{&&
&&&&&&&&&&&&&&&printf(&first&server&delete&a&client[%s],id=%d\n&,inet_ntoa(pClient-&m_addr.sin_addr),pClient-&m_socket);&&
&&&&&&&&&&&&&&&clientList.erase(iter++);//删除节点&&
&&&&&&&&&&&&&&&delete&pC//释放内存&&
&&&&&&&&&&&&&&&pClient=NULL;&&
&&&&&&&&&&&}&&
&&&&&&&&&&&else&&
&&&&&&&&&&&{&&
&&&&&&&&&&&&&&&iter++;&&&&&&&&&&&
&&&&&&&&&&&}&&
&&&&&&&}&&
&&&&&&&LeaveCriticalSection(&csClientList);//离开临界区&&
&&&&&&&Sleep(TIMEFOR_THREAD_HELP);&&
&&&//服务器停止工作&&
&&&if&(!bServerRunning)&&
&&&&&&&//断开每个连接,线程退出&&
&&&&&&&EnterCriticalSection(&csClientList);&&
&&&&&&&CLIENTLIST::iterator&iter=clientList.begin();&&
&&&&&&&for&(;iter&!=&clientList.end();)&&
&&&&&&&{&&
&&&&&&&&&&&Client*&pClient=(Client*)*&&
&&&&&&&&&&&//如果客户端的连接还存在,则断开连接,线程退出&&
&&&&&&&&&&&if&(pClient-&IsConning())&&
&&&&&&&&&&&{&&
&&&&&&&&&&&&&&&pClient-&DisConning();&&
&&&&&&&&&&&}&&
&&&&&&&&&&&++&&
&&&&&&&}&&
&&&&&&&LeaveCriticalSection(&csClientList);//离开临界区&&
&&&&&&&Sleep(TIMEFOR_THREAD_SLEEP);//给连接客户端线程时间,使其自动退出&&
&&&&&&&EnterCriticalSection(&csClientList);//进入临界区&&
&&&&&&&//确保为每个客户端分配的内存空间都回收。如果不加入while()这层循环,可能存在这样的情况,当pClient-&IsExit()时,该线程还没有退出。那么就需要从链表的开始部分重新判断&&
&&&&&&&while(clientList.size()&!=&0)&&
&&&&&&&{&&
&&&&&&&&&&&iter&=&clientList.begin();&&
&&&&&&&&&&&for&(;iter&!=&clientList.end();iter++)&&
&&&&&&&&&&&{&&
&&&&&&&&&&&&&&&Client*&pClient=(Client*)*&&
&&&&&&&&&&&&&&&if&(pClient-&IsExit())//客户端线程已经退出&&
&&&&&&&&&&&&&&&{&&
&&&&&&&&&&&&&&&&&&&printf(&second&server&delete&a&client[%s],id=%d\n&,inet_ntoa(pClient-&m_addr.sin_addr),pClient-&m_socket);&&
&&&&&&&&&&&&&&&&&&&clientList.erase(iter++);//删除节点&&
&&&&&&&&&&&&&&&&&&&delete&pC//释放内存空间&&
&&&&&&&&&&&&&&&&&&&pClient=NULL;&&
&&&&&&&&&&&&&&&}&&
&&&&&&&&&&&}&&
&&&&&&&&&&&Sleep(TIMEFOR_THREAD_SLEEP);//给连接客户端线程时间,使其自动退出&&
&&&&&&&}&&
&&&&&&&LeaveCriticalSection(&csClientList);//离开临界区&&
&&&clientList.clear();//清空线程&&
&&&SetEvent(hServerEvent);//通知主线程退出&&
&&&return&0;&&
DWORD&_stdcall&AcceptThread(void&*pParam)//接受客户端连接线程&&
&&&&SOCKET&sA&&
&&&&sockaddr_in&addrC&&
&&&&while(bServerRunning)&&
&&&&&&&&memset(&addrClient,0,sizeof(sockaddr_in));&&
&&&&&&&&int&lenClient=sizeof(sockaddr_in);&&
&&&&&&&&sAccept=accept(sServer,(sockaddr*)&addrClient,&lenClient);&&
&&&&&&&&if&(INVALID_SOCKET&==&sAccept)&&
&&&&&&&&{&&
&&&&&&&&&&&&int&nErrCode=WSAGetLastError();&&
&&&&&&&&&&&&if&(nErrCode&==&WSAEWOULDBLOCK)//无法立即完成一个非阻挡性套接字操作&&
&&&&&&&&&&&&{&&
&&&&&&&&&&&&&&&&Sleep(TIMEFOR_THREAD_SLEEP);&&
&&&&&&&&&&&&&&&&continue;//继续等待&&
&&&&&&&&&&&&}&&
&&&&&&&&&&&&else&&
&&&&&&&&&&&&{&&
&&&&&&&&&&&&&&&&return&0;//线程退出&&
&&&&&&&&&&&&}&&
&&&&&&&&}&&
&&&&&&&&else//接受客户端的请求&&
&&&&&&&&{&&
&&&&&&&&&&&&Client*&pClient=new&Client(sAccept,addrClient);//创建客户端对象&&
&&&&&&&&&&&&printf(&server&accept&a&client[%s]:%d;id=%d\n&,inet_ntoa(addrClient.sin_addr),ntohs(addrClient.sin_port),sAccept);&&
&&&&&&&&&&&&EnterCriticalSection(&csClientList);//进入临界区&&
&&&&&&&&&&&&clientList.push_back(pClient);//加入链表&&
&&&&&&&&&&&&LeaveCriticalSection(&csClientList);//离开临界区&&
&&&&&&&&&&&&pClient-&StartRunning();//为接受的客户端建立接受数据和发送数据线程&&
&&&&&&&&}&&
&&&&return&0;//线程退出&&
NoBlockClient
NoBlockClient.cpp
[cpp]&view
//&NoBlockClient.cpp&:&定义控制台应用程序的入口点。&&
客户端为多线程工作方式,主线程显示用户的输入和数据的计算结果,子线程接收和发送数据&
1.主线程用于所有的界面显示和接收用户的输入&
2.建立一个数据发送线程,当数据打包后,通知该线程向服务器发送数据&
3.建立一个数据接收线程。当该线程接收到服务器发送的数据后,由该线程完成对数据的解包,并通知主线程显示结果&
4.所有的子线程都必须自动退出。在所有字线程都退出后,主线程最后退出&
客户端退出:&
客户端首先向服务器发送&byebye&或&Byebye&消息,然后等待服务器的返回消息。当客户端的接收数据线程收到服务器返回的&OK&消息后,通知主线程显示该消息。然后,主线程通知发送数据线程和接收数据线程退出。在确定这两个线程都退出后,主线程最后退出。&
#include&&winsock2.h&&&
#include&&stdio.h&&&
#include&&iostream&&&
using&namespace&&&
#pragma&&comment(lib,&ws2_32.lib&)&&
#define&&TIMEFOR_THREAD_EXIT&1000&&
#define&&TIMEFOR_THREAD_SLEEP&500&&
#define&&BUF_MAX_NUM&64&&
#define&&HEADERLEN&(sizeof(hdr))&&
typedef&struct&_head//数据包包头结构该结构在win32xp下为4byte&&
&&&&char&//类型&&
&&&&unsigned&short&//数据包的长度(包括头的长度)&&
typedef&struct&_data//数据包中的数据结构&&
&&&&char&buf[BUF_MAX_NUM];&&
}DATABUF,*pDataB&&
//数据包:包头(类型[2byte]+长度[2byte])+数据;&&
SOCKET&sC//套接字&&
HANDLE&hThreadS//发送数据线程&&
HANDLE&hThreadR//接收数据线程&&
DATABUF&bufS//发送数据缓冲区&&
DATABUF&bufR//接收数据缓冲区&&
CRITICAL_SECTION&csS//当读写&bufSend变量时,要在之前进入临界区,之后退出临界区;//临界区对象,锁定bufSend&&
CRITICAL_SECTION&csR//当读写&bufRecv变量时,要在之前进入临界区,之后退出临界区;//临界区对象,锁定bufRecv&&
BOOL&bSendD//通知发送数据线程发送数据&&
HANDLE&hEventShowDataR//显示计算结果的事件&&
BOOL&bC//与服务器的连接状态&&
HANDLE&arrThread[2];//子线程数组&&
BOOL&InitClient();//初始化&&
BOOL&ConnectServer();//连接服务器&&
BOOL&CreateSendAndRecvThread();//创建发送和接收数据线程&&
void&InputAndOutput();//用户输入数据和显示结果&&
void&ExitClient();//退出&&
void&InitMember();//初始化全局变量&&
BOOL&&InitSocket();//创建SOCKET&&
DWORD&_stdcall&RecvDataThread(LPVOID&pParam);//接收数据线程&&
DWORD&_stdcall&SendDataThread(LPVOID&pParam);//发送数据线程&&
BOOL&PackByebye(const&char*&pExpr);//将输入的&Byebye&,&byebye&字符串打包&&
BOOL&PackExpression(const&char*&pExpr);//将输入的算数表达式打包&&
void&ShowConnectMsg(BOOL&bConnected);//显示连接服务器消息&&
void&ShowDataResultMsg();//显示计算结果&&
void&ShowTipMsg(BOOL&bFirstInput);//显示提示信息&&
int&main()&&
&&&&if&(!InitClient())&&
&&&&&&&&ExitClient();&&
&&&&&&&&return&0;&&
&&&&if&(ConnectServer())&&
&&&&&&&&ShowConnectMsg(TRUE);&&
&&&&else&&
&&&&&&&&ShowConnectMsg(FALSE);&&
&&&&&&&&ExitClient();&&
&&&&&&&&return&0;&&
&&&&}&&&&&
&&&&if&(!CreateSendAndRecvThread())&//创建发送和接收数据线程&&
&&&&&&&&ExitClient();&&
&&&&&&&&return&0;&&
&&&&InputAndOutput();//用户输入数据和显示结果&&&&&&
&&&&ExitClient();//退出&&
&&&&system(&pause&);&&
&&&&return&0;&&
BOOL&InitClient()//初始化&&
&&&&InitMember();&&
&&&&if&(!InitSocket())&&
&&&&&&&&return&FALSE;&&
&&&&return&TRUE;&&
void&InitMember()//初始化全局变量&&
&&&&InitializeCriticalSection(&csSend);&&
&&&&InitializeCriticalSection(&csRecv);&&
&&&&sClient=INVALID_SOCKET;&&
&&&&hThreadRecv=NULL;&&
&&&&hThreadSend=NULL;&&
&&&&bConnected=FALSE;&&
&&&&bSendData=FALSE;&&
&&&&memset(bufRecv.buf,0,BUF_MAX_NUM);&&
&&&&memset(bufSend.buf,0,BUF_MAX_NUM);&&
&&&&//手动设置事件,初始化为无信号状态&&
&&&&hEventShowDataResult=(HANDLE)CreateEvent(NULL,TRUE,FALSE,NULL);&&
BOOL&&InitSocket()//创建SOCKET&&
&&&&WSADATA&&&
&&&&if&(WSAStartup(MAKEWORD(2,2),&wsd)&!=&0)&&
&&&&&&&&return&FALSE;&&
&&&&if&((sClient=socket(AF_INET,SOCK_STREAM,0))&==&INVALID_SOCKET)&&
&&&&&&&&return&FALSE;&&
&&&&//设置套接字为非阻塞模式&&
&&&&unsigned&long&ul=1;&&
&&&&if&(ioctlsocket(sClient,FIONBIO,&ul)&==&SOCKET_ERROR)&&
&&&&&&&&return&FALSE;&&
&&&&return&TRUE;&&
BOOL&ConnectServer()//连接服务器&&
&&&&SOCKADDR_IN&addrS&&
&&&&addrServ.sin_family=AF_INET;&&
&&&&addrServ.sin_addr.s_addr=inet_addr(&127.0.0.1&);&&
&&&&addrServ.sin_port=htons(5000);&&
&&&&int&reV&&
&&&&while&(1)&&
&&&&&&&&reVal=connect(sClient,(SOCKADDR*)&addrServ,sizeof(addrServ));&&
&&&&&&&&//处理连接错误。当使用非阻塞套接字时,建议最好不要使用这种方法来判断客户端是否成功连接服务器。推荐使用select(),WSAAsyncSelect()或者WSAEventselect(),来判断连接服务器是否成功。在后面的章节中会讲解到。&&
&&&&&&&&if&(reVal&==&SOCKET_ERROR)&&
&&&&&&&&{&&
&&&&&&&&&&&&int&nErrCode=WSAGetLastError();&&
&&&&&&&&&&&&if&(nErrCode&==&WSAEWOULDBLOCK&||&nErrCode&==&WSAEINVAL)//连接未能够立即完成;监听状态&&
&&&&&&&&&&&&{&&
&&&&&&&&&&&&&&&&continue;&&
&&&&&&&&&&&&}&&
&&&&&&&&&&&&else&if&(nErrCode&==&WSAEISCONN)//连接已经完成&&
&&&&&&&&&&&&{&&
&&&&&&&&&&&&&&&&break;&&
&&&&&&&&&&&&}&&
&&&&&&&&&&&&else//其他原因,连接失败&&
&&&&&&&&&&&&{&&
&&&&&&&&&&&&&&&&return&FALSE;&&
&&&&&&&&&&&&}&&
&&&&&&&&}&&
&&&&&&&&if(reVal&==&0)//连接成功&&
&&&&&&&&{&&
&&&&&&&&&&&&break;&&
&&&&&&&&}&&
&&&&bConnected=TRUE;&&
&&&&return&TRUE;&&
BOOL&CreateSendAndRecvThread()//创建发送和接收数据线程&&
&&&&unsigned&long&ulThreadId;&&
&&&&if&((hThreadRecv=CreateThread(NULL,0,RecvDataThread,NULL,0,&ulThreadId))&==&NULL)&&
&&&&&&&&return&FALSE;&&
&&&&if&((hThreadSend=CreateThread(NULL,0,SendDataThread,NULL,0,&ulThreadId))&==&NULL)&&
&&&&&&&&return&FALSE;&&
&&&&arrThread[0]=hThreadR&&
&&&&arrThread[1]=hThreadS&&
&&&&return&TRUE;&&
/*&客户端退出&
客户端首先向服务器发送&Byebye&,&byebye&消息,然后等待服务器的返回消息。当客户端的接收数据线程收到服务器返回的&OK&消息后,通知主线程显示该消息。然后,主线程通知发送数据线程和接收数据线程退出。在确认这两个线程都退出后,主线程最后退出。&
void&InputAndOutput()//用户输入数据和显示结果&&
&&&&char&cInput[BUF_MAX_NUM];//用户输入缓冲区&&
&&&&BOOL&bFirstInput=TRUE;//第一次只能输入算术表达式&&
&&&&while(bConnected)//连接状态&&
&&&&&&&&memset(cInput,0,BUF_MAX_NUM);&&
&&&&&&&&ShowTipMsg(bFirstInput);//提示输入信息&&
&&&&&&&&cin&&&&cI&&
&&&&&&&&char*&pTemp=cI&&
&&&&&&&&if&(bFirstInput)//第一次输入&&
&&&&&&&&{&&
&&&&&&&&&&&&if&(!PackExpression(pTemp))//算数表达式打包&&
&&&&&&&&&&&&{&&
&&&&&&&&&&&&&&&&continue;//重新输入&&
&&&&&&&&&&&&}&&
&&&&&&&&&&&&bFirstInput&=&FALSE;//成功输入第一个算数表达式&&
&&&&&&&&}&&
&&&&&&&&else&if&(!PackByebye(pTemp))//Byebye&byebye&打包&&
&&&&&&&&{&&
&&&&&&&&&&&&if&(!PackExpression(pTemp))//算数表达式打包&&
&&&&&&&&&&&&{&&
&&&&&&&&&&&&&&&&continue;//重新输入&&
&&&&&&&&&&&&}&&
&&&&&&&&}&&
&&&&&&&&//等待显示结果&&
&&&&&&&&if&(WAIT_OBJECT_0&==&WaitForSingleObject(hEventShowDataResult,INFINITE))&&
&&&&&&&&{&&
&&&&&&&&&&&&ResetEvent(hEventShowDataResult);//设置为无信号状态&&&
&&&&&&&&&&&&if&(!bConnected)//客户端被动退出,此时接收和发送数据线程已经退出。&&
&&&&&&&&&&&&{&&
&&&&&&&&&&&&&&&&break;&&
&&&&&&&&&&&&}&&
&&&&&&&&&&&&ShowDataResultMsg();//显示数据&&
&&&&&&&&&&&&if&(0&==&strcmp(bufRecv.buf,&OK&))//客户端主动退出&&
&&&&&&&&&&&&{&&
&&&&&&&&&&&&&&&&bConnected=FALSE;&&
&&&&&&&&&&&&&&&&Sleep(TIMEFOR_THREAD_EXIT);//给数据接收和发送线程退出时间&&
&&&&&&&&&&&&}&&
&&&&&&&&}&&
&&&&if&(!bConnected)//与服务器连接已经断开&&
&&&&&&&&ShowConnectMsg(FALSE);//显示信息&&
&&&&//等待数据发送和接收线程退出;为了做到主线程最后退出&&
&&&&if&(WAIT_ABANDONED_0&==&WaitForMultipleObjects(2,arrThread,TRUE,INFINITE))&&
&&&&&&&&int&nErrCode=GetLastError();&&
void&ExitClient()//退出&&
&&&&DeleteCriticalSection(&csSend);&&
&&&&DeleteCriticalSection(&csRecv);&&
&&&&CloseHandle(hThreadRecv);&&
&&&&CloseHandle(hThreadSend);&&
&&&&closesocket(sClient);&&
&&&&WSACleanup();&&
DWORD&_stdcall&RecvDataThread(LPVOID&pParam)//接收数据线程&&
&&&&int&reV&&
&&&&char&temp[BUF_MAX_NUM];&&
&&&&memset(temp,0,BUF_MAX_NUM);&&
&&&&while(bConnected)//连接状态&&&
&&&&&&&&reVal&=&recv(&sClient,temp,BUF_MAX_NUM,0);//接收数据&&
&&&&&&&&if&(&SOCKET_ERROR&==&reVal)//接受数据缓冲区不可用&&
&&&&&&&&{&&
&&&&&&&&&&&&int&nErrCode&=&WSAGetLastError();&&
&&&&&&&&&&&&if&(WSAEWOULDBLOCK&==&nErrCode)&&
&&&&&&&&&&&&{&&
&&&&&&&&&&&&&&&&Sleep(TIMEFOR_THREAD_SLEEP);//线程睡眠&&
&&&&&&&&&&&&&&&&continue;//继续接收数据&&
&&&&&&&&&&&&}&&
&&&&&&&&&&&&else&&
&&&&&&&&&&&&{&&
&&&&&&&&&&&&&&&&bConnected&=&FALSE;&&
&&&&&&&&&&&&&&&&SetEvent(hEventShowDataResult);//通知主线程,防止在无限期的等待&&
&&&&&&&&&&&&&&&&return&0;//线程退出&&&
&&&&&&&&&&&&}&&
&&&&&&&&}&&
&&&&&&&&if&(&0&==&reVal)//服务器关闭了连接&&
&&&&&&&&{&&
&&&&&&&&&&&&bConnected&=&FALSE;//线程退出&&&&&
&&&&&&&&&&&&SetEvent(hEventShowDataResult);//通知主线程,防止在无限期的等待&&
&&&&&&&&&&&&return&0;//线程退出&&&
&&&&&&&&}&&
&&&&&&&&if&(&reVal&&&HEADERLEN&&&&reVal&!=&-1)//收到数据&&
&&&&&&&&{&&
&&&&&&&&&&&&//对数据解包&&
&&&&&&&&&&&&phdr&header&=(phdr)&&
&&&&&&&&&&&&EnterCriticalSection(&csRecv);&&
&&&&&&&&&&&&&&&&&&&&&&&&memset(bufRecv.buf,0,BUF_MAX_NUM);&&
&&&&&&&&&&&&memcpy(bufRecv.buf,temp&+&HEADERLEN,header-&len&-&HEADERLEN);//将数据结果到接收数据缓冲区&&
&&&&&&&&&&&&LeaveCriticalSection(&csRecv);&&
&&&&&&&&&&&&SetEvent(hEventShowDataResult);//通知主线程显示计算结果&&
&&&&&&&&&&&&memset(temp,0,BUF_MAX_NUM);&&
&&&&&&&&}&&
&&&&&&&&Sleep(TIMEFOR_THREAD_SLEEP);//线程睡眠&&
&&&&return&0;&&
DWORD&_stdcall&SendDataThread(LPVOID&pParam)//发送数据线程&&
&&&&while(bConnected)//连接状态&&
&&&&&&&&if&(bSendData)//发送数据&&
&&&&&&&&{&&
&&&&&&&&&&&&EnterCriticalSection(&csSend);//进入临界区&&
&&&&&&&&&&&&while(TRUE)&&
&&&&&&&&&&&&{&&
&&&&&&&&&&&&&&&&int&nBuflen=((phdr)bufSend.buf)-&&&
&&&&&&&&&&&&&&&&int&val&=&send(sClient,bufSend.buf,nBuflen,0);&&
&&&&&&&&&&&&&&&&//处理返回错误&&
&&&&&&&&&&&&&&&&if&(SOCKET_ERROR&==&val)&&
&&&&&&&&&&&&&&&&{&&
&&&&&&&&&&&&&&&&&&&&int&nErrCode&=&WSAGetLastError();&&
&&&&&&&&&&&&&&&&&&&&if&(WSAEWOULDBLOCK&==&nErrCode)//发送缓冲区不可用&&
&&&&&&&&&&&&&&&&&&&&{&&
&&&&&&&&&&&&&&&&&&&&&&&&continue;//继续循环&&
&&&&&&&&&&&&&&&&&&&&}&&
&&&&&&&&&&&&&&&&&&&&else&&
&&&&&&&&&&&&&&&&&&&&{&&
&&&&&&&&&&&&&&&&&&&&&&&&LeaveCriticalSection(&csSend);//离开临界区&&
&&&&&&&&&&&&&&&&&&&&&&&&bConnected&=&FALSE;//断开状态&&
&&&&&&&&&&&&&&&&&&&&&&&&SetEvent(hEventShowDataResult);//通知主线程,防止在无限期的等待返回结果。&&
&&&&&&&&&&&&&&&&&&&&&&&&return&0;&&
&&&&&&&&&&&&&&&&&&&&}&&
&&&&&&&&&&&&&&&&}&&
&&&&&&&&&&&&&&&&bSendData&=&FALSE;//发送状态&&
&&&&&&&&&&&&&&&&break;//跳出for&&
&&&&&&&&&&&&}&&
&&&&&&&&&&&&LeaveCriticalSection(&csSend);//离开临界区&&
&&&&&&&&}&&
&&&&&&&&Sleep(TIMEFOR_THREAD_SLEEP);//线程睡眠&&
&&&&return&0;&&
BOOL&PackByebye(const&char*&pExpr)//将输入的&Byebye&,&byebye&字符串打包&&
&&&&BOOL&reVal&=&FALSE;&&
&&&&if&(!strcmp(&Byebye&,pExpr)&||&!strcmp(&byebye&,pExpr))&&
&&&&&&&&EnterCriticalSection(&csSend);&&
&&&&&&&&phdr&pHeader&=(phdr)bufSend.//强制转换&&
&&&&&&&&pHeader-&type='B';//类型&&
&&&&&&&&pHeader-&len&=&HEADERLEN&+&strlen(&Byebye&);//数据包长度&&
&&&&&&&&memcpy(bufSend.buf&+&HEADERLEN,pExpr,strlen(pExpr));//数据&&
&&&&&&&&LeaveCriticalSection(&csSend);&&
&&&&&&&&pHeader&=&NULL;&&
&&&&&&&&bSendData&=&TRUE;//通知发送数据线程&&
&&&&&&&&reVal&=&TRUE;&&
&&&&return&reV&&&
BOOL&PackExpression(const&char*&pExpr)//将输入的算数表达式打包&&
&&&&char*&pTemp&=&(char*)pE//算数表达式数字开始的位置&&
&&&&while&(!*pTemp)//第一个数字位置&&
&&&&&&&&pTemp++;&&
&&&&char*&pos1=pT&&
&&&&char*&pos2=NULL;&&
&&&&char*&pos3=NULL;&&
&&&&int&len1=0,len2=0,len3=0;&&
&&&&if&((*pTemp&!=&'+')&&&&(*pTemp&!=&'-')&&&&((*pTemp&&&'0')&||&(*pTemp&&&'9')))//第一个字符是+&-&或者是数字&&
&&&&&&&&return&FALSE;&&
&&&&if&((*pTemp++&==&'-')&&(*pTemp&&&'0'&||&*pTemp&&&'9'))&&&&//第一个字符是'-',第二个是数字&&&&
&&&&&&&&return&FALSE;&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&//重新输入&&
&&&&--pT&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&//上移指针&&
&&&&char*&pNum&=&pT&&&&&&&&&&&&&&&&&&&&&//数字开始的位置&&&&&&&&&&&&&&&&&&&&&
&&&&if&(*pTemp&==&'+'||*pTemp&==&'-')&&&&&&&//+&-&&
&&&&&&&&pTemp++;&&
&&&&while&(*pTemp&&=&'0'&&&&*pTemp&&=&'9')&&&&//数字&&
&&&&&&&&pTemp++;&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
&&&&len1&=&pTemp&-&pN//数字长度&&&&&&&&&&&&&&&&&&&&&&&&
&&&&//可能有空格&&
&&&&while(!*pTemp)&&&&&&&&&&&&&&&&&&&&&&&&&&&&
&&&&&&&&pTemp++;&&
&&&&//算数运算符&&
&&&&if&(('+'&!=&*pTemp)&&&&('-'&!=&*pTemp)&&&&&&('*'&!=&*pTemp)&&&&('/'&!=&*pTemp))&&
&&&&&&&&return&FALSE;&&
&&&&pos2&=&pT&&
&&&&len2&=&1;&&
&&&&//下移指针&&
&&&&pTemp++;&&
&&&&//可能有空格&&
&&&&while(!*pTemp)&&
&&&&&&&&pTemp++;&&
&&&&//第2个数字位置&&
&&&&pos3&=&pT&&
&&&&if&(*pTemp&&&'0'&||&*pTemp&&&'9')&&
&&&&&&&&return&FALSE;//重新输入&&&&&&&&&&&
&&&&while&(*pTemp&&=&'0'&&&&*pTemp&&=&'9')//数字&&
&&&&&&&&pTemp++;&&
&&&&if&('='&!=&*pTemp)&&//最后是等于号&&
&&&&&&&&return&FALSE;&&&//重新输入&&
&&&&len3&=&pTemp&-&pos3;//数字长度&&
&&&&int&nExprlen&=&len1&+&len2&+&len3;&&//算数表示长度&&
&&&&//表达式读入发送数据缓冲区&&
&&&&EnterCriticalSection(&csSend);&&&&&&//进入临界区&&
&&&&//数据包头&&
&&&&phdr&pHeader&=&(phdr)(bufSend.buf);&&
&&&&pHeader-&type&=&'E';&&&&&&&&&//类型&&
&&&&pHeader-&len&=&nExprlen&+&HEADERLEN;//数据包长度&&
&&&&//拷贝数据&&
&&&&memcpy(bufSend.buf&+&HEADERLEN,&pos1,&len1);&&
&&&&memcpy(bufSend.buf&+&HEADERLEN&+&len1,&pos2,&len2);&&
&&&&memcpy(bufSend.buf&+&HEADERLEN&+&len1&+&len2&,&pos3,len3);&&
&&&&LeaveCriticalSection(&csSend);&&&&&&//离开临界区&&
&&&&pHeader&=&NULL;&&
&&&&bSendData&=&TRUE;&&&&&&&&&&&&&&&&&&&//通知发送数据线程发送数据&&
&&&&return&TRUE;&&
void&ShowConnectMsg(BOOL&bSuc)//显示连接服务器失败消息&&
&&&&if&(bSuc)&&
&&&&&&&&cout&&&&&*&Succeed&to&connect&server!&*&&&&&&&&
&&&&else&&
&&&&&&&&cout&&&&&*&Fail&to&connect&server!&*&&&&&&&&
void&ShowDataResultMsg()//显示计算结果&&
&&&&EnterCriticalSection(&csRecv);&&&
&&&&cout&&&&&*Result:&&&&bufRecv.buf&&&&&&
&&&&LeaveCriticalSection(&csRecv);&&
void&ShowTipMsg(BOOL&bFirstInput)//显示提示信息&&
&&&&if&(bFirstInput)//首次显示&&
&&&&&&&&cout&&&&&&&&&&&&
&&&&&&&&cout&&&&&*&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&*&&&&&&&
&&&&&&&&cout&&&&&*&Please&input&expression.&&&&&&&*&&&&&&&
&&&&&&&&cout&&&&&*&Usage:NumberOperatorNumber=&&&&*&&&&&&&
&&&&&&&&cout&&&&&*&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&*&&&&&&&
&&&&&&&&cout&&&&&&&&&&&&
&&&&}else{&&
&&&&&&&&cout&&&&&&&&&&&&
&&&&&&&&cout&&&&&*&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&*&&&&&&&
&&&&&&&&cout&&&&&*&Please&input:&expression&&&&&&&*&&&&&&&
&&&&&&&&cout&&&&&*&Usage:NumberOperatorNumber=&&&&*&&&&&&&
&&&&&&&&cout&&&&&*&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&*&&&&&&&
&&&&&&&&cout&&&&&*&If&you&want&to&exit.&&&&&&&&&&&*&&&&&&&
&&&&&&&&cout&&&&&*&Usage:&Byebye&or&byebye&&&&&&&&*&&&&&&&
&&&&&&&&cout&&&&&*&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&*&&&&&&&
&&&&&&&&cout&&&&&&&&&&&&
&&&&}&&&&&
运行结果:
此文章来自于
套接字的非阻塞模式是指套接字在执行操作时,调用的函数不管操作是否完成都会立即返回的工作模式.非阻塞套接字在处理同时建立的多个连接,发送和接收的数据量不均,时间不定时,明显具有明显的优势.但这种套接字在使用上存在一定难度.本章讲述套接字的非阻塞模式及其一个远程算数运算套接字程序.
套接字的阻塞模式是指套接字在执行操作时,调用函数在没有完成操作之前不会立即返回的工作模式.阻塞模式的套接字用于少量数据接收和发送的简单网络程序开发. 套接字的阻塞模式 WindowsSockets分别提供了套接字模式和套接字I/O模型,可以对一个套接字的行为进行控制.套接字模式用于当一个套接字被调用时,决定调用函数的阻塞行为.套接字模式有阻塞和非阻塞两种工作 ...
套接字Select模型是比较常用的一种I/O模型.利用该模型使得WindowsSockets应用程序可以在同一时间内管理和控制多个套接字.该模型的核心是select()函数.在使用该函数是,还需要用到FD_SET,FD_ZERO,FD_ISSET和FD_CLR四个宏.开发WindowsSocket程序时,应用程序需要这样的能力:当执行操作的套接字满足可读可写 ...
非阻塞套接字
非阻塞模式是指:套接字在执行操作时,调用的函数不管操作是否完成都会立即返回的工作模式.
非阻塞套接字在处理同时建立的多个连接等方面具有明显的优势.但是使用过程中有一定的难度.由于函数在 ...
刚开始接触socket的编程的时候,遇到了很多的问题,费了很大劲搞懂.其实往往都是一些比较基本的知识,但是都是很重要的,只要对其熟练的掌握后,相信对基于网络的编程会有很大的提高,.
就拿基于C/S结构的例子来说,我们先看看服务器和客户端的流程(异常处理就省略了):}

我要回帖

更多关于 socket阻塞和非阻塞 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信