windows网络编程之 (三) - Winsock非阻塞select模式服务器

原创 2006年06月01日 00:41:00
/************************************
 作者:  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;
}

『转载』 作者Blog:http://blog.csdn.net/wangweixing2000/

linux下socket编程 select实现非阻塞模式多台客户端与服务器通信

select函数原型如下: int select (int maxfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct t...
  • tingyuanss
  • tingyuanss
  • 2015年04月22日 08:24
  • 7469

Windows网络编程之Select模型学习笔记

关于select模型的理论讲解,网上随便一搜就有很多大神的精彩文章,这里就不重复造轮子了。不过要真正理解select模型,代码才是最好的文章。我在网上看了好多代码,可能是相互转载的原因,有些代码不是编...
  • Fish_55_66
  • Fish_55_66
  • 2015年12月18日 15:26
  • 2128

非阻塞模式WinSock编程入门 使用 WSAAsyncSelect模型

非阻塞模式WinSock编程入门   介绍 WinSock是Windows提供的包含了一系列网络编程接口的套接字程序库。在这篇文章中,我们将介绍如何把它的非阻塞模式引入到应用程序中。文章中所讨论...
  • debugconsole
  • debugconsole
  • 2013年03月17日 18:46
  • 2264

linux 网络编程【三】 非阻塞通信select

函数声明 select函数 int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct ti...
  • qqyanchong
  • qqyanchong
  • 2013年02月22日 16:09
  • 2134

linux 网络编程【三】 非阻塞通信select

函数声明 select函数 int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct ti...
  • ChenAiZiTeng
  • ChenAiZiTeng
  • 2014年01月06日 12:40
  • 362

Windows sockets 网络编程(3) — 非阻塞模式开发

一、套接字的非阻塞模式       套接字的非阻塞模式是指套接字在执行操作时,调用的函数不管操作是否完成都会立即返回的工作模式。根据其返回的错误代码,确定操作是否正确完成。二、设置套接字的非阻塞模式 ...
  • haohuang_hd
  • haohuang_hd
  • 2011年03月17日 15:19
  • 463

关于Libevent的快速可移植非阻塞网络编程(block-》select-》poll-》epoll-》libevent)

Libevent 是一个用C语言编写的、轻量级的开源高性能事件通知库,主要有以下几个亮点:事件驱动(event-driven),高性能;轻量级,专注于网络,不如ACE 那么臃肿庞大;源代码相当精炼、易...
  • ztxjzh
  • ztxjzh
  • 2017年11月08日 16:40
  • 42

linux下非阻塞网络编程-select

select系统调用是用来让我们的程序监视多个文件句柄(file descriptor)的状态变化的。程序会停在select这里等待,直到被监视的文件句柄有某一个或多个发生了状态改变。文件句柄在lin...
  • kingqizhou
  • kingqizhou
  • 2012年07月25日 18:09
  • 1034

Linux 网络编程二:非阻塞select

函数声明 select函数 int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct ti...
  • wxliu1989
  • wxliu1989
  • 2014年07月17日 22:43
  • 276

linux网络编程十五:I/O复用的应用-非阻塞connect

connect系统调用的man手册中描述了connect出错时的一种errno值:EINPROGRESS 这种错误发生在对非阻塞的socket调用connect,而连接又没建立时。 根据 ...
  • jasonliuvip
  • jasonliuvip
  • 2014年04月03日 21:53
  • 1214
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:windows网络编程之 (三) - Winsock非阻塞select模式服务器
举报原因:
原因补充:

(最多只允许输入30个字)