IP和端口

  • 端口:相当于消息接收者对应进程的名字。找到端口就能找到对应的应用程序(短整型 16 为 unsigned short 能够使用的范围 0~ 65535)
  • IP地址能够找到一台电脑

  • 例子:一个快递员送快递,IP地址相当于快递的地址,快递员到达地址对应的屋子后,发现屋子有四个人,他怎么知道快递给谁,就是通过端口号,来确定快递该给谁。

网络分层模型(OSI或ISO)

image.png

字节序

  • 大端:高-》低 ,低-》高;
  • 小端:低-》低,高-》高;
  • 例子:0x12 34 56 78
  • 大端:12 34 56 78
  • 小端:78 56 34 12

字节序转换

  • uint16_t htons(uint16_t hostshort) 将一个短整型从主机字节序-》网络字节序
  • uint16_t ntohs(unit16_t netshort) 将一个短整型从网络字节序-》主机字节序

IP地址转换

  • 主机字节序转换为网络字节序
    • 主机IP地址是字符串,网络IP地址是整形
  1. #include<arpa/inet.h>
  2. int inet_pton(int af,const char *src,void *dst);
  • af 地址族(IP地址家族包括ipv4,ipv6)协议
    • AF_INET:ipv4格式
    • AF_INET6 :ipv6格式ip地址
  • src:传入参数,对应要转换的点分十进制的IP地址:192.168.1.100
  • dst:传出参数,函数调用完成,转换得到的大端整型IP被写入这块内存中
  • 返回值:成功返回1,失败返回0或-1;
  • 网络字节序转换为主机字节序

    1. const char *inet_ntop(int af,const void *src,char *dst,socklen_t size)
    • src :存储传入的大端IP地址
    • dst:传出参数,存储转换得到的小端点分十进制的IP地址
    • size :修饰dst参数的,标记dst指向的内存中最多可以存储多少个字节。
    • 返回值
      • 成功返回不为空的dst的内存地址,通过返回值也可以直接取出转换得到的字符串
      • 失败:NULL
  • 点分十进制IP->大端整形
  1. in_addr_t inet_addr(const char *cp);
  • 大端整形-》点分十进制
  1. char* inet_ntoa(struct in_addr_in);

服务端:

socket()

  • 服务端调用socket()函数,创建ipv4网络协议,传输协议为TCP。

    1. #include<sys/types.h>
    2. #include<sys/socket.h>
    3. int socket(int domain,int type,int protocol);
    1. - domain: ip地址格式:AF_INET
    2. - type:指定传输层协议 SOCK_STREAM(流式协议) TCP连用 SOCK_DGREAM(报式协议)UDP连用
    3. - proticol:与type中指定的传输协议配套使用;为流式0TCP 报式 0 UDP
  • 返回值:
    • 成功:返回文件描述符
    • 失败:-1;

bind()

  • 调用bind()函数,给socket绑定ip地址和端口号

    1. int bind(int sockfd,const struct sockaddr *addrsocklen_t addrlen)
    • 返回值

      • 成功返回1;
      • 失败返回0;

        调用listen()

        函数进行监听文件描述符有没有新的连接。
        1. int listen(int sockfd,int backlog)
    • backlog:指定一次性检测多少个连接数。最大值 128;

    • 返回:成功0,失败-1;
    • listen函数内部有个任务与队列 ;
    • 注意:监听socekt和真正用来传数据的socket是两个
      • 监听socekt
      • 已连接socekt

accept()

  • 进入监听状态后调用accept()函数,获取客户端的连接,没有连接会阻塞等待客户端连接的到来(默认阻塞);

    1. int accept(int sockfd,struct sockaddr *addr,socklen_t *addrlen)
    1. - addr 传出参数,
    2. - addrlen 传入传出参数,整形:指向的内存大小
    3. - 返回值:
    4. - 成功:文件描述符 用于通信的;
    5. - 失败 -1

客户端:

socket()

  • 客户端创建号socekt后,bind()函数绑定后,

  • connect·()

    调用connect函数发起连接,该函数指明服务端的IP地址和端口号,TCP三次握手开始

    1. int connect(int sockfd,const struct sockaddr *addr,socklen_t addrlen)
    • 都是传入参数,通信套接字,
    • 返回值:绑定成功,需要绑定,随机分配一个空闲的端口。0;失败-1;
  • 连接建立好后,客户端和服务端开始相互传输数据,
    • read()和recv();
  1. ssize_t read(int sockfd,void *buf,size_t size);
  2. ssize_t recv(int sockfd,void *buf,size_t size,int flags);
  • sockfd: 用于通信的文件描述符,accept的返回值
  • buf:指向一块有效内存,用于存储接收数据
  • size:参数buf指向的内存的容量
  • falgs:特殊的属性,一般不用,指定为0;
  • 返回值:
    • 大于0;实际接收的字节数;
    • 等于0;对方断开了连接;
    • 如果连接没有断开,接收端接收不到数据,接收端的函数就会阻塞等待数据到达,数据到达后函数接触阻塞,开始接收数据。当发送端断开连接,接收端无法接收到任何数据,但是这时候就不会阻塞了,函数直接返回0;
  • write 和 send();

    1. ssize_t wirte(int fd,const *buf,size_t len)
    2. ssize_t send(int fd,const void *buf,size_t len,int flags)
    • fd :通信文件描述符,accept()函数的返回值
    • buf:传入参数,要发送的字符串
    • len:要发送字符串的额长度
    • flags:不用,指定为0;
    • 返回值:
      • 大于0:实际发送的字节数,和参数len是相等的;
      • -1:发送数据失败了
  • 关闭套接字close();
    1. #include<unistd.h>
    2. int close(int fd);

socket维护了两个队列

  • 一个是还没完全建立连接的队列,叫TCP半连接队列,这个对列还没有完成三次握手服务端处于syn_rcvd状态
  • 一个是已经建立连接的对列,称为TCP全连接对列,这个对列完成了三次握手的连接,此时服务端处于established状态。
  • 当TCP全连接对列不为空后,服务端的accept()函数,就会从队列中拿出一个已经为你撑连接的socket返回应用程序,后续的数据传输都用这个socket。