C语言-TCP通信创建流程

TCP通信创建流程

1. 客户端创建TCP连接

在整个流程中, 主要涉及以下⼏个接⼝socket() : 创建套接字, 使⽤的套接字类型为流式套接字connect() : 连接服务器send() : 数据发送recv() : 数据接收

创建套接字

首先,我们需要创建套接字,套接字是通信的基础。我们可以通过 socket() 函数来创建套接字。

int socket(int domain, int type, int protocol);
参数:@domain地址族AF_UNIX, AF_LOCAL  本地通信,数据不仅过网卡AF_INET         IPV4 ineter⽹通信  AF_INET6        IPV6 ineter⽹通信AF_PACKET       网卡上的数据包通信....@ type使⽤协议类型SOCK_STREAM 流式套接字(TCP)SOCK_DGRAM 报⽂套接字(UDP)SOCK_RAW原始套接字: (IP,ICMP)......@protocol协议编号0 : 让系统⾃动识别IPPROTO_TCP : TCP协议IPPROTO_UDP : UDP协议返回值:成功返回得到的⽂件描述符。当前可使用的最小描述符失败返回 -1

连接服务器

创建套接字之后,我们需要连接服务器。连接服务器需要调用 connect() 函数。

发起对套接字的连接 (基于⾯向连接的协议)
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
参数:@sockfd套接字描述符@addr   连接的套接字的地址结构对象的地址 (⼀般为服务器)服务器地址struct sockaddr {unsigned short sa_family; // 地址族 对应socket()中的domainchar sa_data[14]; // 地址数据 ip地址端口信息};struct sockaddr_in { short int sin_family; // 地址族 AF_INETunsigned short int sin_port; // 端口号struct in_addr sin_addr;// IP地址unsigned char sin_zero[8]; // 填充字节 为了对齐sockaddr};struct in_addr {uint32_t       s_addr; // IP地址};@addrlen地址长度返回值:成功返回0失败返回-1 并设置 errno

数据发送

连接服务器之后,我们就可以向服务器发送数据。发送数据需要调用 send() 函数。

基于套接字(建⽴连接)发送数据
int send(int sockfd, const void *buf, size_t len, int flags);
参数:@sockfd套接字描述符@buf    发送的数据@len    发送数据的长度@flags  发送标志
函数返回值:成功返回发送的字节数失败返回-1,并设置errno

数据接收

服务器向客户端发送数据之后,客户端就可以接收数据。接收数据需要调用 recv() 函数。

接收套接字的数据 (基于⾯向连接的协议)
int recv(int sockfd, void *buf, size_t len, int flags);
参数:@sockfd套接字描述符@buf    接收的数据@len    接收数据的长度@flags  接收标志
函数返回值:成功返回接收的字节数失败返回-1,并设置errno

完整流程

//todo tcp客户端,循环发送数据,接收回传数据
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <unistd.h>#define N 128
//初始化socket
int  init_socket(char *ip,char *port){int init_socket_fd= socket(AF_INET,SOCK_STREAM,0);if (init_socket_fd==-1){printf("init_socket err");exit(EXIT_FAILURE);}struct sockaddr_in server_addr;socklen_t len=sizeof(server_addr);bzero(&server_addr,len);server_addr.sin_family=AF_INET;inet_aton(ip,&server_addr.sin_addr);server_addr.sin_port= htons(atoi(port));//连接int ret= connect(init_socket_fd,(struct sockaddr*)&server_addr,len);if (ret==-1){printf("connect error,连接失败\n");exit(EXIT_FAILURE);}return init_socket_fd;
}//客户端接收数据
int Client_Receive_data(int socket_fd){char receive_msg[N];bzero(receive_msg,N);int recv_len= recv(socket_fd, receive_msg,sizeof(receive_msg),0);if (recv_len == -1) {printf("recv error\n");exit(EXIT_FAILURE);}receive_msg[recv_len] = '\0';printf("收到客户端数据:[%s]\n",receive_msg);
}//客户端发送数据
int  Client_Send_data(int socket_fd){char msg[N];while (1){bzero(&msg, sizeof (msg));printf("请输入:\n");fgets(msg, sizeof(msg),stdin);msg[strlen(msg)-1]='\0';printf("发送数据%s\n",msg);int  Send_data_len= send(socket_fd,&msg, strlen(msg),0);if (Send_data_len==-1){printf("发送失败 send err\n");exit(EXIT_FAILURE);}printf("发送了%d个字节\n",Send_data_len);if (strncmp(msg, "exit", 4) == 0) {printf("退出通信\n");close(socket_fd);break;}break;//接收Client_Receive_data(socket_fd);}return 0;
}int main(){//初始化连接int socket_fd = init_socket("172.17.128.1","8888");//发送数据Client_Send_data(socket_fd);return 0;
}

服务端流程

在这里插入图片描述

在上述流程中,相对于客户端主要增加以下新的流程
bind : 绑定 ip 地址与端⼝号,⽤于客户端连接服务器
listen : 建⽴监听队列,并设置套接字的状态为 listen 状态, 表示可以接收连接请求
accept : 接受连接, 建⽴三次握⼿, 并创建新的⽂件描述符, ⽤于数据传输

socket 套接字状态如下图:
在这里插入图片描述

CLOSED : 关闭状态
SYN-SENT : 套接字正在试图主动建⽴连接 [发送 SYN 后还没有收到 ACK],很短暂
SYN-RECEIVE : 正在处于连接的初始同步状态 [收到对⽅的 SYN,但还没收到⾃⼰发过去的SYN 的 ACK]
ESTABLISHED : 连接已建⽴

bind 函数 绑定 ip 地址与端⼝号,

函数头文件:
#include <sys/types.h>
#include <sys/socket.h>int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);函数功能:
绑定 ip 地址与端⼝号, 使得套接字可以接收客户端的连接请求参数:
sockfd : 套接字描述符
addr : 指向 sockaddr 结构体的指针, 包含了要绑定的 ip 地址和端⼝号
addrlen : 结构体 sockaddr 的长度返回值:
成功 : 0
失败 : -1, 并设置 errno 变量

在服务器绑定 ip 地址与端⼝号之后, 则需要让服务器 socket 套接字设置成被动监听状态,并
创建监听队列,这⾥需要调⽤ listen 函数

listen 函数 建⽴监听队列

函数头文件:
#include <sys/types.h>
#include <sys/socket.h>int listen(int sockfd, int backlog);函数功能:
建⽴监听队列, 并设置套接字的状态为 listen 状态, 表示可以接收连接请求参数:
sockfd : 套接字描述符
backlog : 监听队列的最大长度返回值:
成功 : 0
失败 : -1, 并设置 errno 变量

在服务器端调用 listen 函数之后, 则可以开始接收客户端的连接请求, 并创建新的套接字
用于数据传输, 这⾥需要调⽤ accept 函数

accept 函数 接受连接, 建⽴三次握⼿, 并创建新的⽂件描述符, ⽤于数据传输

函数头文件:
#include <sys/types.h>
#include <sys/socket.h>int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);函数功能:
接受连接, 建⽴三次握⼿, 并创建新的⽂件描述符, ⽤于数据传输参数:
sockfd : 套接字描述符
addr : 指向 sockaddr 结构体的指针, 用于返回客户端的 ip 地址和端⼝号
addrlen : 指向 socklen_t 类型的指针, 用于返回 sockaddr 结构体的长度返回值:
成功 : 新的套接字描述符
失败 : -1, 并设置 errno 变量

在服务器端调用 accept 函数之后, 则可以接收客户端的连接请求, 并创建新的套接字用于数据
传输, 调⽤ recv 和 send 函数进行数据传输

// todo TCP服务端程序 循环接收客户端数据,将数据回传
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <unistd.h>#define N 128//初始化socket
int  init_socket(char *ip,char *port){int init_socket_fd= socket(AF_INET,SOCK_STREAM,0);if (init_socket_fd==-1){printf("init_socket err");exit(EXIT_FAILURE);}struct sockaddr_in server_addr;socklen_t len=sizeof(server_addr);bzero(&server_addr,len);server_addr.sin_family=AF_INET;inet_aton(ip,&server_addr.sin_addr);server_addr.sin_port= htons(atoi(port));int bind_ret= bind(init_socket_fd,(struct sockaddr*)&server_addr,len);if (bind_ret == -1) {printf("bind error\n");exit(EXIT_FAILURE);}int listen_ret= listen(init_socket_fd,10);if (listen_ret == -1) {printf("listen error\n");exit(EXIT_FAILURE);}return init_socket_fd;
}//客户端发送消息
int  Server_Send_data(int clientFD,char* msg){strcat(msg,"-回传");int server_send_len=send(clientFD,msg,strlen(msg),0);if (server_send_len == -1) {printf("send error\n");exit(EXIT_FAILURE);}if (server_send_len == 0) {printf("客户端关闭连接\n");return -1;}printf("发送给客户端数据:[%s]\n",msg);return 0;
}//接收数据
int Server_Receive_data(int clientFD){while (1){//接收-使用新的文件描述符char recv_buf[N];bzero(recv_buf, sizeof(recv_buf));int recv_len = recv(clientFD, recv_buf, sizeof(recv_buf), 0);if (recv_len == -1) {printf("recv error\n");exit(EXIT_FAILURE);}if (recv_len == 0) {printf("客户端关闭连接\n");break;}if (strncmp(recv_buf, "exit", 4) == 0) {printf("客户端退出通信\n");close(clientFD);break;}printf("收到客户端消息:|%s|\n",recv_buf);Server_Send_data(clientFD, recv_buf);}return 0;
}int main(){int socket_fd = init_socket("172.17.140.183","8080");struct sockaddr_in cli_addr;socklen_t cli_len=sizeof(cli_addr);//获取客户端连接int clientFD= accept(socket_fd,(struct sockaddr*)&cli_addr,&cli_len);if (clientFD == -1){printf("accept error\n");exit(EXIT_FAILURE);}printf("连接 ip:%s, port:%d\n",inet_ntoa(cli_addr.sin_addr),ntohs(cli_addr.sin_port));//接收数据Server_Receive_data(clientFD);//关闭连接close(clientFD);
}

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

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

相关文章

构建查询洞察 UI

本文字数&#xff1a;2631&#xff1b;估计阅读时间&#xff1a;7 分钟 作者&#xff1a;Bucky Schwarz 本文在公众号【ClickHouseInc】首发 我们最近发布了 Query Insights 的初步实现&#xff0c;为 ClickHouse Cloud 用户提供了一种便捷的方法来查看和解释查询日志。该功能对…

MySQL索引、事务(数据库管理与高可用)

一、索引的概念 索引&#xff1a;排序的列表&#xff0c;对数据进行快速的查询&#xff1b; 针对不同的产品需求&#xff0c;或者不同的数据库结构&#xff0c;会创建不同的索引&#xff1b; 1&#xff1a;普通索引&#xff08;默认索引&#xff09; 2&#xff1a;唯一索引…

推荐一个酷炫高逼格的服务器探针的监控工具,免费开源(附源码)

背景 作为一名攻城狮&#xff0c;面对各种服务器内存飙高、CPU猛增、磁盘打满等等服务器问题&#xff0c;可谓是伤透了我们的心。 不仅要开发&#xff0c;还要处理这些问题&#xff0c;大把的时间浪费了&#xff0c;这时候一个好的全面的监控工具尤为重要了。 所以&#xff…

C++基础知识:函数重载相关注意事项:1.引用作为重载条件,2.2.函数重载遇见函数默认参数。

1.引用作为重载条件 #include<iostream>using namespace std;//1.引用作为重载的条件 //int 和 const int 类型不同&#xff0c;所以可以作用重载条件 void fn(int &a) //int &a10;不合法 //10放在了常量区&#xff0c;而引用要么在栈区&#xff0c;要么在堆区{…

Unity打包设置

1.Resolution and Presentation (分辨率和显示) Fullscreen Window (全屏窗口): 应用程序将以全屏窗口模式运行&#xff0c;但不会独占屏幕。适用于想要全屏显示但仍需访问其他窗口的情况。 Resizable Window (可调整大小的窗口): 允许用户调整应用程序窗口的大小。适用于窗口…

保险丝(常见元器件及电路基础知识)

分类&#xff1a;简单分为熔断式和非熔断式 电压&#xff1a;保险丝的额定电压是指它的公称额定电压, 通常就是保险丝断开后能够承受的最大电压值保险丝通电时两端所承受的电压大大小于其额定电压&#xff0c;因此额定电压基本上无关紧要。 电流&#xff1a; PFC为功率因数矫…

昇思25天学习打卡营第三十四天|Jack578

昇思25天学习打卡营第三十四天|Jack578 一、数据集Dataset&#xff08;一&#xff09;数据集加载&#xff08;二&#xff09;数据集迭代&#xff08;三&#xff09;数据集常用操作 一、数据集Dataset 数据是深度学习的基础&#xff0c;MindSpore提供基于Pipeline的数据引擎&am…

项目管理中的常用工件(二):可视化工件

项目管理中的常用工件&#xff08;二&#xff09;&#xff1a;可视化工件 亲和图&#xff08;affinity diagram&#xff09;因果图&#xff08;cause-and-effect diagram&#xff09;直方图&#xff08;histogram&#xff09;流程图&#xff08;flowchart&#xff09;散点图&am…

[练习]如何使用递归算法?

&#x1f3a5; 个人主页&#xff1a;Dikz12&#x1f525;个人专栏&#xff1a;算法(Java)&#x1f4d5;格言&#xff1a;吾愚多不敏&#xff0c;而愿加学欢迎大家&#x1f44d;点赞✍评论⭐收藏 目录 1. 递归概述 2.汉诺塔问题 题目描述​编辑 题解 代码实现 3…

mac M1安装Roop教程及所遇到的问题

1.安装miniconda&#xff0c;下载地址&#xff1a; 按 Python 版本划分的最新 Miniconda 安装程序链接&#xff1a;https://docs.anaconda.com/miniconda/miniconda-other-installer-links/ 下载后直接默认安装即可。 我用的是&#xff1a;Python3.10对应的Miniconda 2.下载…

7月26日JavaSE学习笔记

反射 Java是面向对象的&#xff0c;有对象必须先有类&#xff0c; 有static修饰类的属性和方法&#xff1b;在Java中存储了类的内容&#xff0c;这个内容也应该是一个对象&#xff1b;Java中每一个用到的类都会加载一块内存&#xff0c;这每一块内存都是一个对象&#xff1b;这…

php+bootstrap 编写简易的步骤进度条

个人笔记记录&#xff0c;步骤进度条。 版本一&#xff1a; 初始版本&#xff0c; 当前版本单纯的根据bootstrapCSS进行完成的简易版本。这个比较简单 样式我放在了最底下。 <div class"form-group steps"><div class"steps-height"><di…

txt格式单词导入有道词典生词本 (java代码方式)

txt格式单词导入有道词典生词本 (java代码方式) 首先要求txt文档里单词的格式&#xff0c;大概需要像这种&#xff1a; 每行是一个单词&#xff0c;格式为&#xff1a;英文单词空格词性单词意思。 注意 导出单词本的名字就是你 txt 文件的名字 我这里是 公共英语三级 单词本 …

IAR使用调试详解

目录 1 IAR功能介绍 1.1 File文件菜单 1.2 Edit编辑菜单 1.3 View视图菜单 1.4 Projcet工程菜单 1.5Debug调试菜单 1.6 Disassembly反汇编菜单 1.7 Simulator下载调试工具 1.8 Tools工具菜单 1.9 Window窗口菜单 1.10 Help帮助菜单 2 IAR设置 2.1 插入/编辑模板 2…

如何选择最佳的云盘检测方案?

橡胶密封圈是一种用于填塞、密封或隔离物体之间空隙的圆形零件&#xff0c;通常由橡胶或类似材料制成。这些密封圈通常用于工程、机械或汽车领域&#xff0c;以防止液体、气体或其他物质泄漏。 橡胶密封圈通常对多种化学物质具有良好的耐腐蚀性能&#xff0c;使其适用于各种环…

一些关于颜色的网站

欢迎来到 破晓的历程的 博客 ⛺️不负时光&#xff0c;不负己✈️ 1、中国传统色 2、网页颜色选择器 3、渐变色网站 4、多风味色卡生成 5、波浪生成 6、半透明磨砂框 色卡组合

T-CNN——利用张量 CNN 增强缺陷检测

1. 摘要 缺陷检测是制造业中一个重要而具有挑战性的问题。本研究引入了张量卷积神经网络&#xff08;T-CNN&#xff09;&#xff0c;并在罗伯特-博世制造工厂生产的超声波传感器组件缺陷检测的实际应用中验证了其性能。与同类 CNN 模型相比&#xff0c;作者的量子启发 T-CNN 通…

【后端开发实习】Python基于Quart框架实现SSE数据传输

Python基于Quart框架实现SSE数据传输 前言SSE简介理论分析代码实现 前言 在类似Chatgpt的应用中要实现数据的流式传输&#xff0c;模仿实现打字机效果&#xff0c;SSE是不二之选。传统的Flask框架不能满足异步处理的要求&#xff0c;没有异步处理就很难实现实时交互的需求&…

C++客户端Qt开发——Qt窗口(对话框)

5.对话框 ①对话框介绍 对话框是GUI程序中不可或缺的组成部分。一些不适合在主窗口实现的功能组件可以设置在对话框中。对话框通常是一个顶层窗口&#xff0c;出现在程序最上层&#xff0c;用于实现短期任务或者简洁的用户交互。Qt常用的内置对话框有&#xff1a;QFiledialog…

【第四天】计算机网络知识 HTTP1.0,HTTP1.1与HTTP2.0的区别 HTTP3.0

HTTP1.0&#xff0c;HTTP1.1与HTTP2.0的区别 HTTP1.0 默认是短链接&#xff0c;可以强制开启长连接。HTTP1.1默认长连接。HTTP2.0采用多路复用。 HTTP1.0&#xff1a; 默认使用短链接&#xff0c;每次请求都需要建立一个TCP连接。它可以设置&#xff1a;Connection: keep-aliv…