C语言基础知识(5):TCP网络编程

        TCP 是面向连接的、可靠的流协议。流就是指不间断的数据结构,当应用程序采用 TCP 发送消息时,虽然可以保证发送的顺序,但还是犹如没有任何间隔的数据流发送给接收端。TCP 为提供可靠性传输,实行“顺序控制”或“重发控制”机制。此外还具备“流控制(流量控制)”、“拥塞控制”、提高网络利用率等众多功能。

1.TCP协议
  • TCP 与 UDP 的区别相当大。它充分地实现了数据传输时各种控制功能,可以进行丢包时的重发控制,还可以对次序乱掉的分包进行顺序控制。而这些在 UDP 中都没有。
  • 此外,TCP 作为一种面向有连接的协议,只有在确认通信对端存在时才会发送数据,从而可以控制通信流量的浪费。
  • 根据 TCP 的这些机制,在 IP 这种无连接的网络上也能够实现高可靠性的通信( 主要通过检验和、序列号、确认应答、重发控制、连接管理以及窗口控制等机制实现)。
2.三次握手
  • TCP 提供面向有连接的通信传输。面向有连接是指在数据通信开始之前先做好两端之间的准备工作。
  • 所谓三次握手是指建立一个 TCP 连接时需要客户端和服务器端总共发送三个包以确认连接的建立。在socket编程中,这一过程由客户端执行connect来触发。

下面来看看三次握手的流程图:

  • 第一次握手:客户端将标志位SYN置为1,随机产生一个值seq=J,并将该数据包发送给服务器端,客户端进入SYN_SENT状态,等待服务器端确认。
  • 第二次握手:服务器端收到数据包后由标志位SYN=1知道客户端请求建立连接,服务器端将标志位SYN和ACK都置为1,ack=J+1,随机产生一个值seq=K,并将该数据包发送给客户端以确认连接请求,服务器端进入SYN_RCVD状态。
  • 第三次握手:客户端收到确认后,检查ack是否为J+1,ACK是否为1,如果正确则将标志位ACK置为1,ack=K+1,并将该数据包发送给服务器端,服务器端检查ack是否为K+1,ACK是否为1,如果正确则连接建立成功,客户端和服务器端进入ESTABLISHED状态,完成三次握手,随后客户端与服务器端之间可以开始传输数据了。
3.四次挥手
  • 四次挥手即终止TCP连接,就是指断开一个TCP连接时,需要客户端和服务端总共发送4个包以确认连接的断开。在socket编程中,这一过程由客户端或服务端任一方执行close来触发。
  • 由于TCP连接是全双工的,因此,每个方向都必须要单独进行关闭,这一原则是当一方完成数据发送任务后,发送一个FIN来终止这一方向的连接,收到一个FIN只是意味着这一方向上没有数据流动了,即不会再收到数据了,但是在这个TCP连接上仍然能够发送数据,直到这一方向也发送了FIN。首先进行关闭的一方将执行主动关闭,而另一方则执行被动关闭。

下面来看看四次挥手的流程图:

  • 中断连接端可以是客户端,也可以是服务器端。
  • 第一次挥手:客户端发送一个FIN=M,用来关闭客户端到服务器端的数据传送,客户端进入FIN_WAIT_1状态。意思是说"我客户端没有数据要发给你了",但是如果你服务器端还有数据没有发送完成,则不必急着关闭连接,可以继续发送数据。
  • 第二次挥手:服务器端收到FIN后,先发送ack=M+1,告诉客户端,你的请求我收到了,但是我还没准备好,请继续你等我的消息。这个时候客户端就进入FIN_WAIT_2 状态,继续等待服务器端的FIN报文。
  • 第三次挥手:当服务器端确定数据已发送完成,则向客户端发送FIN=N报文,告诉客户端,好了,我这边数据发完了,准备好关闭连接了。服务器端进入LAST_ACK状态。
  • 第四次挥手:客户端收到FIN=N报文后,就知道可以关闭连接了,但是他还是不相信网络,怕服务器端不知道要关闭,所以发送ack=N+1后进入TIME_WAIT状态,如果Server端没有收到ACK则可以重传。服务器端收到ACK后,就知道可以断开连接了。客户端等待了2MSL后依然没有收到回复,则证明服务器端已正常关闭,那好,我客户端也可以关闭连接了。最终完成了四次握手。
4.上面是一方主动关闭,另一方被动关闭的情况,实际中还会出现同时发起主动关闭的情况

具体流程如下图(同时挥手):

5.函数接口

(1)socket():创建套接字

函数原型:

int socket(int domain, int type, int protocol);

参数说明:

domain:协议族

                AF_UNIX, AF_LOCAL 本地通信

                AF_INET ipv4

                AF_INET6 ipv6

type:套接字类型

          SOCK_STREAM:流式套接字

          SOCK_DGRAM:数据报套接字

protocol:协议 - 填0 自动匹配底层 ,根据type 系统默认自动帮助匹配对应协议

                传输层:IPPROTO_TCP、IPPROTO_UDP、IPPROTO_ICMP

                网络层:htons(ETH_P_IP|ETH_P_ARP|ETH_P_ALL)

返回值:

成功:文件描述符

失败:-1

(2)bind():绑定IP和端口

函数原型:

int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);

参数说明:

sockfd:文件描述符

addr:通用结构体,根据socket第一个参数选择的通信方式最终确定这需要真正填充传递的结构体是那个类型。强转后传参数。

addrlen:填充的结构体的大小

返回值:

成功:0

失败:-1

通用结构体:相当于预留一个空间

struct sockaddr {

        sa_family_t sa_family;

        char sa_data[14];

}

ipv4的结构体:

struct sockaddr_in {

        sa_family_t sin_family; //协议族AF_INET

        in_port_t sin_port; //端口

        struct in_addr sin_addr;

};

struct in_addr {

        uint32_t s_addr; //IP地址

};

本地址通信结构体:

struct sockaddr_un {

        sa_family_t sun_family; //AF_UNIX

        char sun_path[108]; //在本地创建的套接字文件的路径及名字

};

ipv6通信结构体:

struct sockaddr_in6 {

        sa_family_t sin6_family;

        in_port_t sin6_port;

        uint32_t sin6_flowinfo;

        struct in6_addr sin6_addr;

        uint32_t sin6_scope_id;

};

struct in6_addr {

        unsigned char s6_addr[16];

};

(3)listen():监听,将主动套接字变为被动套接字

函数原型:

int listen(int sockfd, int backlog);

参数说明:

sockfd:套接字

backlog:同时响应客户端请求链接的最大个数,不能写0,不同平台可同时链接的数不同,一般写6-8个

(队列1:保存正在连接)

(队列2,连接上的客户端)

返回值:

成功:0

失败:-1

(4)accept():阻塞函数,阻塞等待客户端的连接请求,如果有客户端连接,则accept()函数返回,返回一个用于通信的套接字文件;

函数原型:

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

参数说明:

Sockfd :套接字

addr:链接客户端的ip和端口号 如果不需要关心具体是哪一个客户端,那么可以填NULL; addrlen:结构体的大小 如果不需要关心具体是哪一个客户端,那么可以填NULL;

返回值:

成功:文件描述符; //用于通信

失败:-1

(5)recv():接收数据

函数原型:

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

参数说明:

sockfd:acceptfd

buf:接受数据的缓冲区

len:接收数据的缓冲区大小

flags:一般填0,相当于read()函数

MSG_DONTWAIT:非阻塞

返回值:

< 0 失败出错 更新errno

==0 表示客户端退出

> 0 成功接收的字节个数

6.connect():用于连接服务器

函数原型:

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

参数说明:

sockfd:socket函数的返回值

addr:填充的结构体是服务器端的;

addrlen:结构体的大小

返回值 :

成功:0

失败:-1

7.send():发送数据

函数原型:

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

参数说明:

sockfd:socket函数的返回值

buf:发送内容存放的地址

len:发送内存的长度 flags:如果填0,相当于write();

6.示例

服务端:

#include <stdio.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdlib.h>#include <sys/wait.h>#include <string.h>int main(int argc, char const *argv[])
{if (argc != 2){printf("please input %s <port>\n", argv[0]);return -1;}int sockfd, acceptfd;sockfd = socket(AF_INET, SOCK_STREAM, 0);if (sockfd < 0){perror("socket err.");return -1;}struct sockaddr_in serveraddr, caddr;serveraddr.sin_family = AF_INET;serveraddr.sin_port = htons(atoi(argv[1]));// serveraddr.sin_addr.s_addr=inet_addr(argv[1]);//自动获取ip//serveraddr.sin_addr.s_addr=htonl(INADDR_ANY);// serveraddr.sin_addr.s_addr=INADDR_ANY; //0.0.0.0serveraddr.sin_addr.s_addr = inet_addr("0.0.0.0");socklen_t len = sizeof(caddr);if (bind(sockfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr)) < 0){perror("bind err.");return -1;}if (listen(sockfd, 5) < 0){perror("listen err.");return -1;}while (1){acceptfd = accept(sockfd, (struct sockaddr *)&caddr, &len);if (acceptfd < 0){perror("accept err.");return -1;}printf("client:ip=%s  port=%d\n",inet_ntoa(caddr.sin_addr), ntohs(caddr.sin_port));char buf[128];pid_t pid = fork();if (pid < 0){perror("fork err.");return -1;}else if (pid == 0){ //发送int sendbyte;while (1){fgets(buf, sizeof(buf), stdin);if (buf[strlen(buf) - 1] == '\n')buf[strlen(buf) - 1] = '\0';sendbyte = send(acceptfd, buf, sizeof(buf), 0);if (sendbyte < 0){perror("send error.");return -1;}}}else{int recvbyte;while (1){recvbyte = recv(acceptfd, buf, sizeof(buf), 0);if (recvbyte < 0){perror("recv err.");return -1;}else if (recvbyte == 0){printf("client exit.\n");kill(pid,SIGKILL);wait(NULL);break;}else{printf("buf:%s\n", buf);}}close(acceptfd);}}close(sockfd);return 0;
}

客户端:

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <sys/wait.h>int main(int argc, char const *argv[])
{if (argc != 3){printf("please input %s <ip> <port>\n", argv[0]);return -1;}//1.创建套接子int sockfd;sockfd = socket(AF_INET, SOCK_STREAM, 0);if (sockfd < 0){perror("socket error.");return -1;}printf("sockfd=%d\n", sockfd);//填充ipv4的通信结构体  服务器的ip和端口struct sockaddr_in serveraddr;serveraddr.sin_family = AF_INET;serveraddr.sin_port = htons(atoi(argv[2]));serveraddr.sin_addr.s_addr = inet_addr(argv[1]);//2.请求链接服务器if (connect(sockfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr)) < 0){perror("connect error.");return -1;}//5.循环发送消息  通信char buf[128];pid_t pid = fork();if (pid < 0){perror("fork err.");return -1;}else if (pid == 0){int recvbyte;while (1){recvbyte = recv(sockfd, buf, sizeof(buf), 0);if (recvbyte < 0){perror("recv err.");return -1;}printf("buf:%s\n", buf);}}else{int sendbyte;while (1){fgets(buf, sizeof(buf), stdin);if (buf[strlen(buf) - 1] == '\n')buf[strlen(buf) - 1] = '\0';sendbyte = send(sockfd, buf, sizeof(buf), 0);if (sendbyte < 0){perror("send error.");return -1;}if(strncmp(buf,"quit",4)==0){kill(pid,SIGKILL);wait(NULL);exit(-1);}}}close(sockfd);return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/327824.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Redis 持久化——AOF

文章目录 为什么需要AOF?概念持久化查询和设置1. 查询AOF启动状态2. 开启AOF持久化2.1 命令行启动AOF2.2 配置文件启动 AOF 3. 触发持久化3.1 自动触发3.3 手动触发 4. AOF 文件重写4.1 什么是AOF重写&#xff1f;4.2 AOF 重写实现4.3 AOF 重写流程 5. 配置说明6. 数据恢复6.1…

【SpringCloud】之远程消费(进阶使用)

&#x1f389;&#x1f389;欢迎来到我的CSDN主页&#xff01;&#x1f389;&#x1f389; &#x1f3c5;我是君易--鑨&#xff0c;一个在CSDN分享笔记的博主。&#x1f4da;&#x1f4da; &#x1f31f;推荐给大家我的博客专栏《SpringCloud开发之远程消费》。&#x1f3af;&a…

H266/VVC多样化视频编码工具概述

全景视频编码 全景视频&#xff1a; 具有360度全包围视角的球面视频。 全景视频编码&#xff1a; 包括H266在内的视频编码算法都是以平面视频为对象的&#xff0c;为了采用传统的视频编码编码算法&#xff0c;全景视频需要转换为平面视频&#xff0c;其中经纬图等角映射&#…

dubbo的基础知识

dubbo是什么 Dubbo是一个分布式服务框架&#xff0c;是一种高性能的远程通讯框架。它提供了基于Java的RPC&#xff08;远程过程调用&#xff09;通信机制&#xff0c;使得应用之间可以方便地进行远程调用&#xff0c;实现分布式服务的调用和管理。Dubbo提供了服务注册、发现、负…

【大数据进阶第三阶段之Hive学习笔记】Hive常用命令和属性配置

目录 1、Hive安装 2、HiveJDBC访问 2.1、启动hiveserver2服务 2.2、连接hiveserver2服务 2.3、注意 3、Hive常用交互命令 3.1、“-e”不进入hive的交互窗口执行sql语句 3.2、“-f”执行脚本中sql语句 4、Hive其他命令操作 4.1、退出hive窗口 4.2、在hive cli命令窗口…

基于OpenCV的透视变换

基础概念 透视变换(Perspective Transformation)是仿射变换的一种非线性扩展,是将图片投影到一个新的视平面(Viewing Plane)&#xff0c;也称作投影映射(Projective Mapping)。 原理&#xff1a;将二维的图片投影到一个三维视平面上&#xff0c;然后再转换到二维坐标下&#…

LeetCode 2807.在链表中插入最大公约数

【LetMeFly】2807.在链表中插入最大公约数 力扣题目链接&#xff1a;https://leetcode.cn/problems/insert-greatest-common-divisors-in-linked-list/ 给你一个链表的头 head &#xff0c;每个结点包含一个整数值。 在相邻结点之间&#xff0c;请你插入一个新的结点&#x…

使用 C# Winfrom编写倒计时功能

在日常生活中&#xff0c;我们经常需要倒计时来提醒自己重要的时间节点&#xff0c;比如倒计时到达一个特定的日期和时间。介绍一个使用 C# 编写的倒计时应用程序的实现。 步骤一&#xff1a;应用程序的功能 它具有以下几个主要特点&#xff1a; 用户输入目标日期和时间&…

CSS3 边框border、outline、box-shadow

1 border 语法&#xff1a;border: width style color 2 outline 语法&#xff1a;outline: width style color 2.1 outline-offet MDN解释&#xff1a;用于设置outline与一个元素边缘或边框之间的间隙 即&#xff1a;设置outline相对border外边缘的偏移&#xff0c;可以为…

excel统计分析——两因素有重复方差分析

参考资料&#xff1a;生物统计学 无重复观测值的两因素方差分析只能研究两个因素的主效应&#xff0c;不能考察因素间的交互作用&#xff0c;只有在确定因素间不存在交互作用时才能进行无重复观测值的试验和分析。为了准确估计因素的主效应、交互作用和随机误差&#xff0c;每个…

DQL命令查询数据 (二)

本课目标 掌握 ORDER BY 子句 使用 LIMIT 子句实现分页查询 掌握MySQL的分组查询 掌握MySQL的子查询 SELECT 语法 ORDER BY 排序 ORDER BY 子句&#xff1a;按照一定顺序显示查询结果 排序可以是升序&#xff08;ASC&#xff09;或者是降序&#xff08;DESC&#xff09;&…

使用C#发送邮箱验证码

使用C#发送邮箱验证码 在很多应用程序中&#xff0c;我们需要使用邮箱来进行用户身份验证。其中一种常见的方式是通过发送验证码到用户的邮箱&#xff0c;然后要求用户输入该验证码进行验证。本文将介绍如何使用 C# 发送邮箱验证码。 声明 验证码登录没有用任何的工具&#…

【大数据】Zookeeper 数据写入与分布式锁

Zookeeper 数据写入与分布式锁 1.数据是怎么写入的2.基于 Zookeeper 实现分布式锁 1.数据是怎么写入的 无论是 Zookeeper 自带的客户端 zkCli.sh&#xff0c;还是使用 Python&#xff08;或者其它语言&#xff09;实现的客户端&#xff0c;本质上都是连接至集群&#xff0c;然…

记事本在手机桌面上怎么找?手机里的记事本怎么找?

在日常生活、工作和学习中&#xff0c;我们时常需要随手记录一些重要的事项、灵感闪现的瞬间或者是待办的任务。比如&#xff0c;在超市购物前&#xff0c;列出购物清单&#xff1b;在开会时&#xff0c;记下重要的讨论点&#xff1b;在学习时&#xff0c;捕捉那一刹那的灵感。…

C语言-第十八周做题总结-数组3

id:454 A.字符串逆序 题目描述 输入一个字符串&#xff0c;对该字符串进行逆序&#xff0c;输出逆序后的字符串。 输入 输入在一行中给出一个不超过80个字符长度的、以回车结束的非空字符串。 输出 在一行中输出逆序后的字符串。 输入样例 输出样例 题解 先用一个while…

【大数据进阶第三阶段之Hive学习笔记】Hive基础入门

目录 1、什么是Hive 2、Hive的优缺点 2.1、 优点 2.2、 缺点 2.2.1、Hive的HQL表达能力有限 2.2.2、Hive的效率比较低 3、Hive架构原理 3.1、用户接口&#xff1a;Client 3.2、元数据&#xff1a;Metastore 3.3、Hadoop 3.4、驱动器&#xff1a;Driver Hive运行机制…

2.3_6 用信号量实现进程互斥、同步、前驱关系

2.3_6 用信号量实现进程互斥、同步、前驱关系 #mermaid-svg-fj0wp6tJGfadcT8h {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-fj0wp6tJGfadcT8h .error-icon{fill:#552222;}#mermaid-svg-fj0wp6tJGfadcT8h .error-t…

在Ubuntu22.04上离线部署Tailchat(一)

一&#xff1a;Tailchat介绍 Tailchat 是一款插件化易拓展的开源 IM 应用。可拓展架构赋予 Tailchat 无限可能性。前端微内核架构 后端微服务架构 使得 Tailchat 能够驾驭任何定制化/私有化的场景&#xff0c;是面向企业与私域用户打造&#xff0c;高度自由的群组管理与定制化…

第11章 GUI Page462~476 步骤二十三,二十四,二十五 Undo/Redo ③实现“Undo/Redo”菜单项

工程六 添加“编辑”菜单和子菜单 菜单ID分别为 idMenuEditUndo 和 idMenuEditRedo 热键&#xff08;快捷键&#xff09;分别为CtrlZ 和 CtrlShiftZ 变量名分别为 MenuItemEditUndo 和 MenuItemEditRedo 分别添加事件 ActionLink类增加成员函数 运行效果&#xff1a;“添加…

C语言注意点(2)

1.使用pow函数的相关问题 局部变量n0 while(num/pow(10,n)) n; 为什么不可行 printf("%d",num/pow(10,4)%10) 为什么要提前用temp先引出来 答&#xff1a;pow函数的返回值为double类型&#xff0c;1.终止条件不会满足 2.num/pow(10,4)结果为浮点型&#xff0c;浮…