海闊天空

          I'm on my way!
          隨筆 - 17, 文章 - 69, 評論 - 21, 引用 - 0
          數據加載中……

          socket編程


          今天只想談一談關于阻塞和非阻塞的socket。

          調用socket函數得到的socket的文件標示符,默認情況下是一個阻塞的socket,從應用角度,就是每當調用accept,recv,send 等函數的時候,如果對方沒有相應,那么進程會阻塞在那里,直到對方相應。這在應用中有很多不便,尤其是在windows環境中的編程,如果進程被阻塞那么 看上去有一點像死機的感覺。

          其實把一個socket設置為非阻塞的也很簡單。但是,應用select函數,阻塞socket也可以到達類似的效果。一會詳細討論。

          先考慮一個TCP的連接,當服務器程序listen以后,如果直接調用accept,那么進程會阻塞在那里,一直到被client端connect,然后 開啟一個新的線程處理客戶的服務,主線程繼續監聽請求。這樣可以實現同時處理多個客戶端連接,但是這樣是會阻塞主線程,不是一個好的辦法。其實,我當然也 可添加一個線程,在“后臺”accept,但是我覺得這里面有一個多線程操作同一個文件描述符socket的問題。也不是很好。也就是說,如果只用到線程 機制,并不能很好的解決阻塞的問題。

          select函數可以對一套文件描述符操作(windows里叫handle,我覺得沒有什么本 質的區別。都是進程空間里面的一個整數而已),觀察這套文件描述符的可讀或可寫的情況。這樣就給我們提供這樣一個思路,只有當一個socket是可讀的, 也就是有遠程連接請求connect或是對套接口write,那么才去調用accept或是read,這樣就可以避免阻塞了。

          以準備accept的套接口為例,如果調用socket函數得到的文件描述符為s=3,那么,我可以調用select監視所有4以下的文件描述符的讀寫特 性。 select(s+1,&fd_readset,&fd_writeset,&fd_errorset,&tv_time). 這個函數是內核等待特定事件的函數,并不是只有套接口可以用。原型是

          int select(int maxfdp1,fd_set *readset,fd_set *writeset,fd_set *exceptset,const struct timeval *timeout);

          第一個參數表示觀察的最大的文件描述符,一般是你想要觀察的socket+1, 第二個參數是可讀文件描述符set,第三個是可寫set,第四個參數是error set,第五個是超時的時間。如果在超時的時間內沒有觀察過任何改動,那么函數會清空這幾個set(我個人理解),并且返回0;如果出錯,會返回-1。否 則返回>0,并且只把可讀(可寫)的文件描述符保留在對應的set內。所以只要在調用select以前,把等待連接的套接口放到可讀set中,如果 發生connect,select會返回>0,這時候可以觀察一下套接口是否還在這個set中,如果是,則說明有請求。這個方法可以用于多個套接口 同時監聽多個端口的情況。也可以用于一個已經連接的套接口等待對方發送的消息,比如write。

          對于set的操作,有如下幾個宏:FD_ZERO,FD_SET,FD_ISSET,FD_CLR.

          fd_set myset; //定義描述字集數據類型

          FD_ZERO (&myset); //對描述字集初始化

          FD_SET(s, &myset); //打開描述字的第s位,也即把套接口加入set中

          FD_ISSET(s, &myest) //測試描述字的第s位,這個宏一般是在select之后才會用到。

          FD_CLR(s, &myset) // //關閉描述字的第s位,就是在set中清除s.

          這個函數的使用方法,可以參見下面的程序。就不再細訴了。

          FD_ZERO(&fs_ReadSet);
          FD_SET(iSockets, &fs_ReadSet);
          iSockNum = iSockets+1;

          while(1)
          {
          FD_ZERO(&fs_WriteSet);
          for (iSocketz = 0; iSocketz<iSockNum; iSocketz++)
          {
          if (FD_ISSET(iSocketz, &fs_ReadSet))
          FD_SET(iSocketz, &fs_WriteSet);
          }

          tv_time.tv_sec = 2;
          tv_time.tv_usec = 500000;
          iRtn = select(iSockNum, &fs_WriteSet, NULL, NULL, &tv_time);
          if (iRtn == -1)
          {
          printf("function select error\n");
          goto error;
          }
          else if(iRtn == 0)
          {
          continue;
          }

          if (FD_ISSET(iSockets, &fs_WriteSet))
          {
          iSockLen = sizeof(sa_client);
          iSocketz = accept(iSockets, (struct sockaddr*)&sa_client, &iSockLen);
          if (iSocketz == -1)
          {
          printf("function accept error \n");
          goto error;
          }
          printf("z : %d\n", iSocketz);

          if (iSocketz >=MAX_CLIENT)
          {
          close(iSocketz);
          continue;
          }
          if (iSockNum < iSocketz+1)
          iSockNum = iSocketz+1;
          FD_SET(iSocketz, &fs_ReadSet);

          }

          for (iSocketz=iSockets+1; iSocketz < iSockNum; iSocketz++)
          {

          if (FD_ISSET(iSocketz, &fs_WriteSet))
          {
          memset(pRcvBuf, 0, BUFFERSIZE);
          memset(pSndBuf, 0, BUFFERSIZE);
          iRtn = read(iSocketz, pRcvBuf, BUFFERSIZE);
          if (iRtn == -1)
          {
          printf("function read error \n");
          goto error;
          }

          printf("%s\n", pRcvBuf);

          pSndBuf[0] = 0;
          strcpy(pSndBuf, "fuck");
          iBufLen = sizeof("fuck");

          iRtn = write(iSocketz, pSndBuf, iBufLen);
          if (iRtn == -1)
          {
          printf("function write error \n");
          goto error;
          }

          if(strcmp(pRcvBuf,"exit")==0)
          {
          FD_CLR(iSocketz, &fs_ReadSet);
          close(iSocketz);
          }
          }

          }
          for (iSocketz = iSockNum-1; (iSocketz >iSockets&&!FD_ISSET(iSocketz, &fs_ReadSet)); iSocketz = iSockNum-1)
          {
          iSockNum = iSocketz;
          }
          //printf("isockNum: %d\n",iSockNum);
          }

          至于套接口的通信,我還想說一點,套接口和文件管道差不多,一方讀一方寫,read會阻塞到對方write,而write不會阻塞。應該是這樣了。如果我個人對這個部分的理解有錯誤。會在以后改正。

          posted on 2009-12-14 11:35 石頭@ 閱讀(478) 評論(0)  編輯  收藏 所屬分類: Tcp/Ip

          主站蜘蛛池模板: 和林格尔县| 盘锦市| 纳雍县| 手游| 凤冈县| 广丰县| 双辽市| 永州市| 蛟河市| 龙泉市| 茶陵县| 黄陵县| 庄河市| 张北县| 长岛县| 土默特左旗| 临清市| 原阳县| 讷河市| 东港市| 洛南县| 济宁市| 武乡县| 西充县| 宜城市| 栾城县| 华宁县| 孝义市| 三穗县| 唐海县| 鲁山县| 平乐县| 东乡族自治县| 黎平县| 若羌县| 肥东县| 怀宁县| 周宁县| 蒙自县| 大荔县| 犍为县|