windows網路編程之Winsock(三)非阻塞select模式伺服器

來源:互聯網
上載者:User

// 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;
}

如果引用該文,請註明出處!謝謝!

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.