• <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>

            任我行

            一天一個(gè)腳印......
            每日一句:
            posts - 54, comments - 218, trackbacks - 1, articles - 0
              C++博客 :: 首頁 :: 新隨筆 :: 聯(lián)系 :: 聚合  :: 管理

            Winsock編程 入門

            Posted on 2005-10-18 11:51 任我行 閱讀(1588) 評論(0)  編輯 收藏 引用 所屬分類: Windows編程

            一、初使化Winsock


            如果沒有初使化的話,所有的Winsock函數(shù)操作都回失敗(反回SOCKET_ERROR),錯(cuò)誤代碼為WSANOTINITIALISED。
            初使化函數(shù):
            int WSAStartup(WORD 版本號,LPWSADATA pWSADATA)
            版本號的建立可以用用宏:MAKEWORD(x,y)
            WSADATA結(jié)構(gòu):
            {
            WORD 版本
            WORD 高版本
            char[] 描述
            char[] 系統(tǒng)狀態(tài)
            unsigned short iMaxSockets(兼容低版本保留)
            unsigned short iMaxUdpDg(兼容低版本保留)
            char Far* lpVendorInfo也是兼容保留
            }

            這是我機(jī)子上連結(jié)后的運(yùn)行情況
            WSAStartup(MAKEWORD(2,2),&wsaData);
            wVersion 514 unsigned short
            wHighVersion 514 unsigned short
            szDescription 0x0012fd18 "WinSock 2.0" char [257]
            szSystemStatus 0x0012fe19 "Running" char [129]
            iMaxSockets 0 unsigned short
            iMaxUdpDg 0 unsigned short
            lpVendorInfo 0xcccccccc <錯(cuò)誤的指針> char *

            514就是0x202,也是我們的版本號。最后三項(xiàng)被忽略了

            下面一張表是各個(gè)平臺的支持的winsock版本

            Platform

            Winsock Version

            Windows 95

            1.1 (2.2)

            Windows 98

            2.2

            Windows Me

            2.2

            Windows NT 4.0

            2.2

            Windows 2000

            2.2

            Windows XP

            2.2

            Windows CE

            1.1

            int WSACleanup():
            終止使用Winsock函數(shù)。

            二、錯(cuò)誤信息

            使用當(dāng)Winsock函數(shù)返回SOCKET_ERROR時(shí)用int WSAGetLastError(void)檢測錯(cuò)誤代碼。錯(cuò)誤的代碼所對應(yīng)的錯(cuò)誤名稱可以在winsock.h或winsock2.h里找到。
            h_errno為該指定的宏。

            三、選擇一個(gè)協(xié)議

            這里簡單講講通過Internet Protocol(IP)協(xié)議建立最基本的Winsock。之所以現(xiàn)在有很大一部分的winsock程序都用它,最主要的原因是它具有廣泛的通用性。winsock還可以用別的協(xié)議,比如IPX之類的。
               從設(shè)計(jì)上講,IP是連接協(xié)議但不是數(shù)據(jù)傳輸協(xié)議。我們可以用Two higher-level protocols-Transmision Control Protocol(TCP)或者是User Datagram Protocol(UDP),他們都是通過IP,我們一起講就是TCP/IP,UDP/IP。如果你要用IPv4(IP version 4),那你必須要要知道怎樣使用IPv4  

            使用IPv4

            在IPv4里面,計(jì)算機(jī)的分配的一個(gè)地址是32位,當(dāng)客戶端想通過TCP或者UDP連接,那必須要知道主機(jī)的IP地址和端口。同樣,主機(jī)要監(jiān)聽客戶端的請求,那必須要表明一個(gè)IP地址和端口。在Winsock里面,程序表明IP地址和服務(wù)端口信息是通過SOCKADDR_IN結(jié)構(gòu)。他的聲明如下:

            struct sockaddr_in
            {
            short sin_family;
            u_short sin_port;
            struct in_addr sin_addr;
            char sin_zero[8];
            };

            struct in_addr {
            union {
            struct { u_char s_b1,s_b2,s_b3,s_b4; } S_un_b;
            struct { u_short s_w1,s_w2; } S_un_w;
            u_long S_addr;
            } S_un;

            sin_family必須填A(yù)F_INET告訴Winsock我們用的是IP地址
            sin_port說明我們選擇哪個(gè)TCP或者UDP的端口作為我們的通訊端口,對了,有些端口號保留給一些服務(wù),比如說FTP,HTTP等

            sin_addr存儲IPV4地址用4個(gè)字節(jié),就像無符號長整型(DWORD),IP地址在互聯(lián)網(wǎng)上一般用形如a.b.c.d格式。

            sin_zero只不過是讓SOCKADDR_IN和SOCKADDR結(jié)構(gòu)大小一樣。

            下面是一個(gè)很有用的函數(shù),把a(bǔ).b.c.d格式的IP地址轉(zhuǎn)成無符號長整型。
            unsigned long inet_addr(const char FAR* cp);

            字節(jié)順序

            不同的計(jì)算機(jī)處理數(shù)字有兩種形式,big-endian和little-endian型式(
            little-endian格式的數(shù)據(jù),例如0X12345678以(0X78 0X56 0X34 0X12)方式保存、
            big-endian格式的數(shù)據(jù),例如0X12345678以(0X12 0X34 0X56 0X78)方式保存 ),這依賴于他們是怎么設(shè)計(jì)的,比如Intel的x86處理器,多字節(jié)是用little-endian型式。IP地址和和端口在電腦中是多字節(jié)存放的,他們是host-byte順序,然而當(dāng)IP地址和端口通過網(wǎng)絡(luò)時(shí),必須轉(zhuǎn)成big-endian形式,也就是network-byte順序
              有一系列函數(shù)完成兩者之間的轉(zhuǎn)換。比如
            host-byte序轉(zhuǎn)network-byte序
            u_long htonl(u_long hostlong);
            int WSAHtonl( SOCKET s, u_long hostlong, u_long FAR * lpnetlong );
            u_short htons(u_short hostshort);
            int WSAHtons( SOCKET s, u_short hostshort, u_short FAR * lpnetshort );

            network-byte序轉(zhuǎn)host-byte序
            u_long ntohl(u_long netlong);
            int WSANtohl( SOCKET s, u_long netlong, u_long FAR * lphostlong );
            u_short ntohs(u_short netshort);
            int WSANtohs( SOCKET s, u_short netshort, u_short FAR * lphostshort );

            例:
            SOCKADDR_IN addr;
            INT port = 8080;
            addr.sin_family = AF_INET;
            addr.sin_addr.s_addr = inet_addr("216.239.57.99");
            addr.sin_port = htons(port);

            四、建立socket

            通過API SOCKET socket(int af,int type,int protocol);
            第一個(gè)參數(shù)是協(xié)議的地址類別,比如我們前面講用的是IPv4,那么af就是AF_INET,
            第二個(gè)參數(shù)是協(xié)議的socket類型,你用TCP/IP時(shí),type=SOCK_STREAM,你用UDP/IP時(shí),type=SOCK_DGRAM,
            第三個(gè)參數(shù)是協(xié)議是(未詳),如果是TCP的話,則該處是IPPROTO_TCP,如果是UDP的話,則該處是IPPROTO_UDP

            五、服務(wù)器API函數(shù)

            服務(wù)器是一個(gè)進(jìn)程,用來等待不定數(shù)目的客戶端連接,響應(yīng)客戶端的請求。一個(gè)服務(wù)器必須有一個(gè)可供客戶端定位的名字,在TCP/IP里,這個(gè)名字是IP地址和端口。
            第一步:用socket或者WSASocket建立socket,并用bind綁定
            第二步:socket進(jìn)入監(jiān)聽模式。(listen)
            最后:當(dāng)客戶端發(fā)出請求時(shí)響應(yīng)請求。(accept或者WSAAccept)

            綁定(Binding)

            int bind( SOCKET s, const struct sockaddr FAR* name, int namelen );
            第一個(gè)參數(shù)是要綁定的socket;
            第二個(gè)參數(shù)是表明你在使用的協(xié)議
            第三個(gè)參數(shù)表明你指定協(xié)議地址結(jié)構(gòu)的長度。

            例:
            SOCKET s;
            SOCKADDR_IN addr;
            int port = 5555;
            s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
            addr.sin_family = AF_INET;
            addr.sin_port = htons(port);
            addr.sin_addr.s_addr = htonl(INADDR_ANY);
            bind(s, (SOCKADDR *)&addr, sizeof(addr));

            監(jiān)聽(Listening)

            把socket轉(zhuǎn)成監(jiān)聽模式。bind只不過是綁定,listen是告知socket進(jìn)入等待進(jìn)入的連接。
            int listen( SOCKET s, int backlog );
            第一個(gè)參數(shù)是綁定過的socket
            第二個(gè)參數(shù)是最大隊(duì)列長度,比如說這個(gè)數(shù)設(shè)為二,與此同時(shí)有三個(gè)客戶連入,那么先進(jìn)來的二個(gè)進(jìn)入隊(duì)列,第三個(gè)則會收到WSAECONNREFUSED錯(cuò)誤信息。注意服務(wù)器Accept了一個(gè)連接,這個(gè)連接就會從隊(duì)列中移除。
            如果你沒bind而直接listen的話會收到 WSAEINVAL 出錯(cuò)信息。

            同意連接(Accepting Connectino)

            SOCKET accept( SOCKET s, struct sockaddr FAR* addr, int FAR* addrlen );

            第二個(gè)參數(shù)是你收到的客戶端地址。
            第三個(gè)參數(shù)addrlen 表明addr的長度

            六、客戶端API函數(shù)

            第一步:建立socket
            第二步:設(shè)置你要連接對象的SOCKADDR地址
            第三步:用connect 或者WSAConnect連接。

            TCP狀態(tài)

              起初每個(gè)socket都是CLOSED狀態(tài),當(dāng)客戶端初使化一個(gè)連接,他發(fā)送一個(gè)SYN包到服務(wù)器,客戶端進(jìn)入SYN_SENT狀態(tài)。
            服務(wù)器接收到SYN包,反饋一個(gè)SYN-ACK包,客戶端接收后返饋一個(gè)ACK包客戶端變成ESTABLISHED狀態(tài),如果長時(shí)間沒收到SYN-ACK包,客戶端超時(shí)進(jìn)入CLOSED狀態(tài)。
              當(dāng)服務(wù)器綁定并監(jiān)聽某一端口時(shí),socket的狀態(tài)是LISTEN,當(dāng)客戶企圖建立連接時(shí),服務(wù)器收到一個(gè)SYN包,并反饋SYN-ACK包。服務(wù)器狀態(tài)變成SYN_RCVD,當(dāng)客戶端發(fā)送一個(gè)ACK包時(shí),服務(wù)器socket變成ESTABLISHED狀態(tài)。

              當(dāng)一個(gè)程序在ESTABLISHED狀態(tài)時(shí)有兩種圖徑關(guān)閉它, 第一是主動關(guān)閉,第二是被動關(guān)閉。如果你要主動關(guān)閉的話,發(fā)送一個(gè)FIN包。當(dāng)你的程序closesocket或者shutdown(標(biāo)記),你的程序發(fā)送一個(gè)FIN包到peer,你的socket變成FIN_WAIT_1狀態(tài)。peer反饋一個(gè)ACK包,你的socket進(jìn)入FIN_WAIT_2狀態(tài)。如果peer也在關(guān)閉連接,那么它將發(fā)送一個(gè)FIN包到你的電腦,你反饋一個(gè)ACK包,并轉(zhuǎn)成TIME_WAIT狀態(tài)。
              TIME_WAIT狀態(tài)又號2MSL等待狀態(tài)。MSL意思是最大段生命周期(Maximum Segment Lifetime)表明一個(gè)包存在于網(wǎng)絡(luò)上到被丟棄之間的時(shí)間。每個(gè)IP包有一個(gè)TTL(time_to_live),當(dāng)它減到0時(shí)則包被丟棄。每個(gè)路由器使TTL減一并且傳送該包。當(dāng)一個(gè)程序進(jìn)入TIME_WAIT狀態(tài)時(shí),他有2個(gè)MSL的時(shí)間,這個(gè)充許TCP重發(fā)最后的ACK,萬一最后的ACK丟失了,使得FIN被重新傳輸。在2MSL等待狀態(tài)完成后,socket進(jìn)入CLOSED狀態(tài)。
              被動關(guān)閉:當(dāng)程序收到一個(gè)FIN包從peer,并反饋一個(gè)ACK包,于是程序的socket轉(zhuǎn)入CLOSE_WAIT狀態(tài)。因?yàn)閜eer已經(jīng)關(guān)閉了,所以不能發(fā)任何消息了。但程序還可以。要關(guān)閉連接,程序自已發(fā)送給自已FIN,使程序的TCP socket狀態(tài)變成LAST_ACK狀態(tài),當(dāng)程序從peer收到ACK包時(shí),程序進(jìn)入CLOSED狀態(tài)。

            connect

            int connect( SOCKET s, const struct sockaddr FAR* name, int namelen );
            第二個(gè)參數(shù)是你要連接的名字
            第三個(gè)參數(shù)是你加接的名字參數(shù)的長度

            如果連接失敗了則返饋WSAECONNREFUSED錯(cuò)誤。

            send和WSASend

            int send( SOCKET s, const char FAR * buf, int len, int flags );
            第二個(gè)參數(shù)是要發(fā)送的數(shù)據(jù)。
            第三個(gè)參數(shù)是發(fā)送數(shù)據(jù)的長度。
            第四個(gè)參數(shù)可以是0,MSG_DONTROUTE或者是MSG_OOB,這幾個(gè)參數(shù)之間能用or連接。

            正常返回:發(fā)送的字節(jié)。

            int WSASend(
            SOCKET s,
            LPWSABUF lpBuffers,
            DWORD dwBufferCount,
            LPDWORD lpNumberOfBytesSent,
            DWORD dwFlags,
            LPWSAOVERLAPPED lpOverlapped,
            LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine
            );

            最后兩個(gè)參數(shù)用于重疊I/O,重疊I/O是一個(gè)種異步I/O模型。

            WSASendDisconnect

            int WSASendDisconnect ( SOCKET s, LPWSABUF lpOutboundDisconnectData );

             

            91久久成人免费| 婷婷久久综合| 99久久国产免费福利| 国产精品日韩深夜福利久久| 伊人色综合久久天天网| 国产精品免费福利久久| 青青草原综合久久大伊人导航| 精品久久久久久久国产潘金莲| 91精品国产高清91久久久久久| 中文字幕亚洲综合久久菠萝蜜| 精品久久香蕉国产线看观看亚洲| 久久久精品无码专区不卡| 精品蜜臀久久久久99网站| 久久播电影网| 91精品国产综合久久香蕉| 99久久精品国产高清一区二区 | 久久婷婷五月综合97色| 久久久久无码专区亚洲av| 69SEX久久精品国产麻豆| 久久99久久99精品免视看动漫| 久久国产午夜精品一区二区三区| 99精品国产在热久久无毒不卡| 狠狠综合久久AV一区二区三区| 午夜精品久久久内射近拍高清| 久久久精品久久久久久| 久久国产精品偷99| 狠狠人妻久久久久久综合蜜桃| 久久久精品一区二区三区| 精品国产福利久久久| 99热成人精品热久久669| 韩国三级大全久久网站| 国内精品久久久久久野外| A狠狠久久蜜臀婷色中文网| 久久超乳爆乳中文字幕| 2021久久国自产拍精品| 一本久久a久久精品综合夜夜| 久久99毛片免费观看不卡| 久久噜噜电影你懂的| 久久国产精品免费一区| 伊人久久五月天| 久久亚洲国产成人精品性色|