• 网络编程基础第二讲.网络编程框架


            网络编程基础第二讲.网络编程框架

    一丶了解的知识

      1.什么是socket

         socket 是开发接口.是TCP/IP网络环境下.应用程序与驱动程序之间访问的接口.

      2.服务跟类型

        socket服务 分为面向连接跟无连接,代表的协议就是TCP/IP

        socket类型: 有三种类型

          SOCK_STREAM  流式套接字. 可靠的套接字.可以处理大量数据.不会丢包.但是开销比大. 代表就是TCP协议. 是在传输层上做的.

          SOCK_DGRAM  数据报套接字.不可靠.允许丢包. 常用与音频.视频等等. 代表就是UDP协议.

          SOCK_RAW  原始套接字. 是在网络层进行编程的.也就是对底层的IP可以进行编程.不过常用的就是前边两种.

      3.构建Windows框架.

      4.IP地址的表现形式.

      5.编写一个简单的网络程序. TCP模型.

    二丶构建Windows框架

      在windows下使用socket需要使用windows初始化函数.还要包含库文件.

    #include <WinSock2.h>
    #pragma comment(lib,"ws2_32.lib")

    以及使用API进行初始化

     WSADATA data;
     WSAStartup(MAKEWORD(2, 2), &data);
    
    WSAStartup(版本号,执行成功会返回一个WSADATA结构)

    版本号使用MAKEWORD即可.我们现在使用的是2版本. 其实就是 高位低位都为2  0x0202这个值.

    返回值:

        0成功

        1失败;

    完整代码:

      

    #include "stdafx.h"
    #include <stdio.h>
    #include <stdlib.h>
    
    #include <WinSock2.h>
    #pragma comment(lib,"ws2_32.lib")
    
    int main()
    {
        WSADATA data;
        if (WSAStartup(MAKEWORD(2, 2), &data))
        {
            puts("对不起初始化失败
    ");
            system("pause");
        }
        getchar();
    
        return 0;
    }

    三丶IP地址表现形式

     IP地址常用点分法来表示  也就是用点隔开. 用4个 0到255之间的整数来表示

    例如:

      192.168.0.0.1

    但是在计算机中.不使用 点分法来保存IP地址. 这样会浪费存储空间.而且.不便计算 子网掩码.

    我们知道  子网掩码 跟IP地址是and的关系. 所以不使用这个.

    1.网络字节序

      在网络传送中,IP地址会保存为32位的二进制数.

      低位存储地址中保存数据的高位字节.高位中存储低位字节. 也就是我们常说的大端模式.

    小端模式:高地址存储高位,低地址存储低位

      0x12345678  地址放高位. 高地址放低位 例如:  78 56 34 12    我们的高地址存储了12 依次类推.

    大端模式:

        高地址存放低位, 低地址存放高位.

     0x 12345678  在内存中表现形式  12 34 56 78  

    这个就是大端模式.

    VC中 in_addr 来保存IP地址. 对应的转化则是 inet_addr 跟 inet_ntoa

    示例代码如下:

     in_addr addr;
    
     addr.S_un.S_addr = inet_addr("127.0.0.1");  //保存我们的IP地址.需要进行转换.
    typedef struct in_addr {
            union {
                    struct { UCHAR s_b1,s_b2,s_b3,s_b4; } S_un_b;  s_b1 ,s_b2 就是保存着我们的IP地址.点分法表示.
                    struct { USHORT s_w1,s_w2; } S_un_w;
                    ULONG S_addr;
            } S_un;

    转换回来:

      使用 inet_ntoa(addr)即可.

     char *pszIp = NULL;
       pszIp = inet_ntoa(addr);

    代码调试截图:

      

    2.主机字节顺序

      什么是主机字节顺序.主机字节顺序就是指不同的主机在堆IP地址进行存储的时候.使用的格式不同.

    所以需要通过函数进行转换.

      htonl() 将主机字节顺序格式的IP地址转化成为TCP/IP网络字节顺序

      htons 主机转网络.

      ntohl  网络转主机

      ntohs 网络转主机.

    h 主机的意思 to 转化的意思 n 网络的意思 network l 就是 ulong

    所以根据缩写就能明白什么意思.

    四丶客户端服务端编写流程以及代码

    如下图可以清楚看到顺序

     有点复杂的就是服务端(主机).他要绑定本地.并且接受连接.也就是说客户端只要连接.就会有一个Socket

    我们对这个socket操作.就能实现主机跟客户端的通信了.

    当有多个客户端连接的时候.我们就要保存socket了. 这个以后会讲到.也就是可能会接触到各种各样的模型.

    来进行处理.

    需要用到的函数解析

    1.socket 创建socket

    socket (协议家族,套接字类型,连接类型)
    示例代码:
        socket(AF_INET,SOCK_STREAM,IPPROTO_TCP)
    返回一个SOCKET值. windows中定义的是 UINT
    失败: INVALID_SOCKET


    2.绑定

       sockaddr_in addrserver;
        addrserver.sin_family = AF_INET;
        addrserver.sin_port = htons(8564);//端口必须转换
        addrserver.sin_addr.S_un.S_addr = htonl(INADDR_ANY);//给定IP.如果是这个宏.则可以任何主机都可以连接
    
        bind(hServer, (sockaddr *)&addrserver, sizeof(addrserver));

    绑定的时候.我们要指明绑定的IP.以及绑定的端口.

    此时我们会使用一个 sockaddr_in 的结构体.socket中其实真正的结构体是 sockaddr 但是操作不方便.

    所以给了一个sockaddr_in的结构体. 大小跟 sockaddr结构体是一样的.

    因为使用的是网络字节序. 所以 我们的端口以及IP地址都需要进行转换.

    IP地址是 32位的.所以使用 htonl

    端口是2个字节.也就是16为. 所以使用 htons

    3.监听套接字

     nRet = listen(hServer, 10); //参数1.socket 参数2. 同时监听处理的socket的数量
    
      if (SOCKET_ERROR == nRet)
      {
          printf("listen 失败
    ");
          closesocket(hServer);
          WSACleanup();
          goto OPT;
      }

    监听就很简单了.我们要指明同时接受的socket以及同时处理的最大socket.所以给定socket 然后给一个数量即可.

    4.获取用户的连接请求

      获取用户的连接请求主要是用accept关键字. 给一个socket句柄.然后会把客户端连接这个socket的一系列信息都会记录在结构体中.

    并且返回客户端的socket.我们只要对这个socket操作.就能进行操作了.

     SOCKET hClient;
      
      sockaddr_in addrClient;   //保存客户端的addr属性.客户端的IP以及端口都会放在里面
      int nLen = sizeof(addrClient);
      hClient = accept(hServer, (sockaddr *)&addrClient, &nLen);传出结构.以及长度.返回客户端socket

    5.对客户端socket进行读取或者写入.

      读取的 API recv

      写入的API send

    相应的如果是UDP连接则使用 recvfrom /sendto

     char pszBuffer[1024] = { NULL };
      recv(hClient, pszBuffer, sizeof(char) * 1024, 0);
    要对客户端的socket进行读写.所以传入的是客户端的socket

    send是一样的.

    最后进行关闭套接字即可.

    服务端完整代码:

      

    // socket.cpp : 定义控制台应用程序的入口点。
    //
    
    #include "stdafx.h"
    #include <stdio.h>
    #include <stdlib.h>
    
    #include <WinSock2.h>
    #pragma comment(lib,"ws2_32.lib")
    
    int main()
    {
        WSADATA data;
        if (WSAStartup(MAKEWORD(2, 2), &data))
        {
            puts("对不起初始化失败
    ");
            system("pause");
        }
    
        in_addr addr;
    
        addr.S_un.S_addr = inet_addr("127.0.0.1");
        char *pszIp = NULL;
        pszIp = inet_ntoa(addr);
       
    
        SOCKET hServer;
        hServer = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
        if (INVALID_SOCKET == hServer)
        {
            printf("socket 失败
    ");
            goto OPT;
        }
    
        //2.绑定在本地
        sockaddr_in addrserver;
        addrserver.sin_family = AF_INET;
        addrserver.sin_port = htons(8564);//端口必须转换
        addrserver.sin_addr.S_un.S_addr = htonl(INADDR_ANY);//给定IP.如果是这个宏.则可以任何主机都可以连接
    
      int nRet =   bind(hServer, (sockaddr *)&addrserver, sizeof(addrserver));
      if (SOCKET_ERROR == nRet)
      {
          printf("bind 失败
    ");
          goto OPT;
      }
    
      //3监听套接字的连接
      nRet = listen(hServer, 10); //参数1.socket 参数2. 同时监听处理的socket的数量
    
      if (SOCKET_ERROR == nRet)
      {
          printf("listen 失败
    ");
          closesocket(hServer);
          WSACleanup();
          goto OPT;
      }
    
      //4.获取用户的数据请求.也就是接受客户端的连接的socket
      SOCKET hClient;
      
      sockaddr_in addrClient;   //保存客户端的addr属性.客户端的IP以及端口都会放在里面
      int nLen = sizeof(addrClient);
      hClient = accept(hServer, (sockaddr *)&addrClient, &nLen);
    
      //5.接受用户发送的数据
      char pszBuffer[100] = { NULL };
      recv(hClient, pszBuffer, sizeof(char) * 100, 0); //阻塞读取.不读取不返回
      printf("接受到的数据 = %s 
    ", pszBuffer);
    
    OPT:
      closesocket(hClient);  //释放一切资源.
      closesocket(hServer);
      WSACleanup();
        getchar();
        return 0;
    }

    客户端代码就很简单了.

    需要用到一个 connect API. 也就是连接的API. 调用这个API则跟服务端进行连接. 服务端的accept就会返回这个socket.

    完整代码.

    // socket.cpp : 定义控制台应用程序的入口点。
    //
    
    #include "stdafx.h"
    #include <stdio.h>
    #include <stdlib.h>
    
    #include <WinSock2.h>
    #pragma comment(lib,"ws2_32.lib")
    
    int main()
    {
        WSADATA data;
        if (WSAStartup(MAKEWORD(2, 2), &data))
        {
            puts("对不起初始化失败
    ");
            system("pause");
        }
    
       
    
    
        //1.创建
        SOCKET hClient;
        hClient = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
        if (INVALID_SOCKET == hClient)
        {
            printf("socket 失败
    ");
            goto OPT;
        }
    
      
        //2.连接
        sockaddr_in addr;
        addr.sin_family = AF_INET;
        addr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");    //指定IP跟端口才能连接
        addr.sin_port = htons(8564);
        connect(hClient, (sockaddr *)&addr, sizeof(addr));//指明服务端信息.才能进行通讯.
    
        //3.收发数据.
        char pszBuffer[100] = "HelloWorld";
        send(hClient, pszBuffer, sizeof(char) * 100,0);
    
    OPT:
        closesocket(hClient);  //释放一切资源.
        WSACleanup();
        getchar();
        return 0;
    }

    应用实现截图:

      


    课堂代码:
      链接:https://pan.baidu.com/s/1iKX4cvFdQ9yAnZaIydrQIg 密码:9d0t
  • 相关阅读:
    微服务架构有哪些优势?
    Java 线程数过多会造成什么异常?
    Java 死锁以及如何避免?
    抽象的(abstract)方法是否可同时是静态的(static), 是否可同时是本地方法(native),是否可同时被 synchronized 修饰?
    内部类可以引用它的包含类(外部类)的成员吗?有没有 什么限制?
    CSS选取第几个标签元素:nth-child、first-child、last-child
    数据库约束
    DQL查询语句
    网络编程(客户端,服务端文件上传下载)
    缓冲流,转换流
  • 原文地址:https://www.cnblogs.com/iBinary/p/9671724.html
Copyright © 2020-2023  润新知