网络编程预备知识

1. 网络协议层次结构

  • 分层: layer
  • “层”: 功能模块。软件设计方法学,主张把一个大的复杂的工程分成不同的代码码,实现不同的功能。功能模块(function)
  • OSI七层模型:
    • 应用层
    • 表示层
    • 会话层
    • 传输层
    • 网络层
    • 数据链路层
    • 物理层
  • TCP/IP协议的四层模型
    • 应用层
    • 网络应用程序的私有协议:
    • 传输层(tcp, udp)
  • 传输层,规定数据是怎么如何传输的
  • TCP: Transport Control Protocol传输控制协议是一种面向连接的传输层协议,它能提供高可靠性通信(数据无误、数据无丢失、数据无失序、数据无重复)。
  • 面向连接:发数据之前,先建立连接。“三路握手”
  • 应答机制:接收方在收到数据时,要应答。”保持连接”
  • UDP :User Datagram Protocol用户数据报协议,是不可靠的无连接的协议。
    • “不需要连接”
    • “不需要应答机制”=> 高效率的数据传输
    • 数据有可能会丢失,
    • 数据有可能会有误,
    • 数据有可能会失序,
    • 数据有可能重复到达。
    • “实时网络应用”: 丢了就丢了。,“直播”,”时间戳”:表示每一帧数据的时间
  • 网络层(ip, icmp, igmp)
  • IP协议: ipv4 ipv6
  • 通过给网络上每台主机一个虚拟的id ,用来标识这台主机。
  • 设备驱动和硬件层
    • 网卡,……
    • MAC地址:网卡的物理地址,全世界唯一。唯一标识一块物理网卡。

. 数据的发送和接收过程

  • “封装”
  • “拆包”

. 互联网地址(IP地址)

  • 互联网上的每个接口(网卡)必须有一个唯一的Internet地址(也称作IP地址),协议上的一个逻辑地址。
  • ipv4: 32bits -> unsigned int
  • ipv4地址有32bits, 那么怎么区分呢?:一般把ipv4的地址,分为两个部分。像日常生活中的电话号码一样,分“区号”,“主机号”。同理,ipv4的32bits地址,也分为两部分:网段号: 用来标识某个网段;主机号: 用来标识特定网段中的特定的主机号的。32bits = 网段号占的bits + 主机号占的bits,网段号占左边连续的bits,,主机号占右边连续的bits,那么网段号到底占多少bits呢?由netmask,子网掩码来确定。子网掩码就是用来指定ip地址中,哪些bits为网段号,哪些为主机号。子网掩码中为1的bits为网段号,为0的bits为主机号.如:255.255.255.0即二进制的:11111111 11111111 11111111 00000000 =>高24bits为网段号,低8bits为主机号,您设置的这个网段,最多可以有多少台电脑? 256 - 2
  • IP地址分类:”私有地址”:局域网内的ip,不会在外网中使用。
  • ip地址范围

    • 私有地址的范围

      • A 0网络号(7) 主机号(24) 0.0.0.0 ~ 127.255.255.255
        10.0.0.0 ~ 10.255.255.255
      • B 10网络号(14) 主机号(16) 128.0.0.0 ~ 191.255.255.255
        172.16.0.0 ~ 172.31.255.255

      • C 110网络号(21) 主机号(8) 192.0.0.0 ~ 223.255.255.255
        192.168.0.0 ~ 192.168.255.255

      • D 1110 多播组号(28) 224.0.0.0 ~ 239.255.255.255
        D类地址主要是用来进行多播和组播。

      • E 11110 (留待后用) 240.0.0.0 ~ 247.255.255.255

  • ip地址用来唯一标识网络上一台主机,一台主机上可能有多个网络应用程序,这多个网络应用程序的ip是一样的,那我怎么区分这些网络应用程序呢?

端口号

  • TCP和UDP采用16bits的端口号来识别不同的应用程序。
  • IP地址只是唯一标识网络中的主机,但每台主机上可能跑多个网络应用程序,而且网络应用从传输层来看可以分为: TCP应用,UDP应用,所以为了区分这些应用,故提出端口号的概念。不同的网络应用程序的端口号不同。http -> 80,ftp -> 21,QQ ->
    ….
  • TCP端口号和UDP端口号独立的。=>一台主机上的网络应用由: ip地址 + 传输层(TCP,UDP) + 端口号 确定。
  • IANA(Internet Assigned Numbers Authorithy)管理:
    • 众所周知的端口: 1 ~ 1023
    • 注册端口: 1024 ~ 49151
    • 动态或私有端口: 49152 ~ 65535

字节序

  • 大端模式:存储器的低地址存放寄存器的高字节
  • 小端模式:存储器的低地址存放寄存器的低字节
  • 为什么会有大端和小端区别?寄存器按bits来存放数据的,但是存储器却是按字节来编地址。把寄存器的数据存放到存储器中去,就会涉及到存储器的低地址存放寄存器的低位还是高位的问题。网络上按网络字节序,网络字节序采用大端模式。

socket: 套接字

  • socket是一个编程接口(网络编程接口);
  • socket是一个特殊的文件描述符;文件操作的接口同样适用于socket;像write(),read(),socket接口并不仅限于TCP/IP.
  • socket类型:
    • (1) 流式套接字 (SOCK_STREAM)
      针对传输层为TCP
    • (2) 数据报套接字(SOCK_DGRAM)
      针对传输层为UDP
    • (3) 原始套接字(SOCK_RAW)
      直接跳过传输层

socket编程的流程及API函数

  • (1) TCP流程
    • TCP SERVER:
      • socket : 创建一个套接字
        bind : 为套接字指定一个地址(IP地址+端口号),如果您不调bind,socket也会有一个ip+端口,只不过是内核为您动态分配的。作为TCP server端,必须要绑定一个“众所周知”的端口,好让客户端来连接你。
      • listen : 要套接这字进入一个监听模式,可以接收从客户端发起的连接请求。
      • accept: 接收从客户端发起的连接请求
      • 发送数据:
        write/send/sendto
      • 接收数据:
        read/recv/recvfrom
      • 关闭连接:close(),shutdown()
    • TCP CLIENT:
      • socket: 创建一个套接字您不主动调用bind,您的socket也会有一个地址(ip+端口号)这个地址是内核动态分配给您的。只要是socket就一定会有一个地址!
      • connect: 连接服务器
      • 发送数据:
        write/send/sendto
      • 接收数据:
        read/recv/recvfrom
      • 关闭连接:close(),shutdown()
  • (2) UDP流程:

(3)socket API函数

    1. socket: 创建一个套接字
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>

int socket(int domain, int type, int protocol);
  • domain: 协议族。
    - TCP/IP协议族: 主要是针对以太网的协议
    - bluetooth协议族:
    - …
    - socket编程接口不仅支持TCP/IP,还支持像蓝牙,wifi,2.4G, UNIX域协议。domain指定协议族。
    - AF_INET: IPV4协议族
    - AF_INET6: IPV6
    - AF_UNIX/AF_LOCAL :UNIX域协议。
    - …
    - type: 指定套接字的类型:
    - SOCK_STREAM:流式套接字,主要 针对传输层协议为TCP
    - SOCK_DGRAM: 数据报套接字,主要针对传输层协议为UDP
    - SOCK_RAW: 原始套接字。
    - protocol: 指定应用层协议,可以为0
    - 网络应用处于 “应用层”:应用层也有自己的“私有协议”,ftp,QQ协议,SB250,…
    - 返回值:成功返回一个套接字描述符(>0,用文件描述符实现的),失败返回一个-1,同时 errno被设置。
      1. socket地址结构,通用地址结构: linux/socket.h
struct sockaddr
{
    sa_family_t sa_family; //协议族
    char sa_data[14];
};
  • 以太网ipv4地址结构
struct sockaddr_in
{
    sa_family_t sin_family; //协议族, AF_INET
    u_int16_t   sin_port; //端口号, 
    struct in_addr sin_addr; //ipv4地址
    char sin_zero[8] ;// padding, 填充用的,为了保持和其他
                        //协议族地址结构体大小一致!
};

struct in_addr
{
    in_addr_t  s_addr; //ipv4地址是unsigned 32bits number
};
typedef u_int32_t in_addr_t;
  • ip地址的转换函数:
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
int inet_aton(const char *cp, struct in_addr *inp);
in_addr_t inet_addr(const char *cp);
in_addr_t inet_network(const char *cp);
char *inet_ntoa(struct in_addr in);
struct in_addr inet_makeaddr(int net, int host);
in_addr_t inet_lnaof(struct in_addr in);
in_addr_t inet_netof(struct in_addr in);
  • inet_aton: 把cp指定的“点分式的ip字符串”转换成网络地址 struct in_addr
    • a: 点分式ip字符串
    • n: network网络地址
  • int inet_aton(const char *cp, struct in_addr *inp);
    • inet_addr把cp指定的“点分式ip字符串”,转换成网络地址 in_addr_t(uint32),通过返回值返回。
    • in_addr_t inet_addr(const char *cp);
    • inet_network与inet_addr一样。
    • in_addr_t inet_network(const char *cp);
    • inet_ntoa: 把struct in_addr网络地址转换一个点分式的ip字符串。
    • char *inet_ntoa(struct in_addr in);
  • 字节序转换函数
#include <arpa/inet.h>
uint32_t htonl(uint32_t hostlong);
uint16_t htons(uint16_t hostshort);
uint32_t ntohl(uint32_t netlong);
uint16_t ntohs(uint16_t netshort);
  • h:host主机字节序
  • n:network网络字节序
  • l: long int 4字节
  • s: short int 2字节
  • htonl: 把一个主机字节序的long int 转换成network字节序

  • bind()

#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
  • bind为socket描述符,指定(绑定)一个地址(ip+端口号)
    • sockfd: 要绑定地址的套接字描述符;
    • addr: 绑定的地址
    • addrlen: 第二个参数,地址结构体的长度
    • 返回值:成功返回0,失败返回-1,同时errno被设置。
    1. listen:让套接字进入监听模式
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>

int listen(int sockfd, int backlog);
  • sockfd: 要监听的套接字
  • backlog: 设置最大监听数目,同时处理的连接请求数目,在内核的连接请求队列上的最大请求数,不是最大的连接数目!!
  • 返回值:成功返回0,失败返回-1,同时errno被设置。调用listen后的套接字描述符,我们称之为“监听套接字”

      1. accept:接收一个tcp的客户端的请求
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>

int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
  • sockfd: 监听套接字
  • addr: 用来保存客户端的地址信息的,可以为NULL,为NULL,表示不保存客户的地址信息。
  • addrlen: 指针,指向的内存用来保存客户端地址结构体的长度.
  • NOTE:在调用时 addrlen中应该保存addr指向的空间的长度,因为客户端的地址长度有可能超过addr指向的空间,所以为了防止越界,addrlen在调用时要指定addr指向空间的最大长度。在函数返回后addrlen中保存的是客户端地址结构的实际长度。
  • 返回值:成功返回一个连接描述符,这个连接描述符就唯一代表与一个客户端的连接,后续所有与该客户端的数据收发,都需要通过它。connection 描述符。失败返回 -1,同时errno被设置。

      1. connect: 连接一个网络服务器
#include <sys/socket.h>

int connect(int socket, const struct sockaddr *address,socklen_t address_len);
  • socket:套接字描述符
  • address: 要连接的服务器地址
  • address_len : 第二个参数,地址结构体的长度
  • 返回值:成功返回0,失败返回-1,同时errno被设置。

猜你喜欢

转载自blog.csdn.net/qq_36337149/article/details/81333360