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

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

一丶了解的知识

  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("对不起初始化失败\r\n");
        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 失败\r\n");
      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("对不起初始化失败\r\n");
        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 失败\r\n");
        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 失败\r\n");
      goto OPT;
  }

  //3监听套接字的连接
  nRet = listen(hServer, 10); //参数1.socket 参数2. 同时监听处理的socket的数量

  if (SOCKET_ERROR == nRet)
  {
      printf("listen 失败\r\n");
      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 \r\n", 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("对不起初始化失败\r\n");
        system("pause");
    }

   


    //1.创建
    SOCKET hClient;
    hClient = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (INVALID_SOCKET == hClient)
    {
        printf("socket 失败\r\n");
        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

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

扫码关注云+社区

领取腾讯云代金券