// SelectModeWinSock.cpp : 定义控制台应用程序的入口点。 // /************************************ 作者: wangweixing2000 Revision By: 0.01 Revised on 2005-6-29 11:52:03 Comments: SelectModeWinSock 该程序中存在bug,没有处理多线程数据访问的冲突和判断客户端断开的有效处理!哪位有兴趣可以加上! ************************************/ //select(选择)模型,是利用select函数实现对i/o的管理。select函数可以用于 //判断套接字上是否存在数据,或者能否向一个套接字写入数据。 /* int select( int nfds, //被忽略参数,为了保持和早期Berkeley套接字应用程序兼容而保留的这个参数 fd_set* readfds, //检查可读性fd_set*参数 fd_set* writefds, //检查可写性fd_set*参数 fd_set* exceptfds, //带外数据 const struct timeval* timeout //指定select等待i/o操作完成时,最多等待多长时间。 ); typedef struct timeval { long tv_sec; //以秒为单位指定等待的时间 long tv_usec; //以毫秒为单位指定的等待时间 } timeval; 用select对套接字进行监听前,应用程序必须将套接字句柄分配给一个集合,设置好一个或所有的读、写以及例外的 fd_set结构。将一个套接字分配给任何一个集合后,再来调用select,便可知道某个套接字上是否正在发生i/o活动。 Winsock提供了几个宏用来处理fd_set: FD_ZERO(*set) //清空fd_set* FD_CLR(s,*set) //从set中删除s套接字 FD_ISSET(s,*set) //检查s是否是set集合的一名成员,如果是返回TRUE FD_SET(s,*set) //设置套接字s加入集合set中 */ #include "stdafx.h" #include <iostream> #include <Winsock2.h> #include <ws2tcpip.h> #pragma comment(lib,"WS2_32.lib") using namespace std; struct SocketObj { SOCKET socket; //当前对象的socket BOOL listening; //该套接字是否已经 SocketObj *next, //向后 *prev; //向前 }; SocketObj *g_pSocketList = NULL; //Socket连表 SocketObj *g_pSocketEnd = NULL; //连表的尾部 int g_nSocketCount = 0; HANDLE g_hSelect; //创建SocketObj SocketObj* GetSocketObj(SOCKET s,BOOL listening) { SocketObj *newSocketObj = NULL; newSocketObj = (SocketObj*)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(SocketObj)); if(newSocketObj == NULL) { cout<<"GetSocketObj: HeapAlloc failed: "<< GetLastError()<<endl; ExitProcess(-1); //结束进程 } newSocketObj->socket = s; newSocketObj->listening = listening; return newSocketObj; } //插入一个SocketObj void InserSocketObj(SocketObj *obj) { obj->next = obj->prev = NULL; if(g_pSocketList == NULL) { g_pSocketList = g_pSocketEnd = obj; } else { obj->prev = g_pSocketEnd; g_pSocketEnd->next = obj; g_pSocketEnd = obj; } g_nSocketCount++; } //删除 void RemoveSocketObj(SocketObj *obj) { if(obj->prev) { obj->prev->next = obj->next; } if(obj->next) { obj->next->prev = obj->prev; } if(obj == g_pSocketList) { g_pSocketList = obj->next; } if(obj == g_pSocketEnd) { g_pSocketEnd = obj->prev; } g_nSocketCount--; HeapFree(GetProcessHeap(),0,obj); } //监听线程 DWORD WINAPI ListenThread(void *pVoid) { cout<<"server start listening!"<<endl;
SOCKADDR_IN ClientAddr; // 定义一个客户端得地址结构作为参数 int addr_length=sizeof(ClientAddr); SOCKET *listen = (SOCKET*)pVoid; SocketObj *pSocketObj = NULL; while(1) { SOCKET Client = accept(*listen,(sockaddr*)&ClientAddr,&addr_length); if(Client == INVALID_SOCKET) { cout<<"accept failed!"<<endl; continue; } // 这里可以取得客户端的IP和端口,但是我们只取其中的SOCKET编号 LPCTSTR lpIP = inet_ntoa(ClientAddr.sin_addr); UINT nPort = ClientAddr.sin_port; cout<<"一个客户端已经连接!IP:"<<lpIP<<"SOCKET 端口号:"<<nPort<<endl; //创建SocketObj并添加如list pSocketObj = GetSocketObj(Client,TRUE); InserSocketObj(pSocketObj); if(g_nSocketCount == 1) //如果有一个客户端就唤起select线程 { ResumeThread((HANDLE)g_hSelect); } } return 0; } //select线程函数 DWORD WINAPI SelectThread(void *pVoid) { SocketObj *sptr = NULL; fd_set readfds, writefds, exceptfds; timeval timeout; char Buffer[4096]; while(TRUE) { //清空fd_set FD_ZERO(&readfds); FD_ZERO(&writefds); FD_ZERO(&exceptfds); //设置timeout timeout.tv_sec = 5; timeout.tv_usec = 0; sptr = g_pSocketList; //设置fd_set while(sptr) { FD_SET(sptr->socket,&readfds); FD_SET(sptr->socket,&writefds); FD_SET(sptr->socket,&exceptfds); sptr = sptr->next; } //开始select int ret = select(0,&readfds,&writefds,NULL,&timeout); if(ret == SOCKET_ERROR) { cout<<"select failed!"<<endl; WSACleanup(); return -1; } else if(ret == 0) { //超时 cout<<"time out!"<<endl; continue; } else { sptr = g_pSocketList; while(sptr) { if(FD_ISSET(sptr->socket,&readfds)) { ZeroMemory(Buffer,4096); int re = recv(sptr->socket,Buffer,4096,0); if(re == SOCKET_ERROR) { return -1; } else if(re == 0) { //该客户端已经断开 closesocket(sptr->socket); SocketObj *tmp = sptr; sptr = sptr->next; RemoveSocketObj(tmp); continue; } else { cout<<"recv buffer:"<<Buffer<<endl; } } if(FD_ISSET(sptr->socket,&writefds)) { //发送数据给当前客户端 char *sendBuffer = "wwx send msg!"; int re = send(sptr->socket,sendBuffer,16,0); if(re == SOCKET_ERROR) { return -1; } else if(re == 0) { continue; } else { //这里可以显示发送是否成功 //cout<<"send successed!"<<endl; } } sptr = sptr->next; } } } return 0; } int _tmain(int argc, _TCHAR* argv[]) { WSAData wsaData; SOCKET Listen; SocketObj *pSockobj = NULL; SOCKET Accept = INVALID_SOCKET; SOCKADDR_IN ServerAddr; //初始化Winsock库 int ret = WSAStartup(MAKEWORD(2,2),&wsaData); if(ret != 0) { cout<<"WSAStartup error!"<<endl; WSACleanup(); return -1; } Listen = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP); if(Listen == INVALID_SOCKET) { cout<<"Listen create failed!"<<endl; WSACleanup(); return -1; } //绑定 ServerAddr.sin_family = AF_INET; ServerAddr.sin_addr.S_un.S_addr = htonl(INADDR_ANY); ServerAddr.sin_port = htons(10012); ret = bind(Listen,(sockaddr*)&ServerAddr,sizeof(SOCKADDR_IN)); if(ret == SOCKET_ERROR) { cout<<"bind failed!"<<endl; closesocket(Listen); WSACleanup(); return -1; } //监听 listen(Listen,200); //开启监听线程 HANDLE hListen = CreateThread(NULL,0,ListenThread,&Listen,0,NULL); if(hListen == NULL) { cout<<"Create ListenThread failed!"<<endl; } //创价挂起的select线程,因为刚开始没有连接的客户端 g_hSelect = CreateThread(NULL,0,SelectThread,NULL,CREATE_SUSPENDED,NULL); if(g_hSelect == NULL) { cout<<"Create SelectThread failed!"<<endl; } system("PAUSE"); return 0; } 如果引用该文,请注明出处!谢谢!
|