黄色网页视频 I 影音先锋日日狠狠久久 I 秋霞午夜毛片 I 秋霞一二三区 I 国产成人片无码视频 I 国产 精品 自在自线 I av免费观看网站 I 日本精品久久久久中文字幕5 I 91看视频 I 看全色黄大色黄女片18 I 精品不卡一区 I 亚洲最新精品 I 欧美 激情 在线 I 人妻少妇精品久久 I 国产99视频精品免费专区 I 欧美影院 I 欧美精品在欧美一区二区少妇 I av大片网站 I 国产精品黄色片 I 888久久 I 狠狠干最新 I 看看黄色一级片 I 黄色精品久久 I 三级av在线 I 69色综合 I 国产日韩欧美91 I 亚洲精品偷拍 I 激情小说亚洲图片 I 久久国产视频精品 I 国产综合精品一区二区三区 I 色婷婷国产 I 最新成人av在线 I 国产私拍精品 I 日韩成人影音 I 日日夜夜天天综合

socket通信

系統(tǒng) 2097 0

1.Soket 發(fā)展史以及它和 tcp/ip 的關(guān)系

七十年代中,美國國防部高研署 (DARPA) TCP/IP 的軟件提供給加利福尼亞大學(xué) Berkeley 分校后, TCP/IP 很快被集成到 Unix 中,同時出現(xiàn)了許多成熟的 TCP/IP 應(yīng)用程序接口 (API) 。這個 API 稱為 Socket 接口。今天, SOCKET 接口是 TCP/IP 網(wǎng)絡(luò)最為 通用的 API ,也是在 INTERNET 上進行應(yīng)用開發(fā)最為通用的 API 。
  九十年代初,由 Microsoft 聯(lián)合了其他幾家公司共同制定了一套 WINDOWS 下的網(wǎng)絡(luò)編程接口,即 Windows Sockets 規(guī)范。它是 Berkeley Sockets 的重要擴充,主要是增加了一些異步函數(shù),并增加了符合 Windows 消息驅(qū)動特性的網(wǎng)絡(luò)事件異步選擇機制。 Windows Sockets 規(guī)范是一套開放的、支持多種協(xié)議的 Windows 下的網(wǎng)絡(luò)編程接口。目前,在實際應(yīng)用中的 Windows Sockets 規(guī)范主要有 1.1 版和 2.0 版。兩者的最重要區(qū)別是 1.1 版只支持 TCP/IP 協(xié)議,而 2.0 版可以支持多協(xié)議, 2.0 版有良好的向后兼容 性,目前, Windows 下的 Internet 軟件都是基于 WinSock 開發(fā)的。

Socket 實際在計算機中提供了一個通信端口,可以通過這個端口與任何一個具有 Socket 接口的計算機通信。應(yīng)用程序在網(wǎng)絡(luò)上傳輸,接收的信息都通過這個 Socket 接口來實現(xiàn)。在應(yīng)用開發(fā)中就像使用文件 句柄一樣,可以對 Socket 句柄進行讀、寫操作。套接字是網(wǎng)絡(luò)的基本構(gòu)件。它是可以被命名和尋址的通信端點,使用中的每一個套接字都有其類型和一個與之相連進程。套接字存在通信區(qū)域(通信區(qū)域又稱地址簇)中。套接字只與同一區(qū)域中的套接字交換數(shù)據(jù)(跨區(qū)域時,需要執(zhí)行某和轉(zhuǎn)換進程才能實現(xiàn))。 WINDOWS 中的套接字只支持一個域 —— 網(wǎng)際域。套接字具有類型。我們將 Socket 翻譯為套接字,套接字分為以下三種類型:
  字節(jié)流套接字 (Stream Socket)  是最常用的套接字類型, TCP/IP 協(xié)議族中的 TCP 協(xié)議使用此類接口。字節(jié)流套接口提供面向連接的 ( 建立虛電路 ) 、無差錯的、發(fā)送先后順序一致的、無記錄邊界和非重復(fù)的網(wǎng)絡(luò)信包傳輸。
數(shù)據(jù)報套接字 (Datagram Socket) TCP/IP 協(xié)議族中的 UDP 協(xié)議使用此類接口,它是無連接的服務(wù),它以獨立的信包進行網(wǎng)絡(luò)傳輸,信包最大長度為 32KB ,傳輸不保證順 序性、可靠性和無重復(fù)性,它通常用于單個報文傳輸或可靠性不重要的場合。數(shù)據(jù)報套接口的一個重要特點是它保留了記錄邊界。對于這一特點。數(shù)據(jù)報套接口采用了與現(xiàn)在許多包交換網(wǎng)絡(luò) ( 例如以太網(wǎng) ) 非常類似的模型。
  原始數(shù)據(jù)報套接字 (Raw Socket)  提供對網(wǎng)絡(luò)下層通訊協(xié)議 ( IP 協(xié)議 ) 的直接訪問,它一般不是提供給普通用戶的,主要用于開發(fā)新的協(xié)議或用于提取協(xié)議較隱蔽的功能。

2 、 socket 通信概念

* 端口
網(wǎng)絡(luò)中可以被命名和尋址的通信端口,是操作系統(tǒng)可分配的一種資源。
按照 OSI 七層協(xié)議的描述,傳輸層與網(wǎng)絡(luò)層在功能上的最大區(qū)別是傳輸層提供進程通信能力。 從這個意義上講,網(wǎng)絡(luò)通信的最終地址就不僅僅是主機地址了,還包括可以描述進程的某種標(biāo)識符。為此, TCP/IP 協(xié)議提出了協(xié)議端口( protocol port ,簡稱端口)的概念,用于標(biāo)識通信的進程。
端口是一種抽象的軟件結(jié)構(gòu)(包括一些數(shù)據(jù)結(jié)構(gòu)和 I/O 緩沖區(qū))。應(yīng)用程序(即進程)通過系統(tǒng)調(diào) 用與某端口建立連接( binding )后,傳輸層傳給該端口的數(shù)據(jù)都被相應(yīng)進程所接收,相應(yīng)進程發(fā)給傳輸層的數(shù)據(jù)都通過該端口輸出。在 TCP/IP 協(xié)議的實現(xiàn)中,端口操作類似于一般的 I/O 操作,進程獲取一個端口,相當(dāng)于獲取本地唯一的 I/O 文件,可以用一般的讀寫原語訪問之。
類似于文件描述符,每個端口都擁有一個叫端口號( port number )的整數(shù)型標(biāo)識符,用于區(qū)別不同端口。由于 TCP/IP 傳輸層的兩個協(xié)議 TCP UDP 是完全獨立的兩個軟件模塊,因此各自的端口號也相互獨立,如 TCP 有一個 255 號端口, UDP 也可以有一個 255 號端口,二者并不沖突。

* 地址
網(wǎng)絡(luò)通信中通信的兩個進程分別在不同的機器上。在互連網(wǎng)絡(luò)中,兩臺機器可能位于不同的網(wǎng)絡(luò),這些網(wǎng)絡(luò)通過網(wǎng)絡(luò)互連設(shè)備(網(wǎng)關(guān),網(wǎng)橋,路由器等)連接。因此需要三級尋址:

某一主機可與多個網(wǎng)絡(luò)相連,必須指定一特定網(wǎng)絡(luò)地址;

網(wǎng)絡(luò)上每一臺主機應(yīng)有其唯一的地址;

每一主機上的每一進程應(yīng)有在該主機上的唯一標(biāo)識符。
通常主機地址由網(wǎng)絡(luò) ID 和主機 ID 組成,在 TCP/IP 協(xié)議中用 32 位整數(shù)值表示; TCP UDP 均使用 16 位端口號標(biāo)識用戶進程。

* 網(wǎng)絡(luò)字節(jié)順序
不同的計算機存放多字節(jié)值的順序不同,有的機器在起始地址存放低位字節(jié)(低價先存),有的存高位字節(jié)(高價先存)。為保證數(shù)據(jù)的正確性,在網(wǎng)絡(luò)協(xié)議中須指定網(wǎng)絡(luò)字節(jié)順序。 TCP/IP 協(xié)議使用 16 位整數(shù)和 32 位整數(shù)的高價先存格式,它們均含在協(xié)議頭文件中。

* 面向連接

可靠的報文流、可靠的字節(jié)流、可靠的連接,如:文件傳輸( FTP )、遠程登錄( Telnet
數(shù)字話音。

* 無連接

不可靠的數(shù)據(jù)報、有確認的數(shù)據(jù)報、請求-應(yīng)答,如:電子郵件( E-mail )、電子郵件中的掛號信、網(wǎng)絡(luò)數(shù)據(jù)庫查詢。

* 順序

在網(wǎng)絡(luò)傳輸中,兩個連續(xù)報文在端-端通信中可能經(jīng)過不同路徑,這樣到達目的地時的順序

可能會與發(fā)送時不同。 " 順序 " 是指接收數(shù)據(jù)順序與發(fā)送數(shù)據(jù)順序相同。 TCP 協(xié)議提供這項服務(wù)。

* 差錯控制

保證應(yīng)用程序接收的數(shù)據(jù)無差錯的一種機制。檢查差錯的方法一般是采用檢驗、檢查和 Checksum 的方法。而保證傳送無差錯的方法是雙方采用確認應(yīng)答技術(shù)。 TCP 協(xié)議提供這項服務(wù)。

* 流控制
在數(shù)據(jù)傳輸過程中控制數(shù)據(jù)傳輸速率的一種機制,以保證數(shù)據(jù)不被丟失。 TCP 協(xié)議提供這項服務(wù)。

* 字節(jié)流
字節(jié)流方式指的是僅把傳輸中的報文看作是一個字節(jié)序列,不提供數(shù)據(jù)流的任何邊界。 TCP 協(xié)議提供字節(jié)流服務(wù)。

* 報文
接收方要保存發(fā)送方的報文邊界。 UDP 協(xié)議提供報文服務(wù)。

* 全雙工 / 半雙工
端-端間數(shù)據(jù)同時以兩個方向 / 一個方向傳送。

* 緩存 / 帶外數(shù)據(jù)
在字節(jié)流服務(wù)中,由于沒有報文邊界,用戶進程在某一時刻可以讀或?qū)懭我鈹?shù)量的字節(jié)。為保證傳輸正確或采用有流控制的協(xié)議時,都要進行緩存。但對某些特殊的需求,如交互式應(yīng)用程序,又會要求取消這種緩存。

* 客戶 / 服務(wù)器模式

socket通信

TCP/IP 網(wǎng)絡(luò)應(yīng)用中,通信的兩個進程間相互作用的主要模式是客戶 / 服務(wù)器模式( Client/Server model ),即客戶向服務(wù)器發(fā)出服務(wù)請求,服務(wù)器接收到請求后,提供相應(yīng)的服務(wù)??蛻? / 服務(wù)器模式的建立基于以下兩點:首先,建立網(wǎng)絡(luò)的起因是網(wǎng)絡(luò)中軟硬件資源、運算能力和信息不均等,需要共享,從而造就擁有眾多資源的主機提供服務(wù),資源較少的客戶請求服務(wù)這一非對等作用。其次,網(wǎng)間進程通信完全是異步的,相互通信的進程間既不存在父子關(guān)系,又不共享內(nèi)存緩沖區(qū),因此需要一種機制為希望通信的進程間建立聯(lián)系,為二者的數(shù)據(jù)交換提供同步,這就是基于客戶 / 服務(wù)器模式的 TCP/IP
客戶 / 服務(wù)器模式在操作過程中采取的是主動請求方式:
首先服務(wù)器方要先啟動,并根據(jù)請求提供相應(yīng)服務(wù):

打開一通信通道并告知本地主機,它愿意在某一公認地址上(周知口,如 FTP 21 )接收客戶請求;

等待客戶請求到達該端口;

接收到重復(fù)服務(wù)請求,處理該請求并發(fā)送應(yīng)答信號。接收到并發(fā)服務(wù)請求,要激活一新進程來處理這個客戶請求(如 UNIX 系統(tǒng)中用 fork 、 exec )。新進程處理此客戶請求,并不需要對其它請求作出應(yīng)答。服務(wù)完成后,關(guān)閉此新進程與客戶的通信鏈路,并終止。

返回第二步,等待另一客戶請求。

關(guān)閉服務(wù)器

客戶方:

打開一通信通道,并連接到服務(wù)器所在主機的特定端口;

向服務(wù)器發(fā)服務(wù)請求報文,等待并接收應(yīng)答;繼續(xù)提出請求 ......

請求結(jié)束后關(guān)閉通信通道并終止。

從上面所描述過程可知:

客戶與服務(wù)器進程的作用是非對稱的,因此編碼不同。

服務(wù)進程一般是先于客戶請求而啟動的。只要系統(tǒng)運行,該服務(wù)進程一直存在,直到正常或強迫終止。

3.socket 通信五元組

* SOCKET PASCAL FAR socket(int af, int type, int protocol)
該調(diào)用要接收三個參數(shù): af 、 type 、 protocol 。參數(shù) af 指定通信發(fā)生的區(qū)域, UNIX 系統(tǒng)支持的地址族有: AF_UNIX 、 AF_INET AF_NS 等,而 DOS 、 WINDOWS 中僅支持 AF_INET ,它是網(wǎng)際網(wǎng)區(qū)域。因此,地址族與協(xié)議族相同。參數(shù) type 描述要建立的套接字的類型。參數(shù) protocol 說明該套接字使用的特定協(xié)議,如果調(diào)用者不希望特別指定使用的協(xié)議,則置為 0 ,使用默認的連接模式。根據(jù)這三個參數(shù)建立一個套接字,并將相應(yīng)的資源分配給它,同時返回一個整型套接字號。因此, socket() 系統(tǒng)調(diào)用實際上指定了相關(guān)五元組中的 " 協(xié)議 " 這一元。

TCP/IP socket 提供下列三種類型套接字。

流式套接字( SOCK_STREAM
提供了一個面向連接、可靠的數(shù)據(jù)傳輸服務(wù),數(shù)據(jù)無差錯、無重復(fù)地發(fā)送,且按發(fā)送順序接收。內(nèi)設(shè)流量控制,避免數(shù)據(jù)流超限;數(shù)據(jù)被看作是字節(jié)流,無長度限制。文件傳送協(xié)議( FTP )即使用流式套接字。

數(shù)據(jù)報式套接字( SOCK_DGRAM
提供了一個無連接服務(wù)。數(shù)據(jù)包以獨立包形式被發(fā)送,不提供無錯保證,數(shù)據(jù)可能丟失或重復(fù),并且接收順序混亂。網(wǎng)絡(luò)文件系統(tǒng)( NFS )使用數(shù)據(jù)報式套接字。

原始式套接字( SOCK_RAW
該接口允許對較低層協(xié)議,如 IP ICMP 直接訪問。常用于檢驗新的協(xié)議實現(xiàn)或訪問現(xiàn)有服務(wù)中配置的新設(shè)備。

* int PASCAL FAR bind(SOCKET s, const struct sockaddr FAR * name, int namelen)
參數(shù) s 是由 socket() 調(diào)用返回的并且未作連接的套接字描述符 ( 套接字號 ) 。參數(shù) name 是賦給套接字 s 的本地地址(名字),其長度可變,結(jié)構(gòu)隨通信域的不同而不同。 namelen 表明了 name 的長度。

* int PASCAL FAR connect(SOCKET s, const struct sockaddr FAR * name, int namelen);
參數(shù) s 是欲建立連接的本地套接字描述符。參數(shù) name 指出說明對方套接字地址結(jié)構(gòu)的指針。對方套接字地址長度由 namelen 說明。

* SOCKET PASCAL FAR accept(SOCKET s, struct sockaddr FAR* addr, int FAR* addrlen);
參數(shù) s 為本地套接字描述符,在用做 accept() 調(diào)用的參數(shù)前應(yīng)該先調(diào)用過 listen() 。 addr 指向客戶方套接字地址結(jié)構(gòu)的指針,用來接收連接實體的地址。 addr 的確切格式由套接字創(chuàng)建時建立的地址族決定。 addrlen 為客戶方套接字地址的長度(字節(jié)數(shù))。如果沒有錯誤發(fā)生, accept() 返回一個 SOCKET 類型的值,表示接收到的套接字的描述符。否則返回值 INVALID_SOCKET 。

調(diào)用 accept() 后,服務(wù)器等待從編號為 s 的套接字上接受客戶連接請求,而連接請求是由客戶方的 connect() 調(diào)用發(fā)出的。當(dāng)有連接請求到達時, accept() 調(diào)用將請求連接隊列上的第一個客戶方套接字地址及長度放入 addr addrlen ,并創(chuàng)建一個與 s 有相同特性的新套接字號。新的套接字可用于處理服務(wù)器并發(fā)請求。

監(jiān)聽連接 ── listen()
此調(diào)用用于面向連接服務(wù)器,表明它愿意接收連接。 listen() 需在 accept() 之前調(diào)用,其調(diào)用格式如下:
int PASCAL FAR listen(SOCKET s, int backlog);
參數(shù) s 標(biāo)識一個本地已建立、尚未連接的套接字號,服務(wù)器愿意從它上面接收請求。 backlog 表示請求連接隊列的最大長度,用于限制排隊請求的個數(shù),目前允許的最大值為 5 。如果沒有錯誤發(fā)生, listen() 返回 0 。否則它返回 SOCKET_ERROR 。 listen() 在執(zhí)行調(diào)用過程中可為沒有調(diào)用過 bind() 的套接字 s 完成所必須的連接,并建立長度為 backlog 的請求連接隊列。
調(diào)用 listen() 是服務(wù)器接收一個連接請求的四個步驟中的第三步。它在調(diào)用 socket() 分配一個流套接字,且調(diào)用 bind() s 賦于一個名字之后調(diào)用,而且一定要在 accept() 之前調(diào)用。

recv() 調(diào)用用于在參數(shù) s 指定的已連接的數(shù)據(jù)報或流套接字上接收輸入數(shù)據(jù),格式如下:
int PASCAL FAR recv(SOCKET s, char FAR *buf, int len, int flags);
參數(shù) s 為已連接的套接字描述符。 buf 指向接收輸入數(shù)據(jù)緩沖區(qū)的指針,其長度由 len 指定。 flags 指定傳輸控制方式,如是否接收帶外數(shù)據(jù)等。如果沒有錯誤發(fā)生, recv() 返回總共接收的字節(jié)數(shù)。如果連接被關(guān)閉,返回 0 。否則它返回 SOCKET_ERROR

send() 調(diào)用用于在參數(shù) s 指定的已連接的數(shù)據(jù)報或流套接字上發(fā)送輸出數(shù)據(jù),格式如下:
int PASCAL FAR send(SOCKET s, const char FAR *buf, int len, int flags);
參數(shù) s 為已連接的本地套接字描述符。 buf 指向存有發(fā)送數(shù)據(jù)的緩沖區(qū)的指針,其長度由 len 指定。 flags 指定傳輸控制方式,如是否發(fā)送帶外數(shù)據(jù)等。如果沒有錯誤發(fā)生, send() 返回總共發(fā)送的字節(jié)數(shù)。否則它返回 SOCKET_ERROR

輸入 / 輸出多路復(fù)用 ── select()
select()
調(diào)用用來檢測一個或多個套接字的狀態(tài)。對每一個套接字來說,這個調(diào)用可以請求讀、寫或錯誤狀態(tài)方面的信息。請求給定狀態(tài)的套接字集合由一個 fd_set 結(jié)構(gòu)指示。在返回時,此結(jié)構(gòu)被更新,以反映那些滿足特定條件的套接字的子集,同時, select() 調(diào)用返回滿足條件的套接字的數(shù)目,其調(diào)用格式如下:
int PASCAL FAR select(int nfds, fd_set FAR * readfds, fd_set FAR * writefds, fd_set FAR * exceptfds, const struct timeval FAR * timeout);
參數(shù) nfds 指明被檢查的套接字描述符的值域,此變量一般被忽略。
參數(shù) readfds 指向要做讀檢測的套接字描述符集合的指針,調(diào)用者希望從中讀取數(shù)據(jù)。參數(shù) writefds 指向要做寫檢測的套接字描述符集合的指針。 exceptfds 指向要檢測是否出錯的套接字描述符集合的指針。 timeout 指向 select() 函數(shù)等待的最大時間,如果設(shè)為 NULL 則為阻塞操作。 select() 返回包含在 fd_set 結(jié)構(gòu)中已準(zhǔn)備好的套接字描述符的總數(shù)目,或者是發(fā)生錯誤則返回 SOCKET_ERROR

select() 的機制中提供一 fd_set 的數(shù)據(jù)結(jié)構(gòu),實際上是一 long 類型的數(shù)組,每一個數(shù)組元素都能與一打開的文件句柄(不管是 Socket 句柄 , 還是其他文件或命名管道或設(shè)備句柄)建立聯(lián)系,建立聯(lián)系的工作由程序員完成,當(dāng)調(diào)用 select() 時,由內(nèi)核根據(jù) IO 狀態(tài)修改 fd_set 的內(nèi)容,由此來通知執(zhí)行了 select() 的進程哪一 Socket 或文件可讀,下面具體解釋:
#include<sys/types.h>
#include<sys/times.h>
#include<sys/select.h>

intselect(nfds,readfds,writefds,exceptfds,timeout)
intnfds;
fd_set*readfds,*writefds,*exceptfds;
structtimeval*timeout;

ndfs
select 監(jiān)視的文件句柄數(shù),視進程中打開的文件數(shù)而定 , 一般設(shè)為呢要監(jiān)視各文件中的最大文件號加一。
readfds
select 監(jiān)視的可讀文件句柄集合。
writefds:select
監(jiān)視的可寫文件句柄集合。
exceptfds
select 監(jiān)視的異常文件句柄集合。
timeout
:本次 select() 的超時結(jié)束時間。(見 /usr/sys/select.h ,可精確至百萬分之一秒?。?

當(dāng) readfds writefds 中映象的文件可讀或可寫或超時,本次 select()
就結(jié)束返回。程序員利用一組系統(tǒng)提供的宏在 select() 結(jié)束時便可判
斷哪一文件可讀或可寫。對 Socket 編程特別有用的就是 readfds 。
幾只相關(guān)的宏解釋如下:

FD_ZERO(fd_set*fdset)
:清空 fdset 與所有文件句柄的聯(lián)系。
FD_SET(intfd,fd_set*fdset)
:建立文件句柄 fd fdset 的聯(lián)系。
FD_CLR(intfd,fd_set*fdset)
:清除文件句柄 fd fdset 的聯(lián)系。
FD_ISSET(intfd,fdset*fdset)
:檢查 fdset 聯(lián)系的文件句柄 fd 是否可讀寫, >0 表示可讀寫。
(關(guān)于 fd_set 及相關(guān)宏的定義見 /usr/include/sys/types.h

這樣,你的 socket 只需在有東東讀的時候才讀入,大致如下:

...
intsockfd;
fd_setfdR;
structtimevaltimeout=..;
...
for(;;){
FD_ZERO(&fdR);
FD_SET(sockfd,&fdR);
switch(select(sockfd+1,&fdR,NULL,&timeout)){
case-1:
errorhandledbyu;
case0:
timeouthanledbyu;
default:
if(FD_ISSET(sockfd)){
nowureadorrecvsomething;
/*ifsockfdisfatherand
serversocket,ucannow
accept()*/
}
}
}

所以一個 FD_ISSET(sockfd) 就相當(dāng)通知了 sockfd 可讀。 至于 structtimeval 在此的功能,請 manselect 。不同的 timeval 設(shè)置使使 select() 表現(xiàn)出超時結(jié)束、無超時阻塞和輪詢?nèi)N特性。由于 timeval 可精確至百萬分之一秒,所以 Windows SetTimer() 根本不算什么。你可以用 select() 做一個超級時鐘。

服務(wù)器方程序:
/* File Name: streams.c */
#include
#include
#define TRUE 1
/*
這個程序建立一個套接字,然后開始無限循環(huán);每當(dāng)它通過循環(huán)接收到一個連接,則打印出一個信息。當(dāng)連接斷開,或接收到終止信息,則此連接結(jié)束,程序再接收一個新的連接。命令行的格式是: streams */
main( )
{
int sock, length;
struct sockaddr_in server;
struct sockaddr tcpaddr;
int msgsock;
char buf[1024];
int rval, len;
/*
建立套接字 */
sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock < 0) {
perror("opening stream socket");
exit(1);
}
/*
使用任意端口命名套接字 */
server.sin_family = AF_INET;
server.sin_port = INADDR_ANY;
if (bind(sock, (struct sockaddr *)&server, sizeof(server)) < 0) {
perror("binding stream socket");
exit(1);
}
/*
找出指定的端口號并打印出來 */
length = sizeof(server);
if (getsockname(sock, (struct sockaddr *)&server, &length) < 0) {
perror("getting socket name");
exit(1);
}
printf("socket port #%d/n", ntohs(server.sin_port));
/*
開始接收連接 */
listen(sock, 5);
len = sizeof(struct sockaddr);
do {
msgsock = accept(sock, (struct sockaddr *)&tcpaddr, (int *)&len);
if (msgsock == -1)
perror("accept");
else do{
memset(buf, 0, sizeof(buf));
if ((rval = recv(msgsock, buf, 1024)) < 0)
perror("reading stream message");
if (rval == 0)
printf("ending connection /n");
else
printf("-->%s/n", buf);
}while (rval != 0);
closesocket(msgsock);
} while (TRUE);
/*
因為這個程序已經(jīng)有了一個無限循環(huán),所以套接字 "sock" 從來不顯式關(guān)閉。然而,當(dāng)進程被殺死或正常終止時,所有套接字都將自動地被關(guān)閉。 */
exit(0);
}
客戶方程序:
/* File Name: streamc.c */
#include
#include
#define DATA "half a league, half a league ..."
/*
這個程序建立套接字,然后與命令行給出的套接字連接;連接結(jié)束時,在連接上發(fā)送
一個消息,然后關(guān)閉套接字。命令行的格式是: streamc 主機名 端口號
端口號要與服務(wù)器程序的端口號相同 */
main(argc, argv)
int argc;
char *argv[ ];
{
int sock;
struct sockaddr_in server;
struct hostent *hp, *gethostbyname( );
char buf[1024];
/*
建立套接字 */
sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock < 0) {
perror("opening stream socket");
exit(1);
}
/*
使用命令行中指定的名字連接套接字 */
server.sin_family = AF_INET;
hp = gethostbyname(argv[1]);
if (hp == 0) {
fprintf(stderr, "%s: unknown host /n", argv[1]);
exit(2);
}
memcpy((char*)&server.sin_addr, (char*)hp->h_addr, hp->h_length);
sever.sin_port = htons(atoi(argv[2]));
if (connect(sock, (struct sockaddr*)&server, sizeof(server)) < 0) {
perror("connecting stream socket");
exit(3);
}
if (send(sock, DATA, sizeof(DATA)) < 0)
perror("sending on stream socket");
closesocket(sock);
exit(0);
}

4.Windows socket 程序設(shè)計

Windows Sockets 是從 Berkeley Sockets 擴展而來的,其在繼承 Berkeley Sockets 的基礎(chǔ)上,又進行了新的擴充。這些擴充主要是提供了一些異步函數(shù),并增加了符合 WINDOWS 消息驅(qū)動特性的網(wǎng)絡(luò)事件異步選擇機制。

Windows Sockets 由兩部分組成:開發(fā)組件和運行組件。
開發(fā)組件: Windows Sockets 實現(xiàn)文檔、應(yīng)用程序接口 (API) 引入庫和一些頭文件。
運行組件: Windows Sockets 應(yīng)用程序接口的動態(tài)鏈接庫 (WINSOCK.DLL) 。

Microsoft Windows 下開發(fā) Windows Sockets 網(wǎng)絡(luò)程序與在 UNIX 環(huán)境下開發(fā) Berkeley Sockets 網(wǎng)絡(luò)程序有一定的差別,這主要時因為 Windows 是非搶先多任務(wù)環(huán)境,各任務(wù)之間的切換是通過消息驅(qū)動的。因此,在 Windows 下開發(fā) Sockets 網(wǎng)絡(luò)程序要盡量避開阻塞工作方式,而使用 Windows Sockets 提供的基于消息機制的網(wǎng)絡(luò)事件異步存取接口。

Windows Sockets 為了支持 Windows 消息驅(qū)動機制,使應(yīng)用程序開發(fā)者能夠方便地處理網(wǎng)絡(luò)通信,它對網(wǎng)絡(luò)事件采用了基于消息的異步存取策略。基于這一策略, Windows Sockets 在如下方面作了擴充:

* 異步選擇機制

UNIX Sockets 對于異步事件的選擇是靠調(diào)用 select() 函數(shù)來查詢的,這種方式對于 Windows 應(yīng)用程序來說是難以接受的。 Windows Sockets 的異步選擇函數(shù)提供了消息機制的網(wǎng)絡(luò)事件選擇,當(dāng)使用它登記的網(wǎng)絡(luò)事件發(fā)生時, Windows 應(yīng)用程序相應(yīng)的窗口函數(shù)將收到一個消息,消息中指示了發(fā)生的網(wǎng)絡(luò)事件,以及與事件相關(guān)的一些信息。

* 異步請求函數(shù)

在標(biāo)準(zhǔn) Berkeley Sockets 中,請求服務(wù)是阻塞的。 Windows Sockets 除了支持這一類函數(shù)外,還增加了相應(yīng)的異步請求服務(wù)函數(shù) WSAASyncGetXByY() 。這些異步請求函數(shù)允許應(yīng)用程序采用異步方式獲取請求信息,并且在請求的服務(wù)完成時給應(yīng)用程序相應(yīng)的窗口函數(shù)發(fā)送一個消息。

* 阻塞處理方法

Windows Sockets 為了實現(xiàn)當(dāng)一個應(yīng)用程序的套接字調(diào)用處于阻塞時,能夠放棄 CPU 讓其它應(yīng)用程序運行,它在調(diào)用處于阻塞時便進入一個叫“ HOOK ”的例程,此例程負責(zé)接收和分配 Windows 消息,這使得其它應(yīng)用程序仍然能夠接收到自己的消息并取得控制權(quán)。 Windows Sockets 還提供了兩個函數(shù) (WSASetBlockingHook() WSAUnhookBlockingHook()) 讓用戶設(shè)置和取消自己的阻塞處理例程,以支持要求復(fù)雜消息處理的應(yīng)用程序(如多文檔界面)。

* 出錯處理

Windows Sockets 為了和以后多線程環(huán)境(如 Windows/NT )兼容,它提供了兩個出錯處理函數(shù) WSAGetLastError() WSASetLastError() 來獲取和設(shè)置當(dāng)前線程的最近錯誤號,而不使用 Berkeley Sockets 中的全局變量 errno h_errno 。

* 啟動與終止

對于所有在 Windows Sockets 上開發(fā)的應(yīng)用程序,在它使用任何 Windows Sockets API 調(diào)用之前,必須先調(diào)用啟動函數(shù) WSAStartup() ,它完成 Windows Sockets DLL 的初始化;協(xié)商版本支持,分配必要的資源。在應(yīng)用程序完成了對 Windows Sockets 的使用之后,它必須調(diào)用函數(shù) WSACleanup() 來從 Windows Sockets 實現(xiàn)中注銷自己,并允許實現(xiàn)釋放為其分配的任何資源。

* 服務(wù)器端操作 socket (套接字)

在初始化階段調(diào)用 WSAStartup()
此函數(shù)在應(yīng)用程序中初始化 Windows Sockets DLL ,只有此函數(shù)調(diào)用成功后,應(yīng)用程序才可以再調(diào)用其他 Windows Sockets DLL 中的 API 函數(shù)。在程式中調(diào)用該函數(shù)的形式如下: WSAStartup((WORD)((1<<8|1) ,( LPWSADATA &WSAData) ,其中 (1<<8|1) 表示我們用的是 WinSocket1.1 版本, WSAata 用來存儲系統(tǒng)傳回的關(guān)于 WinSocket 的資料。

建立 Socket
  初始化 WinSock 的動態(tài)連接庫后,需要在服務(wù)器端建立一個 監(jiān)聽的 Socket ,為此可以調(diào)用 Socket() 函數(shù)用來建立這個監(jiān)聽的 Socket ,并定義此 Socket 所使用的通信協(xié)議。此函數(shù)調(diào)用成功返回 Socket 對象,失敗則返回 INVALID_SOCKET( 調(diào)用 WSAGetLastError() 可得知原因,所有 WinSocket 的函數(shù)都可以使用這個函數(shù)來獲取失敗的原因 )

SOCKET PASCAL FAR socket( int af, int type, int protocol )
參數(shù) : af: 目前只提供 PF_INET(AF_INET) ;
type
Socket 的類型 (SOCK_STREAM 、 SOCK_DGRAM) ;
protocol
:通訊協(xié)定 ( 如果使用者不指定則設(shè)為 0) ;

如果要建立的是遵從 TCP/IP 協(xié)議的 socket ,第二個參數(shù) type 應(yīng)為 SOCK_STREAM ,如為 UDP (數(shù)據(jù)報)的 socket ,應(yīng)為 SOCK_DGRAM

綁定端口

  接下來要為服務(wù)器端定義的這個監(jiān)聽的 Socket 指定一個地址及端口( Port ),這樣客戶端才知道待會要連接哪一個地址的哪個端口,為此我們要調(diào)用 bind() 函數(shù),該函數(shù)調(diào)用成功返回 0 ,否則返回 SOCKET_ERROR 。
int PASCAL FAR bind( SOCKET s, const struct sockaddr FAR *name,int namelen );

參 數(shù): s Socket 對象名;
name
Socket 的地址值,這個地址必須是執(zhí)行這個程式所在機器的 IP 地址;
namelen
name 的長度;

如果使用者不在意地址或端口的值,那么可以設(shè)定地址為 INADDR_ANY ,及 Port 0 , Windows Sockets 會自動將其設(shè)定適當(dāng)之地址及 Port (1024 5000 之間的值 ) 。此后可以調(diào)用 getsockname() 函數(shù)來獲知其被設(shè)定的值。

監(jiān)聽

當(dāng)服務(wù)器端的 Socket 對象綁定完成之后 , 服務(wù)器端必須建立一個監(jiān)聽的隊列來接收客戶端的連接請求。 listen() 函數(shù)使服務(wù)器端的 Socket 進入監(jiān)聽狀態(tài),并設(shè)定可以建立的最大連接數(shù) ( 目前最大值限制為 5, 最小值為 1) 。該函數(shù)調(diào)用成功返回 0 ,否則返回 SOCKET_ERROR 。

int PASCAL FAR listen( SOCKET s, int backlog );
參 數(shù): s :需要建立監(jiān)聽的 Socket
backlog
:最大連接個數(shù);

異步選擇
服務(wù)器端的 Socket 調(diào)用完 listen ()后,如果此時客戶端調(diào)用 connect ()函數(shù)提出連接申請的話, Server 端必須再調(diào)用 accept() 函數(shù),這樣服務(wù)器端和客戶端才算正式完成通信程序的連接動作。為了知道什么時候客戶端提出連接要求,從而服務(wù)器端的 Socket 在恰當(dāng)?shù)臅r候調(diào)用 accept() 函數(shù)完成連接的建立,我們就要使用 WSAAsyncSelect ()函數(shù),讓系統(tǒng)主動來通知我們有客戶端提出連接請求了。該函數(shù)調(diào)用成功 返回 0 ,否則返回 SOCKET_ERROR 。

int PASCAL FAR WSAAsyncSelect( SOCKET s, HWND hWnd,unsigned int wMsg, long lEvent );
參數(shù): s Socket 對象;
hWnd
:接收消息的窗口句柄;
wMsg
:傳給窗口的消息;
lEvent
: 被注冊的網(wǎng)絡(luò)事件,也即是應(yīng)用程序向窗口發(fā)送消息的網(wǎng)路事件,該值為下列值 FD_READ 、 FD_WRITE 、 FD_OOB FD_ACCEPT 、 FD_CONNECT 、 FD_CLOSE 的組合,各個值的具體含意為 FD_READ :希望在套接字 S 收到數(shù)據(jù)時收到消息; FD_WRITE :希望在套接字 S 上可以發(fā)送數(shù)據(jù)時收到消息; FD_ACCEPT :希望在套接字 S 上收到連接請求時收到消息; FD_CONNECT :希望在套接字 S 上連接成功時收到消 息; FD_CLOSE :希望在套接字 S 上連接關(guān)閉時收到消息; FD_OOB :希望在套接字 S 上收到帶外數(shù)據(jù)時收到消息。
  具體應(yīng)用時, wMsg 應(yīng)是在應(yīng)用程序中定義的消息名稱,而消息結(jié)構(gòu)中的 lParam 則為以上各種網(wǎng)絡(luò)事件名稱。所以,可以在窗口處理自定義消息函數(shù)中使用以下結(jié)構(gòu)來響應(yīng) Socket 的不同事件:  

switch(lParam)
{

case FD_READ:

break;
case FD_WRITE
、

break;

}

服務(wù)器端接受客戶端的連接請求
當(dāng) Client 提出連接請求時, Server hwnd 視窗會收到 Winsock Stack 送來我們自定義的一 個消息,這時,我們可以分析 lParam ,然后調(diào)用相關(guān)的函數(shù)來處理此事件。為了使服務(wù)器端接受客戶端的連接請求,就要使用 accept() 函數(shù),該函數(shù)新建一 Socket 與客戶端的 Socket 相通,原先監(jiān)聽之 Socket 繼續(xù)進入監(jiān)聽狀態(tài),等待他人的連接要求。該函數(shù)調(diào)用成功返回一個新產(chǎn)生的 Socket 對象,否則返回 INVALID_SOCKET

SOCKET PASCAL FAR accept( SCOKET s, struct sockaddr FAR *addr,int FAR *addrlen );
參數(shù): s Socket 的識別碼;
addr
:存放來連接的客戶端的地址;
addrlen
addr 的長度

結(jié)束 socket 連接
結(jié)束服務(wù)器和客戶端的通信連接是很簡單的,這一過程可以由服務(wù)器或客戶機的任一端啟動,只要調(diào)用 closesocket() 就可以了,而要關(guān)閉 Server 端監(jiān)聽狀態(tài)的 socket ,同樣也是利用此函數(shù)。另外,與程序啟動時調(diào)用 WSAStartup() 憨數(shù)相對應(yīng),程式結(jié)束前,需要調(diào)用 WSACleanup() 來通知 Winsock Stack 釋放 Socket 所占用的資源。這兩個函數(shù)都是調(diào)用成功返回 0 ,否則返回 SOCKET_ERROR

int PASCAL FAR closesocket( SOCKET s );
參 數(shù): s Socket 的識別碼;
int PASCAL FAR WSACleanup( void );
參 數(shù): 無

* 客戶端 Socket 的操作

建立客戶端的 Socket
客戶端應(yīng)用程序首先也是調(diào)用 WSAStartup() 函數(shù)來與 Winsock 的動態(tài)連接庫建立關(guān)系,然后同樣調(diào)用 socket() 來建立一個 TCP UDP socket (相同協(xié)定的 sockets 才能相通, TCP TCP UDP UDP )。與服務(wù)器端的 socket 不同的是,客戶端的 socket 可以調(diào)用 bind() 函數(shù),由自己來指定 IP 地址及 port 號碼;但是也可以不調(diào)用 bind() ,而由 Winsock 來自動設(shè)定 IP 地址及 port 號碼。

提出連接申請
  客戶端的 Socket 使用 connect() 函數(shù)來提出與服務(wù)器端的 Socket 建立連接的申請,函數(shù)調(diào)用成功返回 0 ,否則返回 SOCKET_ERROR

int PASCAL FAR connect( SOCKET s, const struct sockaddr FAR *name, int namelen );
參 數(shù): s Socket 的識別碼;
name
Socket 想要連接的對方地址;
namelen
name 的長度

* 數(shù)據(jù)的傳送
雖然基于 TCP/IP 連接協(xié)議(流套接字)的服務(wù)是設(shè)計客戶機 / 服務(wù)器應(yīng)用程序時的主流標(biāo)準(zhǔn),但有些服務(wù)也是可以通過無連接協(xié)議(數(shù)據(jù)報套接字)提供的。先介紹一下 TCP socket UDP socket 在傳送數(shù)據(jù)時的特性: Stream (TCP) Socket 提供雙向、可靠、有次序、不重復(fù)的資料傳送。 Datagram (UDP) Socket 雖然提供雙向的通信,但沒有可靠、有次序、不重復(fù)的保證,所以 UDP 傳送數(shù)據(jù)可能會收到無次序、重復(fù)的資料,甚至資料在傳輸過程中出現(xiàn)遺漏。由于 UDP Socket 在傳送資料時,并不保證資料能完整地送達對方,所以絕大多數(shù)應(yīng)用程序都是采用 TCP 處理 Socket ,以保證資料的正確性。一般情況下 TCP Socket 的數(shù)據(jù)發(fā)送和接收是調(diào)用 send() recv() 這兩個函數(shù)來達成,而 UDP Socket 則是用 sendto() recvfrom() 這兩個函數(shù),這兩個函數(shù)調(diào)用成功發(fā)揮發(fā)送或接收的資料的長度,否則返回 SOCKET_ERROR 。

int PASCAL FAR send( SOCKET s, const char FAR *buf,int len, int flags );
參數(shù): s Socket 的識別碼
buf
:存放要傳送的資料的暫存區(qū)
len buf
:的長度
flags
:此函數(shù)被調(diào)用的方式
對于 Datagram Socket 而言,若是 datagram 的大小超過限制,則將不會送出任何資料,并會傳回錯誤值。對 Stream Socket 言, Blocking 模式下,若是傳送系統(tǒng)內(nèi)的儲存空間不夠存放這些要傳送的資料, send() 將會被 block 住,直到資料送完為止;如果該 Socket 被設(shè)定為 Non-Blocking 模式,那么將視目前的 output buffer 空間有多少,就送出多少資料,并不會被 block 住。 flags 的值可設(shè)為 0 MSG_DONTROUTE MSG_OOB 的組合。

int PASCAL FAR recv( SOCKET s, char FAR *buf, int len, int flags );
參數(shù): s Socket 的識別碼
buf
:存放接收到的資料的暫存區(qū)
len buf
:的長度
flags
:此函數(shù)被調(diào)用的方式
  對 Stream Socket 言,我們可以接收到目前 input buffer 內(nèi)有效的資料,但其數(shù)量不超過 len 的大小。

* 自定義的 CMySocket 類的實現(xiàn)代碼:
根據(jù)上面的知識,自定義了一個簡單的 CMySocket 類,下面是定義的該類的部分實現(xiàn)代碼:

CMySocket::CMySocket() : file:// 類的構(gòu)造函數(shù)
{
WSADATA wsaD;
memset( m_LastError, 0, ERR_MAXLENGTH );
// m_LastError 是類內(nèi)字符串變量 , 初始化用來存放最后錯誤說明的字符串;
// 初始化類內(nèi) sockaddr_in 結(jié)構(gòu)變量,前者存放客戶端地址,后者對應(yīng)于服務(wù)器端地址 ;
memset( &m_sockaddr, 0, sizeof( m_sockaddr ) );
memset( &m_rsockaddr, 0, sizeof( m_rsockaddr ) );
int result = WSAStartup((WORD)((1<<8|1) , &wsaD);// 初始化 WinSocket 動態(tài)連接庫 ;
if( result != 0 ) // 初始化失敗;
{ set_LastError( "WSAStartup failed!", WSAGetLastError() );
return;
}
}

//////////////////////////////
CMySocket::~CMySocket() { WSACleanup(); }//
類的析構(gòu)函數(shù);
////////////////////////////////////////////////////
int CMySocket::Create( void )
{// m_hSocket 是類內(nèi) Socket 對象,創(chuàng)建一個基于 TCP/IP Socket 變量,并將值賦給該變量;
if ( (m_hSocket = socket( AF_INET, SOCK_STREAM, IPPROTO_TCP )) == INVALID_SOCKET )

{
set_LastError( "socket() failed", WSAGetLastError() );
return ERR_WSAERROR;
}
return ERR_SUCCESS;
}
///////////////////////////////////////////////
int CMySocket::Close( void )//
關(guān)閉 Socket 對象;
{
if ( closesocket( m_hSocket ) == SOCKET_ERROR )
{
set_LastError( "closesocket() failed", WSAGetLastError() );
return ERR_WSAERROR;
}
file:// 重置 sockaddr_in 結(jié)構(gòu)變量;
memset( &m_sockaddr, 0, sizeof( sockaddr_in ) );
memset( &m_rsockaddr, 0, sizeof( sockaddr_in ) );
return ERR_SUCCESS;
}
/////////////////////////////////////////
int CMySocket::Connect( char* strRemote, unsigned int iPort )//
定義連接函數(shù);
{
if( strlen( strRemote ) == 0 || iPort == 0 )
return ERR_BADPARAM;
hostent *hostEnt = NULL;
long lIPAddress = 0;
hostEnt = gethostbyname( strRemote );// 根據(jù)計算機名得到該計算機的相關(guān)內(nèi)容;
if( hostEnt != NULL )
{
lIPAddress = ((in_addr*)hostEnt->h_addr)->s_addr;
m_sockaddr.sin_addr.s_addr = lIPAddress;
}
else
{
m_sockaddr.sin_addr.s_addr = inet_addr( strRemote );
}
m_sockaddr.sin_family = AF_INET;
m_sockaddr.sin_port = htons( iPort );
if( connect( m_hSocket, (SOCKADDR*)&m_sockaddr, sizeof( m_sockaddr ) ) == SOCKET_ERROR )
{
set_LastError( "connect() failed", WSAGetLastError() );
return ERR_WSAERROR;
}
return ERR_SUCCESS;

}
///////////////////////////////////////////////////////
int CMySocket::Bind( char* strIP, unsigned int iPort )//
綁定函數(shù);
{
if( strlen( strIP ) == 0 || iPort == 0 )
return ERR_BADPARAM;
memset( &m_sockaddr,0, sizeof( m_sockaddr ) );
m_sockaddr.sin_family = AF_INET;
m_sockaddr.sin_addr.s_addr = inet_addr( strIP );
m_sockaddr.sin_port = htons( iPort );
if ( bind( m_hSocket, (SOCKADDR*)&m_sockaddr, sizeof( m_sockaddr ) ) == SOCKET_ERROR )
{
set_LastError( "bind() failed", WSAGetLastError() );
return ERR_WSAERROR;
}
return ERR_SUCCESS;
}
//////////////////////////////////////////
int CMySocket::Accept( SOCKET s )//
建立連接函數(shù), S 為監(jiān)聽 Socket 對象名;
{
int Len = sizeof( m_rsockaddr );
memset( &m_rsockaddr, 0, sizeof( m_rsockaddr ) );
if( ( m_hSocket = accept( s, (SOCKADDR*)&m_rsockaddr, &Len ) ) == INVALID_SOCKET )
{
set_LastError( "accept() failed", WSAGetLastError() );
return ERR_WSAERROR;
}
return ERR_SUCCESS;
}
/////////////////////////////////////////////////////
int CMySocket::asyncSelect( HWND hWnd, unsigned int wMsg, long lEvent )
file://
事件選擇函數(shù);
{
if( !IsWindow( hWnd ) || wMsg == 0 || lEvent == 0 )
return ERR_BADPARAM;
if( WSAAsyncSelect( m_hSocket, hWnd, wMsg, lEvent ) == SOCKET_ERROR )
{
set_LastError( "WSAAsyncSelect() failed", WSAGetLastError() );
return ERR_WSAERROR;
}
return ERR_SUCCESS;
}
////////////////////////////////////////////////////
int CMySocket::Listen( int iQueuedConnections )//
監(jiān)聽函數(shù);

{
if( iQueuedConnections == 0 )
return ERR_BADPARAM;
if( listen( m_hSocket, iQueuedConnections ) == SOCKET_ERROR )
{
set_LastError( "listen() failed", WSAGetLastError() );
return ERR_WSAERROR;
}
return ERR_SUCCESS;
}
////////////////////////////////////////////////////
int CMySocket::Send( char* strData, int iLen )//
數(shù)據(jù)發(fā)送函數(shù);
{
if( strData == NULL || iLen == 0 )
return ERR_BADPARAM;
if( send( m_hSocket, strData, iLen, 0 ) == SOCKET_ERROR )
{
set_LastError( "send() failed", WSAGetLastError() );
return ERR_WSAERROR;
}
return ERR_SUCCESS;
}
/////////////////////////////////////////////////////
int CMySocket::Receive( char* strData, int iLen )//
數(shù)據(jù)接收函數(shù);
{
if( strData == NULL )
return ERR_BADPARAM;
int len = 0;
int ret = 0;
ret = recv( m_hSocket, strData, iLen, 0 );
if ( ret == SOCKET_ERROR )
{
set_LastError( "recv() failed", WSAGetLastError() );
return ERR_WSAERROR;
}
return ret;
}
void CMySocket::set_LastError( char* newError, int errNum )
file://WinSock API
操作錯誤字符串設(shè)置函數(shù);
{
memset( m_LastError, 0, ERR_MAXLENGTH );
memcpy( m_LastError, newError, strlen( newError ) );
m_LastError[strlen(newError)+1] = '/0';
}
有了上述類的定義,就可以在網(wǎng)絡(luò)程序的服務(wù)器和客戶端分別定義 CMySocket 對象,建立連接,傳送數(shù)據(jù)了。例如,為了在服務(wù)器和客戶端發(fā)送數(shù)據(jù),需要在服務(wù)器端定義兩個 CMySocket 對象 ServerSocket1 ServerSocket2 ,分別用于監(jiān)聽和連接,客戶端定義一個 CMySocket 對象 ClientSocket ,用于發(fā)送或接收數(shù)據(jù),如果建立的連接數(shù)大于一,可以在服務(wù)器端再定義 CMySocket 對象,但要注意 連接數(shù)不要大于五。

VC 中進行 WINSOCK API 編程開發(fā)的時候,需要在項目中使用下面三個文件,否則會出現(xiàn)編譯錯誤。
1
WINSOCK.H: 這是 WINSOCK API 的頭文件,需要包含在項目中。
2
WSOCK32.LIB: WINSOCK API 連接庫文件。在使用中,一定要把它作為項目的非缺省的連接庫包含到項目文件中去。
3
WINSOCK.DLL: WINSOCK 的動態(tài)連接庫,位于 WINDOWS 的安裝目錄下。

socket通信


更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主

微信掃碼或搜索:z360901061

微信掃一掃加我為好友

QQ號聯(lián)系: 360901061

您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。

【本文對您有幫助就好】

您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描上面二維碼支持博主2元、5元、10元、自定義金額等您想捐的金額吧,站長會非常 感謝您的哦!!!

發(fā)表我的評論
最新評論 總共0條評論