日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

WinSock API网络编程——TCP/IP协议详解

發布時間:2023/12/18 编程问答 23 豆豆
生活随笔 收集整理的這篇文章主要介紹了 WinSock API网络编程——TCP/IP协议详解 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.


WinSock API網絡編程——TCP/IP協議(http://www.impcas.ac.cn/usr/lujun/browse.asp?id=winsock_tcp)

???????????


    WinSock API網絡編程——TCP/IP協議
    作者:陸軍 Email:ldlujun@163.com 時間:2004-08-28

    80年代初,美國政府的高級研究工程機構(ARPA)給加利福尼亞大學Berkeley分校提供了資金,為實現UNIX操作系統下的TCP/IP協議而開發了一個API(Application Programming Interface),稱為Socket接口(套接字)。Socket接口是TCP/IP網絡最為通用的API。

    90年代初,由Microsoft聯合了其他幾家公司共同制定了一套Windows下的網絡編程接口,即Windows Sockets規范。Windows Sockets規范是一套開放的、支持多種協議的Windows網絡編程接口,并已成為Windows網絡編程的事實上的標準。目前,在實際應用中的Windows Sockets規范主要有1.1版和2.0版。2.0版可以支持多協議,有良好的向后兼容性,任何使用1.1版的源代碼,二進制文件,應用程序都可以不加修改在2.0規范下使用。

    Socket實際上在計算機中提供了一個通信端口,可以通過這個端口與任何一個具有Socket接口的計算機通信。應用程序在網絡上傳輸/接收的信息都通過這個Socket接口來實現的。在應用開發中可以像使用文件句柄一樣來對Socket句柄進行讀/寫操作。目前可以使用兩種套接口,即流式套接字(SOCK_STREAM)和數據報套接字(SOCK_DGRAM)。流式套接字提供了一個面向連接的、可靠的、數據無錯的、無重復發送的及按發送順序接收數據的服務;數據報套接字提供不可靠的、無連接的數據報傳輸服務。

    套接字可分為阻塞套接字和非阻塞套接字。阻塞套接字是指執行此套接字的網絡調用時,直到成功才返回,否則一直阻塞在此網絡調用上;而非阻塞套接字是指執行此套接字的網絡調用時,不管是否執行成功,都立即返回。實際上非阻塞套接字是用得最多的。

    C/S模型,即客戶機/服務器模型,是一種非對稱式編程模式。對于這種模式而言,其中一部分需要作為服務端,用來響應并為客戶提供固定的服務;另一部分則作為客戶端用來向服務端提出請求或要求某種服務。在實際應用中,程序可以同時包含客戶端和服務端。

    Microsoft Visual C++提供了十分完整的Windows Sockets庫函數,并且對這些庫函數進行了一系列封裝,繼而產生了CAsynSocket、CSocket、CSocketFile等類,它們封裝著有關Socket的各種功能,使網絡編程變得更加簡單。但是為了更好理解Winsock的通信原理,這里將介紹怎樣使用底層的API函數來實現網絡通訊。

    面向連接協議的通信過程如下:服務端和客戶端都必須建立通信套接字,而服務端套接字應先進入監聽狀態,然后客戶端套接字發出連接請求,服務端套接字收到連接請求后,建立一個新套接字與客戶端套接字進行通信,原來負責監聽的套接字仍進行監聽,如果再收到其它客戶端套接字的連接請求,則再建立一個新套接字與之通信。通信完畢后斷開連接,關閉相應套接字。

    (1) 初始化通信端口。可以在程序向導中添加Windows Sockets支持,或者直接添加代碼:

    #include <afxsock.h>
    if (!AfxSocketInit())
    {
    ????AfxMessageBox("Windows 通信端口初始化失敗!");
    }

    (2) 初始化Windows Sockets DLL。目前Winsock有兩個版本,版本號分別為1.1和2.2,對應參數為0x101和0x202。

    WSADATA wsaData;
    if (WSAStartup(MAKEWORD(1,1), &wsaData) != 0)
    {
    ????AfxMessageBox("加載Windows Sockets DLL失敗!");
    ????WSACleanup();
    }

    (3) 創建流式套接字。

    套接字族:
    ?AF_UNIX:UNIX內部協議族
    AF_INET:Iternet協議
    AF_NS:XeroxNs協議
    AF_IMPLINK:IMP鏈接層
    套接字類型:
    ?SOCK_STREAM:流式套接字
    SOCK_DGRAM:數據報套接字
    SOCK_RAW:原始套接字
    SOCK_SEQPACKET:定序分組套接字
    ?

    SOCKET m_Socket;
    m_Socket = INVALID_SOCKET;
    if ((m_Socket = socket(AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET)
    {
    ????AfxMessageBox("創建套接字失敗!");
    }

    (4) 服務端綁定端口。端口號范圍:1024到65535,低于1024的端口對應著因特網上的一些常見服務。

    struct sockaddr {
    ????u_short sa_family;???????????????// 地址族地址族 address family
    ????address family char sa_data[14]; // 14字節的協議地址 up to 14 bytes of direct address
    };
    typedef struct sockaddr SOCKADDR;
    typedef struct sockaddr *PSOCKADDR;
    typedef struct sockaddr FAR *LPSOCKADDR;

    struct sockaddr_in {
    ????short sin_family; ???????// 地址族
    ????u_short sin_port;????????// 端口號
    ????struct in_addr sin_addr; // IP地址
    ????char sin_zero[8]; ???????// 填充0
    };
    typedef struct sockaddr_in SOCKADDR_IN;
    typedef struct sockaddr_in *PSOCKADDR_IN;
    typedef struct sockaddr_in FAR *LPSOCKADDR_IN;

    字節順序轉換函數:
    ????htons():"Host to Network Short"
    ????htonl():"Host to Network long"
    ????ntohs():"Network to Host Short"
    ????ntohl():"Network to Host Long"

    SOCKADDR_IN m_saAddr;
    u_short ????m_nPort = 20048; ???????????????// 端口號
    ZeroMemory(&m_saAddr, sizeof(m_saAddr));
    m_saAddr.sin_family ?????= AF_INET;
    m_saAddr.sin_port ???????= htons(m_nPort); ?// 如果此值為0,系統將隨機選擇一個未被使用的端口號
    m_saAddr.sin_addr.s_addr = INADDR_ANY; ?????// 填入本機IP地址
    if (bind(m_Socket, (LPSOCKADDR) &m_saAddr, sizeof(m_saAddr)) == SOCKET_ERROR)
    {
    ????AfxMessageBox("綁定端口失敗!");
    }

    (5) 服務端監聽端口。

    #define MAX_BACKLOG 5
    if (listen(m_Socket, MAX_BACKLOG) == SOCKET_ERROR)
    {
    ????AfxMessageBox("監聽失敗!");
    }

    (6) 客戶端請求連接。

    DWORD m_dwServerIP;
    char m_sServerIP[] = "127.0.0.1"; // 主機IP地址
    u_short m_nServerPort = 20048; ???// 主機端口號
    if ((m_dwServerIP = inet_addr(m_sServerIP)) == INADDR_NONE)
    {
    ????AfxMessageBox("無法獲取主機IP!");
    ????return;
    }
    m_saAddr.sin_family = AF_INET;
    m_saAddr.sin_port = htons(m_nServerPort);
    m_saAddr.sin_addr.s_addr = m_dwServerIP;
    if (connect(m_Socket, (LPSOCKADDR) &m_saAddr, sizeof(m_saAddr)))
    {
    ????AfxMessageBox("連接服務器失敗!");
    }

    (7) 注冊網絡事件。

    網絡事件定義:
    ?FD_READ:網絡數據包到達
    FD_WRITE:發送網絡數據
    FD_OOB:OOB數據到達
    FD_ACCEPT:收到連接請求
    FD_CONNECT:已建立連接
    FD_CLOSE:斷開連接
    FD_QOS:服務質量(QoS)發生變化
    FD_GROUP_QOS:保留事件
    FD_ROUTING_INTERFACE_CHANGE:指定地址的路由接口發生變化
    FD_ADDRESS_LIST_CHANGE:本地地址變化
    ?

    #define WM_NETWORK_EVENT WM_USER + 101
    if (WSAAsyncSelect(m_Socket, m_hWnd, WM_NETWORK_EVENT, FD_ACCEPT | FD_READ | FD_CLOSE)
    == SOCKET_ERROR)
    {
    ????AfxMessageBox("注冊網絡事件失敗!");
    }

    (8) 處理網絡事件。

    afx_msg LRESULT OnNetworkEvent(WPARAM wParam, LPARAM lParam);
    ON_MESSAGE(WM_NETWORK_EVEN, OnNetworkEvent)

    LRESULT OnNetworkEvent(WPARAM wParam, LPARAM lParam)
    {
    ????switch (WSAGETSELECTEVENT(lParam))
    ????{
    ????case FD_ACCEPT:
    ????????// 接受連接請求
    ????????break;
    ????case FD_READ:
    ????????// 接收數據
    ????????break;
    ????case FD_CLOSE:
    ????????// 斷開連接
    ????????break;
    ????}
    ????return 0L;
    }

    (9) 服務端接受連接請求。(采用上敘方法處理這里的網絡事件,只是不需要處理FD_ACCEPT事件。)

    #define WM_CLIENT_EVENT WM_USER + 102

    SOCKET m_hClientSocket[MAX_BACKLOG];
    SOCKADDR_IN m_saClientAddr[MAX_BACKLOG];
    for (int i = 0; i < MAX_BACKLOG; i++)
    {
    ????ZeroMemory(&m_saClientAddr[i], sizeof(m_saClientAddr[i]));
    ????m_hClientSocket[i] = INVALID_SOCKET;
    }

    BOOL Accept(void)
    {
    ????CString sClientIP;
    ????int nLength = sizeof(SOCKADDR);
    ????for (int i = 0; i < MAX_BACKLOG; i++)
    ????{
    ????????if (m_hClientSocket[i] == INVALID_SOCKET)
    ????????{
    ????????????m_hClientSocket[i] = socket(PF_INET, SOCK_STREAM, 0);
    ????????????m_hClientSocket[i] = accept(m_Socket, (LPSOCKADDR) &m_saClientAddr[i], (LPINT) &nLength);
    ????????????WSAAsyncSelect(m_hClientSocket[i], m_hWnd, WM_CLIENT_EVENT, FD_READ | FD_CLOSE);
    ????????????// 獲取客戶端IP
    ????????????sClientIP.Format("%d.%d.%d.%d",
    ????????????????m_saClientAddr[i].sin_addr.S_un.S_un_b.s_b1,
    ????????????????m_saClientAddr[i].sin_addr.S_un.S_un_b.s_b2,
    ????????????????m_saClientAddr[i].sin_addr.S_un.S_un_b.s_b3,
    ????????????????m_saClientAddr[i].sin_addr.S_un.S_un_b.s_b4);
    ????????????AfxMessageBox(sClientIP);
    ????????????return TRUE;
    ????????}
    ????}
    ????AfxMessageBox("連接資源不足!");
    ????return FALSE;
    }

    (10) 客戶端斷開連接。

    void ClientClose(WPARAM wParam)
    {
    ????for (int i = 0; i < MAX_BACKLOG; i++)
    ????{
    ????????if (m_ClientSocket[i] == wParam)
    ????????{
    ????????????closesocket(m_hClientSocket[i]);
    ????????????m_hClientSocket[i] = INVALID_SOCKET;
    ????????}
    ????}
    }

    (11) 讀取客戶端數據。

    BOOL ClientRead(WPARAM wParam)
    {
    ????int nBytesRead;
    ????int nBufferLength;
    ????int nEnd;
    ????int nSpaceRemaining;
    ????char chIncomingDataBuffer[4096];
    ????nEnd = 0;
    ????nBufferLength = sizeof(chIncomingDataBuffer);
    ????nSpaceRemaining = sizeof(chIncomingDataBuffer);
    ????nSpaceRemaining -= nEnd;
    ????for (int i = 0; i < MAX_BACKLOG; i++)
    ????{
    ????????if (m_hClientSocket[i] == wParam)
    ????????{
    ????????????nBytesRead = recv(m_hClientSocket[i], (LPSTR) (chIncomingDataBuffer + nEnd), nSpaceRemaining, 0);
    ????????????nEnd += nBytesRead;
    ????????????if (nBytesRead == SOCKET_ERROR)
    ????????????{
    ????????????????AfxMessageBox("讀取數據出錯!")
    ????????????????return FALSE;
    ????????????}
    ????????????chIncomingDataBuffer[nEnd] = '/0';
    ????????????if (lstrlen(chIncomingDataBuffer) != 0)
    ????????????{
    ????????????????AfxMessageBox(chIncomingDataBuffer);
    ????????????}
    ????????}
    ????}
    ????return TRUE;
    }

    (12) 讀取服務端數據。

    BOOL Read(void)
    {
    ????int nBytesRead;
    ????int nBufferLength;
    ????int nEnd;
    ????int nSpaceRemaining;
    ????char chIncomingDataBuffer[4096];
    ????nEnd = 0;
    ????nBufferLength = sizeof(chIncomingDataBuffer);
    ????nSpaceRemaining = sizeof(chIncomingDataBuffer);
    ????nSpaceRemaining -= nEnd;
    ????nBytesRead = recv(m_Socket, (LPSTR) (chIncomingDataBuffer + nEnd), nSpaceRemaining, 0);
    ????nEnd += nBytesRead;
    ??? if (nBytesRead == SOCKET_ERROR)
    ????{
    ????????AfxMessageBox("讀取數據出錯!")
    ????????return FALSE;
    ????}
    ????chIncomingDataBuffer[nEnd] = '/0';
    ????if (lstrlen(chIncomingDataBuffer) != 0)
    ????{
    ????????AfxMessageBox(chIncomingDataBuffer);
    ????}
    ????return TRUE;
    }

    (13) 發送數據。

    BOOL Send(SOCKET Socket, CString sSendData)
    {
    ????if (Socket == INVALID_SOCKET)
    ????{
    ????????AfxMessageBox("套接字不可用!");
    ????????return FALSE;
    ????}
    ????if (send(Socket, sSendData, sSendData.GetLength(), 0) == SOCKET_ERROR)
    ????{
    ????????AfxMessageBox("發送數據失敗!");
    ????????return FALSE;
    ????}
    ????return TRUE;
    }

    (14) 關閉套接字。

    if (m_Socket != INVALID_SOCKET)
    {
    ????closesocket(m_Socket);
    }
    m_Socket = INVALID_SOCKET;
    WSACleanup();

    希望本文能夠對網絡編程的初學者有所幫助。文中沒有把服務端和客戶端徹底分開,有些代碼是它們共有的部分,而有的代碼則專屬于服務端或客戶端,閱讀時請注意相應的文字說明。由于時間匆忙,文中給出的代碼都沒有經過調試,難免有錯誤和不足之處,敬請大家原諒,同時也真心希望您能指出和糾正。

    總結

    以上是生活随笔為你收集整理的WinSock API网络编程——TCP/IP协议详解的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。