计算机网络基本知识

Windows Sockets的实现

套接字类型

  • 流式套接字/SOCK_STREAM: TCP
  • 数据报文套接字/SOCK_DGRAM: UDP
  • 原始套接字/SOCK_RAW

基于TCP(面向连接)的socket编程

服务器端

  • 创建套接字: socket
  • 套接字绑定到一个本地地址和端口上(bind)
  • 接字设为监听模式,准备接收客户请求(listen)
  • 等待客户请求到来(accept); 当请求到来后,接受连接请求,返回一个新的对应于此次连接的套接字。
  • 用返回的套接字和客户端进行通信(send/recv)。
  • 返回,等待另一客户请求。
  • 闭套接字。

客户端

  • 创建套接字
  • 向服务器发出连接请求(connect)。
  • 服务器端进行通信(send/recv)。
  • 关闭套接字。

服务器端,当调用 accept 函数时,程序就会等待,等待客户端调用 connect函数发出连接请求,然后服务器端接受该请求,于是双方就建立了连接。之后,服务器端和客户端就可以利用 send和 recv函数进行通信了

基于UDP(面向无连接)的socket编程

相关函数

  • WSAStartup

    1. int WSAStartup(
    2. WORD wVersionRequired,
    3. [out] LPWSADATA lpWSAData
    4. );
  • SOCKET

    SOCKET WSAAPI socket(
    [in] int af,
    [in] int type,
    [in] int protocol
    );
    
  • bind

    int WSAAPI bind(
    [in] SOCKET         s,
    [in] const sockaddr *name,
    [in] int            namelen
    );
    
  • inet_addrinet_ntoa: 以将 IP地址指定为 INADDR_ANY,允许套接字向任何分配给本地机器的 IP地址发送或接收数据 ```cpp unsigned long WSAAPI inet_addr( const char *cp );

char *WSAAPI inet_ntoa( in_addr in );


- [inet_pton ](https://docs.microsoft.com/en-us/windows/win32/api/ws2tcpip/nf-ws2tcpip-inet_pton)和 [inet_ntop](https://docs.microsoft.com/en-us/windows/win32/api/ws2tcpip/nf-ws2tcpip-inet_ntop): 两个函数是为了适应 IPv6 而出现的,用于将 Internet 网络地址在"标准文本表示形式"和"数字二进制形式"之间进行转换,对于IPv4 地址和 IPv6 地址都适用,函数中p和 n 分别代表表达(presentation)和数值(numeric)
```cpp
PCSTR WSAAPI inet_ntop(
  [in]  INT        Family,
  [in]  const VOID *pAddr,
  [out] PSTR       pStringBuf,
  [in]  size_t     StringBufSize
);

char *WSAAPI inet_ntoa(
  in_addr in
);
  • listen:

    int WSAAPI listen(
    [in] SOCKET s,
    [in] int    backlog
    );
    
  • accept:

    SOCKET WSAAPI accept(
    [in]      SOCKET   s,
    [out]     sockaddr *addr,
    [in, out] int      *addrlen
    );
    
  • send

    int WSAAPI send(
    [in] SOCKET     s,
    [in] const char *buf,
    [in] int        len,
    [in] int        flags
    );
    
  • recv

    int WSAAPI recv(
    [in]  SOCKET s,
    [out] char   *buf,
    [in]  int    len,
    [in]  int    flags
    );
    
  • connect

    int WSAAPI connect(
    [in] SOCKET         s,
    [in] const sockaddr *name,
    [in] int            namelen
    );
    
  • recvfrom

    int WSAAPI recvfrom(
    [in]                SOCKET   s,
    [out]               char     *buf,
    [in]                int      len,
    [in]                int      flags,
    [out]               sockaddr *from,
    [in, out, optional] int      *fromlen
    );
    
  • sendto

    int WSAAPI sendto(
    [in] SOCKET         s,
    [in] const char     *buf,
    [in] int            len,
    [in] int            flags,
    [in] const sockaddr *to,
    [in] int            tolen
    );
    
  • htonshtonl ```cpp u_short WSAAPI htons( [in] u_short hostshort );

u_long WSAAPI htonl( [in] u_long hostlong );

<a name="NtRDz"></a>
# 基于TCP的网络应用程序的编写

<a name="YKScE"></a>
## 服务器端
```cpp
#include <Winsock2.h>
#include <iostream>

#include <Ws2tcpip.h>
#pragma comment(lib,"Ws2_32.lib")

int main() {
    //加载套接字库
    WORD wVersionRequested;
    WSADATA wsaData;
    int err;

    wVersionRequested = MAKEWORD(1, 1);

    err = WSAStartup(wVersionRequested, &wsaData);
    if (err != 0) {
        return -1;
    }

    if (LOBYTE(wsaData.wVersion) != 1 ||
        HIBYTE(wsaData.wVersion) != 1) {
        WSACleanup();
        return -1;
    }

    //创建用于监听的套接字
    SOCKET sockSrv = socket(AF_INET, SOCK_STREAM, 0);

    SOCKADDR_IN addrSrv;
    addrSrv.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
    addrSrv.sin_family = AF_INET;
    addrSrv.sin_port = htons(6000);

    //绑定套接字
    bind(sockSrv, (SOCKADDR*)&addrSrv, sizeof(SOCKADDR));

    //将套接字设为监听模式,准备接收客户请求
    listen(sockSrv, 5);

    SOCKADDR_IN addrClient;
    int len = sizeof(SOCKADDR);

    while (1)
    {
        //等待客户请求到来
        SOCKET sockConn = accept(sockSrv, (SOCKADDR*)&addrClient, &len);
        char sendBuf[100];
        char str[INET_ADDRSTRLEN];
        sprintf_s(sendBuf, 100, "Welcome %s to http://www.phei.com.cn",
            inet_ntop(AF_INET, &addrClient.sin_addr, str, sizeof(str)));

        //发送数据
        send(sockConn, sendBuf, strlen(sendBuf) + 1, 0);
        char recvBuf[100];

        //接收数据
        recv(sockConn, recvBuf, 100, 0);

        //打印接收的数据
        printf("%s\n", recvBuf);

        //关闭套接字
        closesocket(sockConn);
    }

    return 0;
}

客户端

#include <iostream>
#include <Winsock2.h>
#include <Ws2tcpip.h>

#pragma comment(lib,"Ws2_32.lib")

int main()
{
    //加载套接字库
    WORD wVersionRequested;
    WSADATA wsaData;
    int err;

    wVersionRequested = MAKEWORD(1, 1);

    err = WSAStartup(wVersionRequested, &wsaData);
    if (err != 0) {
        return -1;
    }


    if (LOBYTE(wsaData.wVersion) != 1 ||
        HIBYTE(wsaData.wVersion) != 1) {
        WSACleanup();
        return -1;
    }

    //创建套接字
    SOCKET sockClient = socket(AF_INET, SOCK_STREAM, 0);

    SOCKADDR_IN addrSrv;
    inet_pton(AF_INET, "127.0.0.1", &addrSrv.sin_addr);
    addrSrv.sin_family = AF_INET;
    addrSrv.sin_port = htons(6000);

    //向服务器发出连接请求
    connect(sockClient, (SOCKADDR*)&addrSrv, sizeof(SOCKADDR));

    //接收数据
    char recvBuf[100];
    recv(sockClient, recvBuf, 100, 0);
    printf("%s\n", recvBuf);

    //发送数据
    send(sockClient, "This is 李四", strlen("This is 李四") + 1, 0);

    //关闭套接字
    closesocket(sockClient);
    WSACleanup();

    system("PAUSE ");
    return 0;
}

基于UDP的网络应用程序的编写

基于UDP的简单聊天程序