一、OSI七层模型

  OSI定义了网络互连的七层框架(物理层、数据链路层、网络层、传输层、会话层、表示层、应用层),即ISO开放互连系统参考模型。如下图所示。

  每一层实现各自的功能和协议,并完成与相邻层的接口通信。OSI的服务定义详细说明了各层所提供的服务。某一层的服务就是该层及其下各层的一种能力,它通过接口提供给

更高一层。各层所提供的服务与这些服务是怎么实现的无关。

 

 

 

 

  每层常见的物理设备如下:

  虽然OSI模型是一种接近完美的理论(注意这种模型只出现在教课书里)这种模型是在TCP/IP协议已经成熟之后提出的,可以理解为升级版。但是并没有流行出来。所以,网

络数据传输是TCP/IP的天下。

二、TCP/IP五层协议

  每一层都呼叫它的下一层提供的网络来完成自己的需求。(如果是四层模型数据链路层和物理层在一层)

  1、物理层:负责光电信号传递方式。集线器工作在物理层。以太网协议。

  2、数据链路层:负责设备之间的数据帧的传输和识别。交换机工作在数据链路层。例如网卡设备的驱动,帧同步,冲突检测,数据差错校验等工作。

  3、网络层:负责地址管理和路由选择。路由器工作在网络层。

  4、传输层:负责两台主机之间的数据传输。

  5、应用层:负责应用程序之间的沟通。网络编程主要针对的就是应用层。

  关系如下:

 

  ARP协议可实现通过IP地址获得对应主机的物理地址(MAC地址)。

  RARP协议是将MAC物理地址转换成IP地址

  ICMP协议确认IP包是否成功到达目标地址以及通知在发送过程中IP包被丢弃的原因。

  IGMP协议用来在IP 主机和与其直接相邻的组播路由器之间建立、维护组播组成员关系。

三、什么是socket

 

  Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口。在设计模式中,Socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socket接口后面,

对用户来说,一组简单的接口就是全部,让Socket去组织数据,以符合指定的协议。 

  所以,我们无需深入理解TCP/UDP协议,socket已经为我们封装好了,我们只需要遵循socket的规定去编程,写出的程序自然就是遵循TCP/UDP标准的。

 

  另一方面,我们可以抽象地将socket说成ip+port,ip是用来标识互联网中的一台主机的位置,而port是用来标识这台机器上的一个应用程序,ip地址是配置到网卡上的,而port

是应用程序开启的,ip与port的绑定就标识了互联网中独一无二的一个应用程序。

四、socket工作流程

  生活中的场景,你要打电话给一个朋友,先拨号,朋友听到电话铃声后提起电话,这时你和你的朋友就建立起了连接,就可以讲话了。等交流结束,挂断电话结束此次交谈。

 

 

五、socket服务器端(server)

//ssocket.h
#ifndef SSOCKET_H
#define SSOCKET_H

#include <iostream>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>

#define port 7788
#define backlog 5
namespace ssocket { class clientSocket; class newSocket { private: int fd; sockaddr_in mAddr; public: newSocket(); void bind(); void listen(); void accept(clientSocket &client); ~newSocket(); }; class clientSocket { public: int fd; //套接字 socklen_t mLen; //地址长度 sockaddr_in mAddr; //地址 ~clientSocket(); }; } // namespace ssocket #endif

  AF_INET是一个地址系列,用于指定套接字可以与之通信的地址类型(在本例中为Ipv4地址)。创建套接字时,必须指定其地址族,然后只能使用该类型的地址与套

接字。

  套接字的特征在于它们的域,类型和传输协议。常见域名是:

  AF_UNIX:地址格式为UNIX路径名

  AF_INET:地址格式为主机和端口号

ssocket::newSocket::newSocket()
{
    bzero(&mAddr, sizeof(mAddr));
    mAddr.sin_family = AF_INET;
    mAddr.sin_port = htons(port);
    //INADDR_ANY含义是让服务器端计算机上的所有网卡的IP地址都可以作为服务器IP地址
    //也即监听外部客户端程序发送到服务器端所有网卡的网络请求。
    mAddr.sin_addr.s_addr = INADDR_ANY;
    this->fd = socket(AF_INET, SOCK_STREAM, 0);
    int val = 1;
    //closesocket(一般不会立即关闭而经历TIME_WAIT的过程)后想继续重用该socket
    int ret = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR,
                         (void *)&val, sizeof(int));
    if (ret < 0)
    {
        perror("setsocketopt");
        exit(-1);
    }
    if (this->fd < 0)
    {
        perror("socket");
        exit(-1);
    }
    std::cout << "成功创建socket:" << this->fd << std::endl;
}

  sockaddr在头文件#include <sys/socket.h>中定义,sockaddr的缺陷是:sa_data把目标地址和端口信息混在一起了,如下:

  struct sockaddr {      

     sa_family_t sin_family;  //地址族     

    char sa_data[14];     //14字节,包含套接字中的目标地址和端口信息                  

  }; 

  sockaddr_in在头文件#include<netinet/in.h>或#include <arpa/inet.h>中定义,该结构体解决了sockaddr的缺陷,把port和addr 分开储存在两个变量中,如下: 

 

void ssocket::newSocket::bind()
{
    std::cout << "bind" << std::endl;
    int ret = ::bind(this->fd, (struct sockaddr *)&mAddr, sizeof(mAddr));
    if (ret < 0)
    {
        perror("bind");
        exit(-1);
    }
}

   其中,backlog参数决定了未完成队列和已完成队列中连接数目之和的最大值

void ssocket::newSocket::listen()
{
    printf("listen\n");
    int ret = ::listen(this->fd, backlog);
    if (ret < 0)
    {
        perror("listen");
        exit(-1);
    }
}

  其中,client.mAddr和client.mLen是输出参数。

void ssocket::newSocket::accept(clientSocket &client)
{
    client.fd = ::accept(this->fd, (struct sockaddr *)&client.mAddr, &client.mLen);
    if (client.fd < 0)
    {
        perror("accept");
        exit(-1);
    }
}

  close为释放套接字。

ssocket::newSocket::~newSocket()
{
    close(this->fd);
}

ssocket::clientSocket::~clientSocket()
{
    close(this->fd);
}

   

版权声明:本文为ustcwei原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://www.cnblogs.com/ustcwei/p/14044493.html