成人午夜激情影院,小视频免费在线观看,国产精品夜夜嗨,欧美日韩精品一区二区在线播放

Windows Socket API 使用經(jīng)驗

2010-08-28 10:47:25來源:西部e網(wǎng)作者:

    本文是我在進(jìn)行MS-Windows、HP-Unix網(wǎng)絡(luò)編程的實踐過程中總結(jié)出來的一些經(jīng)驗,僅供大家參考。本文所談到的Socket函數(shù)如果沒有特別說明,都是指的Windows Socket API。

  一、WSAStartup函數(shù)

  int WSAStartup(
  WORD wVersionRequested,
  LPWSADATA lpWSAData
  );
  使用Socket的程序在使用Socket之前必須調(diào)用WSAStartup函數(shù)。該函數(shù)的第一個參數(shù)指明程序請求使用的Socket版本,其中高位字節(jié)指明副版本、低位字節(jié)指明主版本;操作系統(tǒng)利用第二個參數(shù)返回請求的Socket的版本信息。當(dāng)一個應(yīng)用程序調(diào)用WSAStartup函數(shù)時,操作系統(tǒng)根據(jù)請求的Socket版本來搜索相應(yīng)的Socket庫,然后綁定找到的Socket庫到該應(yīng)用程序中。以后應(yīng)用程序就可以調(diào)用所請求的Socket庫中的其它Socket函數(shù)了。該函數(shù)執(zhí)行成功后返回0。
  例:假如一個程序要使用2.1版本的Socket,那么程序代碼如下
  wVersionRequested = MAKEWORD( 2, 1 );
  err = WSAStartup( wVersionRequested, &wsaData );

  二、WSACleanup函數(shù)

  int WSACleanup (void);
  應(yīng)用程序在完成對請求的Socket庫的使用后,要調(diào)用WSACleanup函數(shù)來解除與Socket庫的綁定并且釋放Socket庫所占用的系統(tǒng)資源。

  三、socket函數(shù)

  SOCKET socket(
  int af,
  int type,
  int protocol
  );
  應(yīng)用程序調(diào)用socket函數(shù)來創(chuàng)建一個能夠進(jìn)行網(wǎng)絡(luò)通信的套接字。第一個參數(shù)指定應(yīng)用程序使用的通信協(xié)議的協(xié)議族,對于TCP/IP協(xié)議族,該參數(shù)置PF_INET;第二個參數(shù)指定要創(chuàng)建的套接字類型,流套接字類型為SOCK_STREAM、數(shù)據(jù)報套接字類型為SOCK_DGRAM;第三個參數(shù)指定應(yīng)用程序所使用的通信協(xié)議。該函數(shù)如果調(diào)用成功就返回新創(chuàng)建的套接字的描述符,如果失敗就返回INVALID_SOCKET。套接字描述符是一個整數(shù)類型的值。每個進(jìn)程的進(jìn)程空間里都有一個套接字描述符表,該表中存放著套接字描述符和套接字?jǐn)?shù)據(jù)結(jié)構(gòu)的對應(yīng)關(guān)系。該表中有一個字段存放新創(chuàng)建的套接字的描述符,另一個字段存放套接字?jǐn)?shù)據(jù)結(jié)構(gòu)的地址,因此根據(jù)套接字描述符就可以找到其對應(yīng)的套接字?jǐn)?shù)據(jù)結(jié)構(gòu)。每個進(jìn)程在自己的進(jìn)程空間里都有一個套接字描述符表但是套接字?jǐn)?shù)據(jù)結(jié)構(gòu)都是在操作系統(tǒng)的內(nèi)核緩沖里。下面是一個創(chuàng)建流套接字的例子:
  struct protoent *ppe;
  ppe=getprotobyname("tcp");
  SOCKET ListenSocket=socket(PF_INET,SOCK_STREAM,ppe->p_proto);

  四、closesocket函數(shù)

  int closesocket(
  SOCKET s
  );
  closesocket函數(shù)用來關(guān)閉一個描述符為s套接字。由于每個進(jìn)程中都有一個套接字描述符表,表中的每個套接字描述符都對應(yīng)了一個位于操作系統(tǒng)緩沖區(qū)中的套接字?jǐn)?shù)據(jù)結(jié)構(gòu),因此有可能有幾個套接字描述符指向同一個套接字?jǐn)?shù)據(jù)結(jié)構(gòu)。套接字?jǐn)?shù)據(jù)結(jié)構(gòu)中專門有一個字段存放該結(jié)構(gòu)的被引用次數(shù),即有多少個套接字描述符指向該結(jié)構(gòu)。當(dāng)調(diào)用closesocket函數(shù)時,操作系統(tǒng)先檢查套接字?jǐn)?shù)據(jù)結(jié)構(gòu)中的該字段的值,如果為1,就表明只有一個套接字描述符指向它,因此操作系統(tǒng)就先把s在套接字描述符表中對應(yīng)的那條表項清除,并且釋放s對應(yīng)的套接字?jǐn)?shù)據(jù)結(jié)構(gòu);如果該字段大于1,那么操作系統(tǒng)僅僅清除s在套接字描述符表中的對應(yīng)表項,并且把s對應(yīng)的套接字?jǐn)?shù)據(jù)結(jié)構(gòu)的引用次數(shù)減1。
  closesocket函數(shù)如果執(zhí)行成功就返回0,否則返回SOCKET_ERROR。

  五、send函數(shù)

  int send(
  SOCKET s,
  const char FAR *buf,
  int len,
  int flags
  );
  不論是客戶還是服務(wù)器應(yīng)用程序都用send函數(shù)來向TCP連接的另一端發(fā)送數(shù)據(jù)。客戶程序一般用send函數(shù)向服務(wù)器發(fā)送請求,而服務(wù)器則通常用send函數(shù)來向客戶程序發(fā)送應(yīng)答。該函數(shù)的第一個參數(shù)指定發(fā)送端套接字描述符;第二個參數(shù)指明一個存放應(yīng)用程序要發(fā)送數(shù)據(jù)的緩沖區(qū);第三個參數(shù)指明實際要發(fā)送的數(shù)據(jù)的字節(jié)數(shù);第四個參數(shù)一般置0。這里只描述同步Socket的send函數(shù)的執(zhí)行流程。當(dāng)調(diào)用該函數(shù)時,send先比較待發(fā)送數(shù)據(jù)的長度len和套接字s的發(fā)送緩沖區(qū)的長度,如果len大于s的發(fā)送緩沖區(qū)的長度,該函數(shù)返回SOCKET_ERROR;如果len小于或者等于s的發(fā)送緩沖區(qū)的長度,那么send先檢查協(xié)議是否正在發(fā)送s的發(fā)送緩沖中的數(shù)據(jù),如果是就等待協(xié)議把數(shù)據(jù)發(fā)送完,如果協(xié)議還沒有開始發(fā)送s的發(fā)送緩沖中的數(shù)據(jù)或者s的發(fā)送緩沖中沒有數(shù)據(jù),那么send就比較s的發(fā)送緩沖區(qū)的剩余空間和len,如果len大于剩余空間大小send就一直等待協(xié)議把s的發(fā)送緩沖中的數(shù)據(jù)發(fā)送完,如果len小于剩余空間大小send就僅僅把buf中的數(shù)據(jù)copy到剩余空間里(注意并不是send把s的發(fā)送緩沖中的數(shù)據(jù)傳到連接的另一端的,而是協(xié)議傳的,send僅僅是把buf中的數(shù)據(jù)copy到s的發(fā)送緩沖區(qū)的剩余空間里)。如果send函數(shù)copy數(shù)據(jù)成功,就返回實際copy的字節(jié)數(shù),如果send在copy數(shù)據(jù)時出現(xiàn)錯誤,那么send就返回SOCKET_ERROR;如果send在等待協(xié)議傳送數(shù)據(jù)時網(wǎng)絡(luò)斷開的話,那么send函數(shù)也返回SOCKET_ERROR。要注意send函數(shù)把buf中的數(shù)據(jù)成功copy到s的發(fā)送緩沖的剩余空間里后它就返回了,但是此時這些數(shù)據(jù)并不一定馬上被傳到連接的另一端。如果協(xié)議在后續(xù)的傳送過程中出現(xiàn)網(wǎng)絡(luò)錯誤的話,那么下一個Socket函數(shù)就會返回SOCKET_ERROR。(每一個除send外的Socket函數(shù)在執(zhí)行的最開始總要先等待套接字的發(fā)送緩沖中的數(shù)據(jù)被協(xié)議傳送完畢才能繼續(xù),如果在等待時出現(xiàn)網(wǎng)絡(luò)錯誤,那么該Socket函數(shù)就返回SOCKET_ERROR)

  注意:在Unix系統(tǒng)下,如果send在等待協(xié)議傳送數(shù)據(jù)時網(wǎng)絡(luò)斷開的話,調(diào)用send的進(jìn)程會接收到一個SIGPIPE信號,進(jìn)程對該信號的默認(rèn)處理是進(jìn)程終止。

    六、recv函數(shù)

  int recv(
  SOCKET s,
  char FAR *buf,
  int len,
  int flags
  );
  不論是客戶還是服務(wù)器應(yīng)用程序都用recv函數(shù)從TCP連接的另一端接收數(shù)據(jù)。該函數(shù)的第一個參數(shù)指定接收端套接字描述符;第二個參數(shù)指明一個緩沖區(qū),該緩沖區(qū)用來存放recv函數(shù)接收到的數(shù)據(jù);第三個參數(shù)指明buf的長度;第四個參數(shù)一般置0。這里只描述同步Socket的recv函數(shù)的執(zhí)行流程。當(dāng)應(yīng)用程序調(diào)用recv函數(shù)時,recv先等待s的發(fā)送緩沖中的數(shù)據(jù)被協(xié)議傳送完畢,如果協(xié)議在傳送s的發(fā)送緩沖中的數(shù)據(jù)時出現(xiàn)網(wǎng)絡(luò)錯誤,那么recv函數(shù)返回SOCKET_ERROR,如果s的發(fā)送緩沖中沒有數(shù)據(jù)或者數(shù)據(jù)被協(xié)議成功發(fā)送完畢后,recv先檢查套接字s的接收緩沖區(qū),如果s接收緩沖區(qū)中沒有數(shù)據(jù)或者協(xié)議正在接收數(shù)據(jù),那么recv就一直等待,只到協(xié)議把數(shù)據(jù)接收完畢。當(dāng)協(xié)議把數(shù)據(jù)接收完畢,recv函數(shù)就把s的接收緩沖中的數(shù)據(jù)copy到buf中(注意協(xié)議接收到的數(shù)據(jù)可能大于buf的長度,所以在這種情況下要調(diào)用幾次recv函數(shù)才能把s的接收緩沖中的數(shù)據(jù)copy完。recv函數(shù)僅僅是copy數(shù)據(jù),真正的接收數(shù)據(jù)是協(xié)議來完成的),recv函數(shù)返回其實際copy的字節(jié)數(shù)。如果recv在copy時出錯,那么它返回SOCKET_ERROR;如果recv函數(shù)在等待協(xié)議接收數(shù)據(jù)時網(wǎng)絡(luò)中斷了,那么它返回0。
  注意:在Unix系統(tǒng)下,如果recv函數(shù)在等待協(xié)議接收數(shù)據(jù)時網(wǎng)絡(luò)斷開了,那么調(diào)用recv的進(jìn)程會接收到一個SIGPIPE信號,進(jìn)程對該信號的默認(rèn)處理是進(jìn)程終止。

  七、bind函數(shù)

  int bind(
  SOCKET s,
  const struct sockaddr FAR *name,
  int namelen
  );
  當(dāng)創(chuàng)建了一個Socket以后,套接字?jǐn)?shù)據(jù)結(jié)構(gòu)中有一個默認(rèn)的IP地址和默認(rèn)的端口號。一個服務(wù)程序必須調(diào)用bind函數(shù)來給其綁定一個IP地址和一個特定的端口號。客戶程序一般不必調(diào)用bind函數(shù)來為其Socket綁定IP地址和斷口號。該函數(shù)的第一個參數(shù)指定待綁定的Socket描述符;第二個參數(shù)指定一個sockaddr結(jié)構(gòu),該結(jié)構(gòu)是這樣定義的:
  struct sockaddr {
  u_short sa_family;
  char sa_data[14];
  };
  sa_family指定地址族,對于TCP/IP協(xié)議族的套接字,給其置AF_INET。當(dāng)對TCP/IP協(xié)議族的套接字進(jìn)行綁定時,我們通常使用另一個地址結(jié)構(gòu):
  struct sockaddr_in {
  short sin_family;
  u_short sin_port;
  struct in_addr sin_addr;
  char sin_zero[8];
  };
  其中sin_family置AF_INET;sin_port指明端口號;sin_addr結(jié)構(gòu)體中只有一個唯一的字段s_addr,表示IP地址,該字段是一個整數(shù),一般用函數(shù)inet_addr()把字符串形式的IP地址轉(zhuǎn)換成unsigned long型的整數(shù)值后再置給s_addr。有的服務(wù)器是多宿主機(jī),至少有兩個網(wǎng)卡,那么運行在這樣的服務(wù)器上的服務(wù)程序在為其Socket綁定IP地址時可以把htonl(INADDR_ANY)置給s_addr,這樣做的好處是不論哪個網(wǎng)段上的客戶程序都能與該服務(wù)程序通信;如果只給運行在多宿主機(jī)上的服務(wù)程序的Socket綁定一個固定的IP地址,那么就只有與該IP地址處于同一個網(wǎng)段上的客戶程序才能與該服務(wù)程序通信。我們用0來填充sin_zero數(shù)組,目的是讓sockaddr_in結(jié)構(gòu)的大小與sockaddr結(jié)構(gòu)的大小一致。下面是一個bind函數(shù)調(diào)用的例子:
  struct sockaddr_in saddr;
  saddr.sin_family = AF_INET;
  saddr.sin_port = htons(8888);
  saddr.sin_addr.s_addr = htonl(INADDR_ANY);
  bind(ListenSocket,(struct sockaddr *)&saddr,sizeof(saddr));

  八、listen函數(shù)

  int listen( SOCKET s, int backlog );
  服務(wù)程序可以調(diào)用listen函數(shù)使其流套接字s處于監(jiān)聽狀態(tài)。處于監(jiān)聽狀態(tài)的流套接字s將維護(hù)一個客戶連接請求隊列,該隊列最多容納backlog個客戶連接請求。假如該函數(shù)執(zhí)行成功,則返回0;如果執(zhí)行失敗,則返回SOCKET_ERROR。

  九、accept函數(shù)

  SOCKET accept(
  SOCKET s,
  struct sockaddr FAR *addr,
  int FAR *addrlen
  );
  服務(wù)程序調(diào)用accept函數(shù)從處于監(jiān)聽狀態(tài)的流套接字s的客戶連接請求隊列中取出排在最前的一個客戶請求,并且創(chuàng)建一個新的套接字來與客戶套接字創(chuàng)建連接通道,如果連接成功,就返回新創(chuàng)建的套接字的描述符,以后與客戶套接字交換數(shù)據(jù)的是新創(chuàng)建的套接字;如果失敗就返回INVALID_SOCKET。該函數(shù)的第一個參數(shù)指定處于監(jiān)聽狀態(tài)的流套接字;操作系統(tǒng)利用第二個參數(shù)來返回新創(chuàng)建的套接字的地址結(jié)構(gòu);操作系統(tǒng)利用第三個參數(shù)來返回新創(chuàng)建的套接字的地址結(jié)構(gòu)的長度。下面是一個調(diào)用accept的例子:
  struct sockaddr_in ServerSocketAddr;
  int addrlen;
  addrlen=sizeof(ServerSocketAddr);
  ServerSocket=accept(ListenSocket,(struct sockaddr *)&ServerSocketAddr,&addrlen);

  十、connect函數(shù)

  int connect(
  SOCKET s,
  const struct sockaddr FAR *name,
  int namelen
  );
  客戶程序調(diào)用connect函數(shù)來使客戶Socket s與監(jiān)聽于name所指定的計算機(jī)的特定端口上的服務(wù)Socket進(jìn)行連接。如果連接成功,connect返回0;如果失敗則返回SOCKET_ERROR。下面是一個例子:
  struct sockaddr_in daddr;
  memset((void *)&daddr,0,sizeof(daddr));
  daddr.sin_family=AF_INET;
  daddr.sin_port=htons(8888);
  daddr.sin_addr.s_addr=inet_addr("133.197.22.4");
  connect(ClientSocket,(struct sockaddr *)&daddr,sizeof(daddr));

關(guān)鍵詞:WindowsSocketAPI

贊助商鏈接:

主站蜘蛛池模板: 濮阳市| 聂拉木县| 荆门市| 清徐县| 阳朔县| 禹州市| 溧水县| 桐梓县| 龙江县| 阳信县| 桐庐县| 义乌市| 天津市| 页游| 宣城市| 麻栗坡县| 哈密市| 门源| 酉阳| 海淀区| 富顺县| 沽源县| 陈巴尔虎旗| 射洪县| 宕昌县| 定南县| 清新县| 灵丘县| 平塘县| 都昌县| 且末县| 东乡族自治县| 连山| 慈溪市| 清徐县| 揭西县| 时尚| 津南区| 剑阁县| 石台县| 南丰县|