• 04_tcp


    知识点1【多播】

    多播地址:

    多播地址向以太网MAC地址的映射

    UDP多播工作过程:

    多播地址结构体:

    多播套接口选项:

    知识点2【TCP面向链接编程】

    1、创建tcp套接字

    2、做为客户端需要具备的条件

    3、connect链接服务器的函数

    4、send函数

    5、recv函数

    知识点3【TCP服务器】

    做为TCP服务器需要具备的条件

    1、listen 函数

    2、accept函数

    知识点4【TCP的三次握手 四次挥手】

    1、TCP的三次握手 客户端 connec函数调用的时候发起

    2、四次挥手 调用close 激发 底层发送FIN关闭请求

    3、close 关闭套接字

    知识点1【多播】

    多播:

    数据的收发仅仅在同一分组中进行

    多播的特点:

    1、多播地址标示一组接口

    2、多播可以用于广域网使用

    在IPv4中,多播是可选的

    多播地址:

    Pv4的D类地址是多播地址

    十进制:224.0.0.1 ~ 239.255.255.254 任意一个IP地址 都代表一个多播组

    十六进制:E0.00.00.01 à EF.FF.FF.FE

    多播地址向以太网MAC地址的映射

    UDP多播工作过程:

    总结:1、主机先加入多播组 2、往多播组发送数据

    多播地址结构体:

    在IPv4因特网域(AF_INET)中,多播地址结构体用如下结构体ip_mreq表示

    多播套接口选项:

    1. int setsockopt(int sockfd, int level,int optname,   
    2.                 const void *optval, socklen_t optlen);
    3. 成功执行返回0,否则返回-1

    level

    optname

    说明

    optval类型

    IPPROTO_IP

    IP_ADD_MEMBERSHIP

    加入多播组

    ip_mreq{}

    IP_DROP_MEMBERSHIP

    离开多播组

    ip_mreq{}

    只能将自己加入到某个多播组

    1. #include<stdio.h>
    2. #include<string.h>
    3. #include<sys/socket.h>
    4. #include<netinet/in.h>
    5. #include <sys/types.h>
    6. #include <sys/stat.h>
    7. #include <arpa/inet.h>
    8. #include <fcntl.h>
    9. //将主机 加入到多播组 224.0.0.2 接受
    10. int main()
    11. {
    12. int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    13. //让sockfd有一个固定的IP端口
    14. struct sockaddr_in my_addr;
    15. bzero(&my_addr,sizeof(my_addr));
    16. my_addr.sin_family = AF_INET;
    17. my_addr.sin_port = htons(8000);
    18. my_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    19. bind(sockfd, (struct sockaddr *)&my_addr,sizeof(my_addr));
    20. //192.168.0.111 加入到多播组 224.0.0.2
    21. struct ip_mreq mreq;
    22. mreq.imr_multiaddr.s_addr = inet_addr("224.0.0.2");
    23. mreq.imr_interface.s_addr = htonl(INADDR_ANY);
    24. setsockopt(sockfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq,sizeof(mreq));
    25. while(1)
    26. {
    27. unsigned char buf[1500]="";
    28. recvfrom(sockfd,buf,sizeof(buf), 0,NULL,NULL);
    29. printf("buf=%s\n", buf);
    30. }
    31. close(sockfd);
    32. return 0;
    33. }

     运行结果:

    知识点2【TCP面向链接编程】

    TCP回顾

    1、面向连接的流式协议;可靠、出错重传、且每收到一个数据都要给出相应的确认

    2、通信之前需要建立链接

    3、服务器被动链接,客户端是主动链接

    TCP与UDP的差异:

    TCP C/S架构

    1、创建tcp套接字

    2、做为客户端需要具备的条件

    1、知道“服务器”的ip、port

    2、Connect主动连接“服务器”

    3、需要用到的函数

    socket—创建“主动TCP套接字”

    connect—连接“服务器”

    send—发送数据到“服务器”

    recv—接受“服务器”的响应

    close—关闭连接

    3、connect链接服务器的函数

    int connect(int sockfd,const struct sockaddr *addr,socklen_t len);

    功能:

    主动跟服务器建立链接

    参数:

    sockfd:socket套接字

    addr:   连接的服务器地址结构

    len:  地址结构体长度

    返回值:

    成功:0    失败:其他

    注意:

    1、connect建立连接之后不会产生新的套接字

    2、连接成功后才可以开始传输TCP数据

    3、头文件:#include

    4、send函数

    ssize_t send(int sockfd, const void* buf,size_t nbytes, int flags);

    功能:

    用于发送数据

    参数:

    sockfd: 已建立连接的套接字

    buf:    发送数据的地址

    nbytes:  发送缓数据的大小(以字节为单位)

    flags:    套接字标志(常为0)

    返回值:

    成功发送的字节数

    头文件:

    #include

    5、recv函数

    ssize_t recv(int sockfd, void *buf, size_t nbytes, int flags);

    功能:

    用于接收网络数据

    参数:

    sockfd:套接字

    buf: 接收网络数据的缓冲区的地址

    nbytes: 接收缓冲区的大小(以字节为单位)

    flags: 套接字标志(常为0)

    返回值:

    成功接收到字节数

    头文件:

    #include

    案例:TCP客户端

    1. #include<stdio.h>
    2. #include<string.h>
    3. #include<sys/socket.h>
    4. #include<netinet/in.h>
    5. #include <sys/types.h>
    6. #include <sys/stat.h>
    7. #include <arpa/inet.h>
    8. #include <fcntl.h>
    9. //TCP客户端
    10. int main()
    11. {
    12. //创建一个TCP套接字 SOCK_STREAM
    13. int sockfd = socket(AF_INET, SOCK_STREAM, 0);
    14. printf("sockfd = %d\n", sockfd);
    15. //bind是可选的
    16. struct sockaddr_in my_addr;
    17. bzero(&my_addr,sizeof(my_addr));
    18. my_addr.sin_family = AF_INET;
    19. my_addr.sin_port = htons(9000);
    20. my_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    21. bind(sockfd,(struct sockaddr *)&my_addr,sizeof(my_addr));
    22. //connect链接服务器
    23. struct sockaddr_in ser_addr;
    24. bzero(&ser_addr,sizeof(ser_addr));
    25. ser_addr.sin_family = AF_INET;
    26. ser_addr.sin_port = htons(8000);//服务器的端口
    27. ser_addr.sin_addr.s_addr = inet_addr("192.168.0.110");//服务器的IP
    28. //如果sockfd没有绑定固定的IP以及端口 在调用connect时候 系统给sockfd分配自身IP以及随机端口
    29. connect(sockfd, (struct sockaddr *)&ser_addr,sizeof(ser_addr));
    30. //给服务器发送数据 send
    31. printf("发送的消息:");
    32. char buf[128]="";
    33. fgets(buf,sizeof(buf),stdin);
    34. buf[strlen(buf)-1]=0;
    35. send(sockfd, buf, strlen(buf), 0);
    36. //接收服务器数据 recv
    37. char msg[128]="";
    38. recv(sockfd, msg,sizeof(msg), 0);
    39. printf("服务器的应答:%s\n", msg);
    40. //关闭套接字
    41. close(sockfd);
    42. return 0;
    43. }

    运行结果: 

    知识点3【TCP服务器】

    做为TCP服务器需要具备的条件

    1、具备一个可以确知的地址 bind

    2、让操作系统知道是一个服务器,而不是客户端 listen

    3、等待连接的到来 accpet

    对于面向连接的TCP协议来说,连接的建立才真正意味着数据通信的开始.

    1、listen 函数

    int listen(int sockfd, int backlog);

    功能:

    将套接字由主动修改为被动。

    使操作系统为该套接字设置一个连接队列,用来记录所有连接到该套接字的连接。

    参数:

    sockfd: socket监听套接字

    backlog:连接队列的长度

    返回值:

    成功:返回0

    2、accept函数

    int accept(int sockfd,struct sockaddr *cliaddr, socklen_t *addrlen);

    功能:

    从已连接队列中取出一个已经建立的连接,如果没有任何连接可用,则进入睡眠等待(阻塞)

    参数:

    sockfd: socket监听套接字

    cliaddr: 用于存放客户端套接字地址结构

    addrlen:套接字地址结构体长度的地址

    返回值:

    已连接套接字

    头文件:

    #include

    注意:

    返回的是一个已连接套接字,这个套接字代表当前这个连接

    1. #include<stdio.h>
    2. #include<string.h>
    3. #include<sys/socket.h>
    4. #include<netinet/in.h>
    5. #include <sys/types.h>
    6. #include <sys/stat.h>
    7. #include <arpa/inet.h>
    8. #include <fcntl.h>
    9. int main()
    10. {
    11. //1、创建一个tcp监听套接字
    12. int sockfd = socket(AF_INET, SOCK_STREAM, 0);
    13. //2、使用bind函数 给监听套接字 绑定固定的ip以及端口
    14. struct sockaddr_in my_addr;
    15. bzero(&my_addr,sizeof(my_addr));
    16. my_addr.sin_family = AF_INET;
    17. my_addr.sin_port = htons(8000);
    18. my_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    19. bind(sockfd, (struct sockaddr *)&my_addr, sizeof(my_addr));
    20. //3、使用listen创建连接队列 主动变被动
    21. listen(sockfd, 10);
    22. //4、使用accpet函数从连接队列中 提取已完成的连接 得到已连接套接字
    23. struct sockaddr_in cli_addr;
    24. socklen_t cli_len = sizeof(cli_addr);
    25. int new_fd = accept(sockfd, (struct sockaddr *)&cli_addr, &cli_len);
    26. //new_fd代表的是客户端的连接 cli_addr存储是客户端的信息
    27. char ip[16]="";
    28. inet_ntop(AF_INET,&cli_addr.sin_addr.s_addr, ip,16);
    29. printf("客户端:%s:%hu连接了服务器\n",ip,ntohs(cli_addr.sin_port));
    30. //5、获取客户端的请求 以及 回应客户端
    31. char buf[128]="";
    32. recv(new_fd, buf,sizeof(buf),0);
    33. printf("客户端的请求为:%s\n",buf);
    34. send(new_fd,"recv", strlen("recv"), 0);
    35. //6、关闭已连接套接字
    36. close(new_fd);
    37. //7、关闭监听套接字
    38. close(sockfd);
    39. return 0;
    40. }

     运行结果:

    知识点4【TCP的三次握手 四次挥手】

    1、TCP的三次握手 客户端 connec函数调用的时候发起

    SYN是一个链接请求:是TCP报文中的某一个二进制位

    第一次握手:客户端 发送SYN请求 链接服务器

    第二次握手:服务器 ACK回应客户端的链接请求 同时 服务器给客户端发出链接请求

    第三次握手:客户端 ACK回应 服务器的链接请求

    2、四次挥手 调用close 激发 底层发送FIN关闭请求

    不缺分客户端 或 服务器先后问题。

    第一次挥手:A调用close 激发底层 发送FIN关闭请求 并且A处于半关闭状态

    第二次挥手:B的底层给A回应ACK 同时导致B的应用层recv/read收到0长度数据包

    第三次挥手:B调用close 激发底层给A发送FIN关闭请求 并且B处于半关闭状态

    第四次挥手:A的底层给B回应ACK 同时 A处于完全关闭状态,B收到A的ACK也处于完全关闭状态

    3、close 关闭套接字

     

  • 相关阅读:
    ch0-01
    一次生产环境OOM排查
    2.3 初探Hadoop世界
    【实训项目】“优品果园”-线上水果商城小程序
    用HTTP proxy module配置一个简单反向代理服务器
    1、第一个驱动程序hello_drv
    C语言之指针(中)
    vue-cli3项目本地启用https,并用mkcert生成证书
    End-to-end 3D Point Cloud Instance Segmentation without Detection
    Ros noetic opencv4.2.0 cv_bridge 冲突 适配Opencv3.2.0完成自己的USB_Camera 跑通 Orb_slam2
  • 原文地址:https://blog.csdn.net/buhuidage/article/details/127940542