• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            隨筆 - 97, 文章 - 22, 評論 - 81, 引用 - 0
            數(shù)據(jù)加載中……

            網(wǎng)絡(luò)編程路漫漫(一)啟程

            一、套接字
                  1、什么是套接字(socket)
                  2、創(chuàng)建套接字
                        1) 協(xié)議族(Protocol Family)
                        2) 套接字類型
                        3) 協(xié)議確定
                  3、分配IP和端口
                        1) IP(Internet Protocol)
                        2) 端口
                        3) 地址信息詳解
                        4) 主機字節(jié)序/網(wǎng)絡(luò)字節(jié)序
                        5) 綁定IP和端口
            二、基于TCP的服務(wù)器端
                  1、TCP/IP協(xié)議
                  2、TCP服務(wù)器主流程
                  3、等待連接請求
                  4、受理請求
                  5、數(shù)據(jù)交換
                  6、斷開連接
                  7、調(diào)試工具
            三、基于TCP的客戶端
                  1、TCP客戶端主流程
                  2、請求連接
            四、回顧主流程

             一、套接字
                    1、什么是套接字(socket)
                    首先,我不想闡述太多的概念,直接拿例子說話最實際,說得越多就越亂。那么讓我們先來看一個概念(囧),網(wǎng)絡(luò)編程。
                    網(wǎng)絡(luò)編程就是編寫程序?qū)膳_連網(wǎng)的計算機實現(xiàn)數(shù)據(jù)交換。如何進行數(shù)據(jù)交換?首先需要物理連接,這個不是我們程序員需要關(guān)心的事情,我們需要關(guān)心的是如何編寫數(shù)據(jù)傳輸軟件。操作系統(tǒng)為我們提供了“套接字”(socket)模塊來干這件事。
                    套接字是網(wǎng)絡(luò)數(shù)據(jù)傳輸用的軟件設(shè)備。更加直白的解釋是這樣的:網(wǎng)絡(luò)上兩個程序通過一個雙向的通信連接實現(xiàn)數(shù)據(jù)交換,這個連接的一端稱為一個套接字。
                    2、創(chuàng)建套接字
                    好了,概念貌似是比較清晰了的樣子,那么讓我們來看看套接字到底是個什么鬼。
                    創(chuàng)建套接字的函數(shù)如下:
                            int socket(int domain, int type, int protocol);
                                      成功時返回文件描述符,失敗時返回-1。
                    這個函數(shù)是操作系統(tǒng)提供的,用于創(chuàng)建一個套接字的內(nèi)核對象(內(nèi)核對象是指由操作系統(tǒng)創(chuàng)建的一系列資源,比如進程、線程、文件、套接字、信號量、互斥量等等)。返回值是一個文件描述符,可以簡單的理解為一個ID。如果熟悉MFC的話,我們會發(fā)現(xiàn)Windows開發(fā)時創(chuàng)建窗口返回的是一個叫“句柄”的東西。沒錯了,Linux上叫文件描述符,Windows上叫句柄。
                    這里有必要講一下文件描述符和socket的關(guān)系,因為在Linux下socket操作和文件操作沒有區(qū)別,即socket也是文件的一種(Windows下并非如此)。文件描述符是系統(tǒng)分配給文件或套接字的整數(shù)。在stdio.h頭文件下有三個預(yù)定義的文件描述符:
                #define stdin  (&__iob_func()[0])
                #define stdout (&__iob_func()[1])
                #define stderr (&__iob_func()[2])
                     即0代表標準輸入,1代表標準輸出,2代表標準錯誤。所以應(yīng)用程序申請的socket編號從3開始。
                     接下來解釋下socket創(chuàng)建時用到的參數(shù):
                     domain 代表套接字中使用的協(xié)議族,type 代表套接字數(shù)據(jù)傳輸類型,protocol 代表通信協(xié)議。這樣一解釋,是不是本來還有點理解,現(xiàn)在完全懵逼了?不要緊張,一個一個來解釋。
                        1)協(xié)議族(Protocol Family)
                     能用圖的時候堅決不寫字,所以就有了下面這個圖。
             
            圖一-2-1
                    IPv4是互聯(lián)網(wǎng)協(xié)議的第四版,也是第一個被廣泛使用,構(gòu)成現(xiàn)今互聯(lián)網(wǎng)技術(shù)的基礎(chǔ)的協(xié)議。所以我們著重講解PF_INET,其它的暫且可以不管了,是不是很開心?
                        2)套接字類型
                    套接字類型決定了數(shù)據(jù)傳輸方式。總共有兩種:SOCK_STREAM創(chuàng)建面向連接的套接字、SOCK_DGRAM創(chuàng)建面向消息的套接字。
                    SOCK_STREAM:
                        面向連接,可以理解成一條傳送帶,只要這條傳送帶質(zhì)量沒有問題(也就是網(wǎng)一直連著),那么傳送帶上的物品就不會丟失,較晚的物品不會先到達(傳送帶的保序特性),并且傳輸?shù)奈锲凡淮嬖跀?shù)據(jù)邊界:即發(fā)送方和接收方的發(fā)送/接收的動作并非一一對應(yīng),比如發(fā)送方在傳送帶起點連續(xù)多次放了一些物品,接收方可以只通過一次操作就取走所有物品。這是因為發(fā)送和接收數(shù)據(jù)有一個內(nèi)部緩沖(buffer),發(fā)送方的數(shù)據(jù)通過發(fā)送方的輸出緩沖存放至接收方的輸入緩沖,如果接收方不取走數(shù)據(jù),這些數(shù)據(jù)就一直在它的輸入緩沖中。緩沖滿了會提供數(shù)據(jù)重傳機制,所以面向連接的套接字不會存在數(shù)據(jù)丟失。一句話概括:
                        可靠、保序、面向連接的數(shù)據(jù)傳輸方式的套接字。
                    SOCK_DGRAM:
                        面向消息,可以理解成快遞。快遞運送過程有可能丟失,不同快遞公司運送的速度不同,所以無法保證先寄出的快遞先到達,并且發(fā)送一個快遞,接收的也是一個,因此有數(shù)據(jù)邊界。而且必須限制快遞的大小。一句話概括:
                        不可靠、不保序、以高速數(shù)據(jù)傳輸為目的的套接字。
                        3)協(xié)議確定
                    參數(shù)PF_INET指定的IPv4協(xié)議族中,指定SOCK_STREAM面向連接的傳輸方式,滿足前兩個條件的協(xié)議只有IPPROTO_TCP,因此可以如下調(diào)用創(chuàng)建:
                    int tcp_sock = socket(PF_INET, SOCK_STREAM, 0);
                    參數(shù)PF_INET指定的IPv4協(xié)議族中,指定SOCK_DGRAM面向消息的傳輸方式,滿足前兩個條件的協(xié)議只有IPPROTO_UDP,因此可以如下調(diào)用創(chuàng)建:
                    int udp_sock = socket(PF_INET, SOCK_DGRAM, 0);
                    因為兩種socket都只有一種協(xié)議,所以第三個參數(shù)可以省略。
                    那么什么時候用TCP,什么時候用UDP呢?來日方長。先賣個關(guān)子。這里先以TCP連接為例子進行展開。
                    3、分配IP和端口
                    上面講了一大堆,竟然只講了一個socket的創(chuàng)建,看來我還是太啰嗦了。那么,既然啰嗦了,就再啰嗦幾句吧(囧)。創(chuàng)建完的套接字需要綁定一個網(wǎng)絡(luò)地址,這樣網(wǎng)絡(luò)的兩端才能進行通信。
                    綁定套接字網(wǎng)絡(luò)地址的函數(shù)如下:
                            int bind(int sockfd, struct sockaddr* addr, socket_t addrlen);
                                      成功時返回0,失敗時返回-1。
                     第一個參數(shù)就是我們創(chuàng)建的那個套接字ID(文件描述符),第二個參數(shù)就說來話長了,首先來看下sockaddr這個結(jié)構(gòu)體的定義:
                            struct sockaddr {
                      unsigned short sin_family;       /* Address family */
                      char sa_data[14];                /* 14 bytes of protocol address */
                  };

                     看完了嗎?
                     不說話就是看完了,那我們再來看另一個?
                            struct sockaddr_in {
                      unsigned short sin_family;       /* Address family */
                      unsigned short sin_port;         /* Port number */
                      struct in_addr sin_addr;         /* Internet address */
                      unsigned char sin_zero[8];       /* Same size as struct sockaddr */
                  };

                     嗯,細心的你應(yīng)該發(fā)現(xiàn)了,還有一個結(jié)構(gòu)體沒有定義。
                            struct in_addr {
                        unsigned int s_addr;            /* Internet address */
                  };
                     現(xiàn)在開始科普,1個sockaddr結(jié)構(gòu)體的總字節(jié)數(shù)為16(2+14),sockaddr_in的總字節(jié)數(shù)也是16(2+2+4+8)。所以兩者可以通過取地址后用指針進行強制轉(zhuǎn)換(C/C++中的基礎(chǔ)知識)。那么為什么要設(shè)計兩種結(jié)構(gòu)體呢?先來看下IP和端口的定義。
                        1)IP(Internet Protocol)
                    為了使計算機連接到網(wǎng)絡(luò)并收發(fā)數(shù)據(jù),必須向其分配IP地址。IP地址主要分為兩類:IPv4和IPv6。IPv4是4字節(jié)的,IPv6是為了應(yīng)對IP地址耗盡的問題而提出的標準,它是16字節(jié)的。目前主要應(yīng)用的還是IPv4,這里也只討論IPv4的情況。
                    IPv4標準的4字節(jié)IP地址分為網(wǎng)絡(luò)地址和主機地址,且分為A、B、C、D、E等類型,E類作為保留使用,如圖一-3-1所示。
            圖一-3-1
                    數(shù)據(jù)在互聯(lián)網(wǎng)進行傳輸?shù)臅r候,首先瀏覽網(wǎng)絡(luò)地址,將數(shù)據(jù)傳輸?shù)綄?yīng)的網(wǎng)絡(luò),再由該網(wǎng)絡(luò)將數(shù)據(jù)分派到對應(yīng)的主機。
                    并且只需要判斷首字節(jié)就能清楚的知道是哪類地址。A類地址首字節(jié)范圍:0~127,B類地址首字節(jié)范圍:128~191,A類地址首字節(jié)范圍:192~223。
                        2)端口號
                    IP用于區(qū)分計算機,端口號用于區(qū)分應(yīng)用程序。比如你在看視頻和瀏覽網(wǎng)頁的時候都需要用到數(shù)據(jù)傳輸,那么如何區(qū)分數(shù)據(jù)是傳遞到那個應(yīng)用程序呢,就需要給套接字指定端口號。端口號是一個2字節(jié)的無符號整型,端口號范圍0-65535,其中0-1023為知名端口已經(jīng)被占用(如FTP、HTTP、SMTP)。
                        3)地址信息詳解
                    了解IP和端口后,就可以填充sockaddr_in結(jié)構(gòu)了。讓我們再來回顧一下sockaddr_in結(jié)構(gòu)體。
                            * sin_family
                                    還記得創(chuàng)建socket時候的第一個參數(shù)嗎?我們這里只討論IPv4協(xié)議族,所以這里用PF_INET即可(有些代碼里用AF_INET,它和PF_INET在Windows里是同一個宏)。
                            * sin_port
                                    保存16位(2字節(jié))端口號,需要以網(wǎng)絡(luò)字節(jié)序保存(稍后介紹)。
                            * sin_addr
                                    保存32位(4字節(jié))IP地址信息,以網(wǎng)絡(luò)字節(jié)序保存。雖然是個結(jié)構(gòu)體,但是結(jié)構(gòu)體下只有一個整型變量,所以可以直接理解成32位整數(shù)即可。
                            * sin_zero
                                    保留字段,只是為了和sockaddr字節(jié)對齊。引入sockaddr_in的原因是當協(xié)議族不是PF_INET時,情況不一樣。比如IPv6的情況,IP地址是16個字節(jié)的,sin_addr明顯不夠用。
                        4)主機字節(jié)序/網(wǎng)絡(luò)字節(jié)序
                    主機字節(jié)序主要有兩種:大端序:高字節(jié)放低位地址、小端序:高字節(jié)放高位地址。
                    例如,一個2字節(jié)的數(shù)字0x1234,存放的基地址為0x10。如果采用大端序,0x10存放0x12,0x11存放0x34。如果采用小端序,0x10存放0x34,0x11存放0x12。
                    網(wǎng)絡(luò)傳輸數(shù)據(jù)時統(tǒng)一采用大端序。所以必須先將數(shù)據(jù)轉(zhuǎn)化成大端序格式再進行網(wǎng)絡(luò)傳輸。轉(zhuǎn)換函數(shù)操作系統(tǒng)已經(jīng)給出了,總共有如下四個:htons、ntohs、htonl、ntohl。
                    我去!這是什么鬼?
                    不用擔心,其實這四個函數(shù)很容易識別,它是由h、to、n、l、s這幾個詞拼出來的。h代表host,n則是network,l是long,s是short。
                    主要用于對IP和端口進行主機字節(jié)序和網(wǎng)絡(luò)字節(jié)序的轉(zhuǎn)換。那么也許有人會問,那實際數(shù)據(jù)傳輸?shù)臅r候也需要這么干嗎?答案是否定的!
                        5)綁定IP和端口
                    實際綁定過程如下代碼所示:
                            #define IP "156.123.122.11"
                            #define PORT 10101
                            struct sockaddr_in serv_addr;

                            memset(&serv_addr, 0, sizeof(serv_addr));
                            serv_addr.sin_family = PF_INET;
                            serv_addr.sin_addr.s_addr = inet_addr(IP);
                            serv_addr.sin_port = htons(PORT);
                            bind(serv_sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr));
                    其中inet_addr是系統(tǒng)函數(shù),將點分十進制的IP地址轉(zhuǎn)換成32位大端序的整型值,失敗返回 INADDR_NONE。
             二、基于TCP的服務(wù)器端
                    1、TCP/IP協(xié)議
                    根據(jù)數(shù)據(jù)傳輸方式的不同,基于網(wǎng)絡(luò)協(xié)議的套接字一般分為TCP套接字和UDP套接字。還記得創(chuàng)建套接字時指定的第二個參數(shù)嗎?當它為SOCK_STREAM時,即為TCP套接字。TCP是Transmission Control Protocol(傳輸控制協(xié)議)的簡稱。TCP和UDP處于網(wǎng)絡(luò)四層協(xié)議棧的傳輸層(網(wǎng)絡(luò)接口層-IP層-傳輸層-應(yīng)用層)。之所以要分層,是為了通過標準化操作設(shè)計開放式系統(tǒng),路由器用來完成IP層的交互任務(wù),不同公司生產(chǎn)的路由器可以進行互相替換,因為生產(chǎn)商會按照IP層的標準制造。而網(wǎng)卡則是遵循了網(wǎng)絡(luò)接口層的協(xié)議標準制造的。
                    2、TCP服務(wù)端主流程

            圖二-2-1
                    如圖二-2-1所示,為TCP服務(wù)端的函數(shù)調(diào)用順序。調(diào)用socket創(chuàng)建套接字,然后利用bind為套接字分配地址。利用listen使對應(yīng)套接字進入等待連接請求狀態(tài)。如果有新的請求,則調(diào)用accept接收新的客戶端連接。接著使用read(接收)和write(發(fā)送)實現(xiàn)數(shù)據(jù)交換。數(shù)據(jù)交換完畢調(diào)用close關(guān)閉套接字。
                    3、等待連接請求
                    只有當服務(wù)端調(diào)用了listen函數(shù)等待連接請求,客戶端才有機會接入。listen的調(diào)用比較簡單。
                            int listen(int sockfd, int backlog);
                                      成功時返回0,失敗時返回-1。
                    sockfd代表了希望進入等待連接請求的文件描述符,傳遞完畢后該套接字成為服務(wù)器端套接字(監(jiān)聽套接字)。backlog為指定的連接請求等待隊列的長度。
                    對于一個TCP連接,服務(wù)器端與客戶端需要通過三次握手來建立網(wǎng)絡(luò)連接。當三次握手成功后,端口狀態(tài)由LISTEN轉(zhuǎn)變?yōu)镋STABLISHED,接著這條鏈路上就可以開始傳送數(shù)據(jù)了。每一個處于監(jiān)聽(Listen)狀態(tài)的端口,都有自己的監(jiān)聽隊列。監(jiān)聽隊列的長度與如下兩方面有關(guān):
                        a. proc/sys/net/core/somaxconn
                        b. listen 的第二個參數(shù)backlog
                    隊列大小為兩者的小值,可以通過 echo 1000 > proc/sys/net/core/somaxconn 來修改前者。第二個參數(shù)backlog設(shè)置太小會導(dǎo)致高并發(fā)情況下,客戶端connect的時候隊列滿了,服務(wù)器端就不會受理了,客戶端繼續(xù)嘗試...如果還是滿的...就這樣惡性循環(huán),最后導(dǎo)致連接超時。
                    4、受理請求
                    服務(wù)端受理客戶端請求是由以下函數(shù)完成的:
                            int accept(int sockfd, struct sockaddr* addr, socket_t* addrlen);
                                      成功時返回創(chuàng)建的套接字文件描述符,失敗時返回-1。
                    函數(shù)調(diào)用成功后,操作系統(tǒng)將產(chǎn)生用于數(shù)據(jù)I/O的套接字。當客戶端發(fā)起connect(稍后第三節(jié)會講到)請求,服務(wù)端不會馬上受理,而是將這些請求放在listen所對應(yīng)套接字的等待隊列中,accept的作用就是將隊列首的請求取出來進行受理。accept的第二個參數(shù)addr就保存了客戶端的地址信息,因為調(diào)用前我們并不知道是哪個客戶端接入的,所以我們并不需要填充addr指向的結(jié)構(gòu)。當accept返回時,自然就把地址填在*addr(注意前面的*,表示解引用,因為addr是個指針)了。
                    并且如果沒有特殊指定,accept是同步阻塞的,即當?shù)却犃袨榭諘r,accept函數(shù)不會返回。
                    5、數(shù)據(jù)交換
                    所謂數(shù)據(jù)交換,就是我們通常所說的I/O(Input/Output)。對于TCP連接(UDP的先不介紹),主要有兩個系列的函數(shù):
                    a、read/write      (Linux)
                    b、recv/send       (Windows/Linux)
                    限于篇幅問題,這里簡單介紹下read和write,剩余內(nèi)容待到日后再詳細研究。
                            int read(int fd, void *buf, size_t nbytes);
                                      成功時返回接收到的字節(jié)數(shù)(遇到文件結(jié)尾返回0),失敗時返回-1。
                    read用于讀取(接收)數(shù)據(jù)。fd代表數(shù)據(jù)接收對象的文件描述符,可以是文件也可以是套接字;buf保存了接收數(shù)據(jù)的首地址;nbytes代表將要接收的最大字節(jié)數(shù)。
                            int write(int fd, const void *buf, size_t nbytes);
                                      成功時返回發(fā)送的字節(jié)數(shù),失敗時返回-1。
                    write用于寫入(發(fā)送)數(shù)據(jù)。fd代表數(shù)據(jù)發(fā)送對象的文件描述符,可以是文件也可以是套接字;buf傳入的是發(fā)送數(shù)據(jù)的首地址;nbytes代表發(fā)送的最大字節(jié)數(shù)。
                    read和write相呼應(yīng),一端進行write,另一端進行read。這里涉及到I/O緩沖的問題,有必要解釋一下。對于TCP而言,數(shù)據(jù)收發(fā)無邊界,意味著服務(wù)端調(diào)用3次write函數(shù),每次10字節(jié)(數(shù)據(jù)收發(fā)以字節(jié)為單位),客戶端有可能通過一次read調(diào)用直接接收30字節(jié)的數(shù)據(jù),反之亦然。那么這個是如何做到的呢?發(fā)送出去的字節(jié)在沒有接收的情況下是寄存在哪里的?
                    如圖二-5-1,write函數(shù)調(diào)用后并非立即發(fā)送數(shù)據(jù),read函數(shù)調(diào)用后也并非立即接收數(shù)據(jù)。write調(diào)用瞬間,數(shù)據(jù)將移至輸出緩沖;read調(diào)用瞬間,從輸入緩沖讀取數(shù)據(jù)。并且在適當?shù)臅r候,會將本地的輸出緩沖的數(shù)據(jù)傳送到對方的輸入緩沖中區(qū)。
            圖二-5-1
                    這里需要理清幾個知識點:
                    a.每個TCP套接字都有自己的I/O緩沖,并且在創(chuàng)建該套接字時自動生成(即用戶不需要關(guān)心它的創(chuàng)建和銷毀)。
                    b.如果輸出緩沖中還有數(shù)據(jù),關(guān)閉套接字,這些數(shù)據(jù)還是會傳送出去。
                    c.如果輸入緩沖中還有數(shù)據(jù),關(guān)閉套接字,這些數(shù)據(jù)就丟失了。
                    那么write函數(shù)何時返回?等到對方read之后才返回?答案是否定的。我們之前提到了輸出緩沖,沒錯,write函數(shù)就是負責將數(shù)據(jù)移動到輸出緩沖,然后就返回了。那么如果輸出緩沖滿了怎么辦?這個又是一個值得討論的問題。需要分阻塞socket和非阻塞socket,限于篇幅先不考慮,以后專門講這個問題。對于read的時候緩沖區(qū)為空時,同樣需要考慮阻塞和非阻塞的情況。
                    6、關(guān)閉套接字
                    直接給出關(guān)閉函數(shù):
                            int close(int sockfd);
                                      成功時返回0,失敗時返回-1。
                    關(guān)閉函數(shù)還有一個叫shutdown的,屬于“半關(guān)閉”。這里先介紹到這里。
                    最后,附上只處理一個請求的服務(wù)端源碼:https://pan.baidu.com/s/1qXGuhB2
                    7、調(diào)試工具
                    這里假設(shè)我們的服務(wù)器是架設(shè)在Linux上的,可以利用gcc(GNU Compiler Collection)對源文件進行編譯和鏈接。
                        gcc test.c -o test
                                將test.c預(yù)處理、匯編、編譯并鏈接形成可執(zhí)行文件test。-o選項用來指定輸出文件的文件名。
                        gcc -g test.c -o test
                                增加-g選項,便于用gdb進行調(diào)試。
                    程序出現(xiàn)錯誤后,會生成一個core.test.30212的dump文件(其中30212為當時運行時候的進程ID),可以利用gdb進行堆棧查看。
                        gdb test core.test.30212
                    運行指令后,進入gdb調(diào)試界面。輸入where、backtrace、info stack都可以查看到發(fā)生錯誤的堆棧。
                    注意:寫代碼的時候可以在核心代碼的前后打上printf用于測試代碼是否正確運行到這里,注意Linux下printf最后需要加上'\n',因為printf是行緩沖。
             三、基于TCP的客戶端
                    1、TCP客戶端主流程
                    客戶端的結(jié)構(gòu)相對于服務(wù)端比較簡單,創(chuàng)建socket之后調(diào)用connect進行連接,剩下的流程就一樣了。
            圖三-1-1
                    2、請求連接
                    請求連接的函數(shù)如下:
                            int connect(int sockfd, struct sockaddr* servaddr, socket_t addrlen);
                                      成功時返回0,失敗時返回-1。
                    第一個參數(shù)為客戶端的文件描述符,第二個參數(shù)為服務(wù)端的網(wǎng)絡(luò)地址(同上文中的bind)的指針,第三個參數(shù)傳入sizeof(sockaddr)即可。
                    上文說到服務(wù)端調(diào)用listen函數(shù)后創(chuàng)建連接請求的等待隊列,這時候客戶端調(diào)用connect進行請求。connect函數(shù)的返回條件是服務(wù)端接收連接請求或者發(fā)生斷網(wǎng)等異常情況,這里的服務(wù)端“接收連接“并不是代表服務(wù)端調(diào)用accept,其實服務(wù)端把請求記錄到等待隊列里。
                    客戶端進行本次連接的IP和端口號并不需要應(yīng)用程序去分配,操作系統(tǒng)已經(jīng)把這點做好了。
                    同樣附上一個簡單的客戶端連接源碼:https://pan.baidu.com/s/1nuRV8Zf

            四、回顧主流程
                    最后我們來回顧下,今天學(xué)到的主要內(nèi)容。用偽代碼來描述下整個過程。
                    服務(wù)端:
                    int main() {
                        server_sock = socket();
                        bind(server_sock);
                        listen(server_sock, 5);
                        client_sock = accept(server_sock);
                        read(client_sock, buf);
                        write(client_sock, buf);
                        close(client_sock);
                        close(server_sock);
                    }
                    客戶端:
                    int main() {
                        sock = socket();
                        connect(sock);
                        write(sock, buf);
                        read(sock, buf);
                        close(sock);
                    }
                
                    (未完待續(xù)...)

            posted on 2017-12-20 20:36 英雄哪里出來 閱讀(1493) 評論(0)  編輯 收藏 引用


            只有注冊用戶登錄后才能發(fā)表評論。
            網(wǎng)站導(dǎo)航: 博客園   IT新聞   BlogJava   博問   Chat2DB   管理


            久久狠狠高潮亚洲精品| 99久久免费国产特黄| 香蕉久久夜色精品国产尤物| 日韩欧美亚洲综合久久| 婷婷久久香蕉五月综合加勒比| 亚洲精品乱码久久久久久按摩| 国产精品美女久久久久网| 久久激情五月丁香伊人| 亚洲精品99久久久久中文字幕| 国产精品久久久久天天影视| 少妇被又大又粗又爽毛片久久黑人| 麻豆亚洲AV永久无码精品久久| 久久久99精品成人片中文字幕| 少妇久久久久久久久久| 国产成人综合久久精品尤物| 国内精品人妻无码久久久影院导航 | 亚洲va国产va天堂va久久| 亚洲欧美成人综合久久久 | 久久99国产精品99久久| 精品人妻伦九区久久AAA片69 | 麻豆精品久久精品色综合| 久久乐国产精品亚洲综合| 久久精品九九亚洲精品| 色综合久久88色综合天天 | 国产精品成人99久久久久91gav| 亚洲人成精品久久久久 | 久久亚洲美女精品国产精品| 久久人人爽人人爽人人片AV麻豆 | 99久久精品免费看国产| 久久精品人人做人人爽电影蜜月| 久久经典免费视频| 亚洲国产成人精品女人久久久| 中文精品久久久久国产网址| 精品久久人妻av中文字幕| 色妞色综合久久夜夜| 久久精品国产99国产精偷| 一级做a爰片久久毛片免费陪| 国产精品久久久久久一区二区三区| 久久精品国产亚洲AV嫖农村妇女 | 久久久久婷婷| 日韩电影久久久被窝网|