zoukankan      html  css  js  c++  java
  • (四)linux网络编程

    一、CS架构,BS架构

    (1)CS架构介绍(client server,客户端服务器架构),例如:qq、360网盘
    (2)BS架构介绍(broswer server,浏览器服务器架构)例如:浏览器

    二、TCP协议学习1

    1、关于TCP理解的重点
    (1)TCP协议工作在传输层,对上服务socket接口,对下调用IP层
    (2)TCP协议面向连接,通信前必须先3次握手建立连接关系后才能开始通信。
    (3)TCP协议提供可靠传输,不怕丢包、乱序等。
    2、TCP如何保证可靠传输
    (1)TCP在传输有效信息前要求通信双方必须先握手,建立连接才能通信
    (2)TCP的接收方收到数据包后会ack给发送方,若发送方未收到ack会丢包重传
    (3)TCP的有效数据内容会附带校验,以防止内容在传递过程中损坏
    (4)TCP会根据网络带宽来自动调节适配速率(滑动窗口技术),如果接收方接收压力很小,发送方便会慢慢提高发送速率;相反接收方压力大,发送方会慢慢讲地发送速率
    (5)发送方会给各分割报文编号,接收方会校验编号,一旦顺序错误即会重传。

    三、TCP协议的学习2

    1、TCP的三次握手

    (1)建立连接需要三次握手
    (2)建立连接的条件:服务器listen时客户端主动发起connect
    2、TCP的四次握手
    (3)关闭连接需要四次握手
    (4)服务器或者客户端都可以主动发起关闭

    注:这些握手协议已经封装在TCP协议内部,socket编程接口平时不用管
    3、基于TCP通信的服务模式
    (1)具有公网IP地址的服务器(或者使用动态IP地址映射技术)
    (2)服务器端socket、bind、listen后处于监听状态
    (3)客户端socket后,直接connect去发起连接。
    (4)服务器收到并同意客户端接入后会建立TCP连接,然后双方开始收发数据,收发时是双向的,而且双方均可发起
    (5)双方均可发起关闭连接
    4、常见的使用了TCP协议的网络应用
    (1)http(也是一个协议,纯传文件,文本信息)、ftp
    (2)QQ服务器
    (3)mail服务器

    四、socket编程接口介绍

    表示IP地址数据结构都定义在 /usr/include/netinet/in.h下,我们在linux下输入vim /usr/include/netinet/in.h便可查看in.h下定义的结构体

    1、建立连接
    (1)socket。socket函数类似于open,用来打开一个网络连接,如果成功则返回一个网络文件描述符(int类型),之后我们操作这个网络连接都通过这个网络文件描述符。

      int socket(int domain,int type,int protocol) //socket函数非常类似于open函数,socket函数是用来发开一个网络的,如果成功就会返回一个int型数据,网络文件描述符。之后我们操作这个网络都通过这个网络文件描述符。

      dimain:域,网络域,网络地址范围(IPV4或IPV6等)

      type:指定类型:SOCK_STREAM(TCP网络)、SOCK_DGRAM(UDP)、SOCK_SEQPACKET

      protocol:指定协议,如果指定0,表示使用默认的协议
    (2)bind函数

      int bind(int socket,const struct *address,socklen_t address_len);

      函数功能及作用:把本地的IP地址和我们的socket绑定起来,也就是第一个参数int socket,

      const struct  sockaddr *address:struct sockaddr,这个结构体是网络编程接口中用来表示一个IP地址的,注意这个IP地址是不区分IPv4和IPv6的(或者说是兼容IPv4和IPv6的)

      address_len:表示IP地址的长度  

    (3)listen函数

      int listen(int socket,int backlog)

      int socket:socket接口,和上面一样

      int backlog:表示我们可以同时接收几个,设置监听队列长度。

    对于服务器来说只要调用soclet、bind、listen函数就可以进入监听状态了,客户端那边调用socket函数打开文件描述符,然后调用connect去连接

    (4)connect

      int connect(int socket,const struct sockaddress *address,socklen_t address_len);

      要连那个服务器,就用那个address,调用connect函数去连接起来。自己的IP地址会自动得放到发送报文里去。这个函数是兼容IPV4和IPV6的,因为struct suckaddress它是兼容IPV4和IPV6的。

    3、发送和接收

    TCP层是不管谁发谁收的,在应用层实现。

    (1)send和write   在网络上发送,类似写文件;接收,类似于读文件。

    socket和write函数很想,返回值几乎是一模一样的。ssize_t write(int fildes,const void *buf,size_t nbyte),我们调用write函数写就可以把要写的内容发出去。

    send函数封装:ssize_t send(int socket,const void *buffer,size_t length,int flags),send比write就多一个flags,不考虑这个讲flags设置为0,那就和write是一模一样的。我们可以设置flags一些值:MSG_EOR(一个记录信息的一个结尾,表示我们一个信息就要完了),MSG_OOB(带外数据,即非正式数据,正常通信时是没有的,在一些特殊协议里会有),因此send在一些特殊协议我们才会用到flags,正常协议用不到的,正常通信设置flags为0,和write就一样
    (2)recv和read

    recv,read和send,write也是类比一样。不过他们是接收

    recv函数封装:ssize_t recv(int socket,void *buffer,size_t length,int flags);
    4、辅助性函数

    辅助性函数主要是进行一些IP地址转换的,为什么要进行IP地址转换呢,IP地址有两种形式,一种是点分十进制的形式即192.32.44.5,一种是32位二进制的形式也就是int类型,点分十进制比较符合人们的观看习惯;而二进制格式,更适合机器编程进行封装,操作。因此我们需要在这两种IP地址之间进行可靠的转换,而转换是由封装好的函数实现的。如下所示:

    (1)inet_aton、inet_ntoa、inet_addr

    inet_aton:将一个字符串IP地址转换为一个32位的网络序列IP地址

    int inet_aton(const char *cp, struct in_addr *inp);

    inet_aton函数接受两个参数。参数描述如下:
    1 输入参数string包含ASCII表示的IP地址。
    2 输出参数addr是将要用新的IP地址更新的结构。

    inet_ntoa:将网络地址转换成“.”点隔的字符串格式

    int inet_aton(const char *cp, struct in_addr *inp);

    inet_addr():的功能是将一个点分十进制的IP转换成一个长整数型数(u_long类型)

    in_addr inet_addr(const char *cp);

    这三个尽量都少使用,没下面好,其实他们和下面两个功能都一致,都没什么缺陷,唯一缺陷是他们不支持IPV6,下面支持。
    (2)inet_ntop、inet_pton

    这两个现在阶段变成最好使用,上面三个尽量少使用,这两个程序既支持IPV4又支持IPV6,现在我们下代码肯定要用inet_ntop和inet_pton函数

    inet_ntop:将32为二进制IP地址转换为点分十进制的形式。

    inet_pton:将点分十进制的形式转换为32位二进制的形式。

    我们记忆这个比较好记。ntop即n(网络)-to-p(字符串),网络层使用的是32位二进制,因此是32位二进制IP地址转换为点分十进制的形式,pton相反

    5、表示IP地址相关数据结构
    (1)都定义在 netinet/in.h
    (2)struct sockaddr,这个结构体是网络编程接口中用来表示一个IP地址的,注意这个IP地址是不区分IPv4和IPv6的(或者说是兼容IPv4和IPv6的)
    (3)

    typedef uint32_t in_addr_t; 网络内部用来表示IP地址的类型

    struct in_addr
    {
      in_addr_t s_addr;
    };
    (5)

    struct sockaddr_in
    {
      __SOCKADDR_COMMON (sin_);
      in_port_t sin_port; /* Port number. */
      struct in_addr sin_addr; /* Internet address. */

      /* Pad to size of `struct sockaddr'. */
      unsigned char sin_zero[sizeof (struct sockaddr) -
      __SOCKADDR_COMMON_SIZE -
      sizeof (in_port_t) -
      sizeof (struct in_addr)];
    };
    (6)struct sockaddr 这个结构体是linux的网络编程接口中用来表示IP地址的标准结构体,bind、connect等函数中都需要这个结构体,这个结构体是兼容IPV4和IPV6的。在实际编程中这个结构体会被一个struct sockaddr_in(IPV4)或者一个struct sockaddr_in6(IPV6)所填充。sockaddr这个结构体是兼容struct sockaddr_in和struct sockaddr_in6的

    6、IP地址格式转换函数实践

    (1)inet_addr函数:

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

    in_addr_t inet_addr(const char *cp); 表示将一个十进制的字符串IP地址,转换为机器可是别的in_addr_t(uint32_t)格式。

    示例代码如下:

    #include <stdio.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <arpa/inet.h>
    
    #define  IPADDR        "192.168.1.103"
    
    int main(void)
    {
        in_addr_t addr = 0;
        
        addr = inet_addr(IPADDR);
        printf("addr = 0x%x.
    ",addr); //0x6701a8c0 0x67:103、0x01:1、0xa8:168、0xc0:192
        
        return 0;
    }

    网络字节序默认是大端模式,如果是大端模式直接带进去参数就好了,如果是小端模式,要先转换为大端模式,然后再往进传参数。在网络上只允许使用大端模式,不允许小端模式。inet_addr这个函数可以可以检测当前cpu是大端模式还是小端模式,函数内部做了转换了。所以我们不用管大小端,只管他是大端模式就好了。

    不过inet_addr这个函数现在一般不用,因为他只适合IPV4网络,不适合IPV6网络。

    (2)inet_pton

     #include <arpa/inet.h>

    int inet_pton(int af, const char *src, void *dst);

     这里我们要知道:

    int af:表示是选择是IPV4还是IPV6,IPV4参数:AF_INET  IPV6参数:AF_INET6;

    const char *src:表示源十进制字符串IP地址,例如"192.168.1.1"

    void *dst:表示转出的,32位二进制IP地址,他的传参类型根据IPV4和IPV6有所不同,IPV4传的是:struct  in_addr 类型参数,IPV6传的是:struct in6_addr类型参数。我们还要注意in_addr和in6_addr这两个结构体的组成,如下所示:

    typedef uint32_t in_addr_t;
    struct in_addr
    {
      in_addr_t s_addr;
    };

    可以看出in_addr这个结构体里面只有一个成员为s_addr,这个成员的类型为in_addr_t即uint32_t类型。

    struct in6_addr
    {
        union
        {
            uint8_t __u6_addr8[16];
            #if defined __USE_MISC || defined __USE_GNU
            uint16_t __u6_addr16[8];
            uint32_t __u6_addr32[4];
            #endif
        } __in6_u;
        #define s6_addr __in6_u.__u6_addr8
        #if defined __USE_MISC || defined __USE_GNU
        # define s6_addr16 __in6_u.__u6_addr16
        # define s6_addr32 __in6_u.__u6_addr32
        #endif
    };                

    inet_pton函数返回值为一个int类型数据,表示转换是否完成,1表示转换成功,0表示转换不成功。

    下面写一个IPV4的调用inet_pton函数转换IP地址函数:

     #include <stdio.h>
     #include <arpa/inet.h>
     
     #define  IPADDRESS        "192.168.100.160"
     int main(void)
     {
         struct in_addr sAddr = {0};
         int  ulText;
         if(ulText)
         {
            ulText = inet_pton(AF_INET,IPADDRESS, &sAddr);
         }
         printf("ulText = %d
    sAddr = 0x%x
    ",ulText,sAddr.s_addr);
         
         return 0;
     }
     

    (3)inet_ntop函数

    #include <arpa/inet.h>

    const char *inet_ntop(int af, const void *src,char *dst, socklen_t size);

    int af:表示IPV4或者IPV6网络;

    const void *src:表示待转换二进制IP地址;

    char *dst:表示转换后的十进制字符串IP地址;

    socklen_t size:表示转换后的十进制字符串IP地址的长度。

    示例代码如下:

    #include<stdio.h>
    #include <arpa/inet.h>
    
    int main(void)
    {
        struct in_addr addr = {0};
        char ucBuff[50] = {0};
        
        addr.s_addr = 0x6601a8c0;
        inet_ntop(AF_INET,&addr,ucBuff,sizeof(ucBuff));
        
        printf("IP:%s
    ",ucBuff);
        
        return 0;
    }    

     

  • 相关阅读:
    你不该知道的.NET 第零回: 不继承Object的后果 不及格的程序员
    开张 不及格的程序员
    用WinDBG调试器 修改 星际争霸 等游戏. 不及格的程序员
    自定义服务器控件 继承不到父类/基类的 SupportsEventValidation 特性. 不及格的程序员
    讨论 计算机 操作系统休眠恢复的过程. 不及格的程序员
    十一期间 极品飞车13:变速 通关了 不及格的程序员
    谁发明的 Ctrl + Alt + Del 组合键,以及它在Windows中的重要性. 不及格的程序员
    asp.net development server 挂起问题解决
    SqlServer数据库记录数大引起的一系列问题解决
    完全分布模式hadoop集群安装配置之一安装第一个节点
  • 原文地址:https://www.cnblogs.com/zhangshenghui/p/6018160.html
Copyright © 2011-2022 走看看