TCP(TCP客户端、服务器如何通信)

一、TCP介绍

TCP的特点:

  1. 面向连接的协议:TCP是一种可靠的、面向连接的协议,在通信之前需要建立连接,以确保数据的可靠传输。这意味着在传输数据之前,发送方和接收方之间需要建立一条可靠的连接通道。
  2. 流式协议:TCP将数据看作是一连串的字节流,而不是独立的数据包。这意味着在传输过程中,数据可以被分割成多个数据包进行传输,并在接收端进行重新组装。
  3. 可靠传输:TCP通过使用校验和、序列号和确认应答等机制,确保数据的可靠传输。如果在传输过程中发生错误或丢失,TCP会进行重传,以确保数据的完整性和准确性。
  4. 出错重传:当TCP接收到错误的数据包时,它会要求发送方重新发送该数据包,以确保数据的正确性。
  5. 确认应答:TCP使用确认应答机制来确保数据的可靠传输。当接收方收到一个数据包时,它会向发送方发送一个确认应答,表示已经收到了该数据包。
  6. 服务器被动连接,客户端主动连接:在TCP连接中,服务器通常处于被动状态,等待客户端的连接请求。而客户端则处于主动状态,负责发起连接请求。一旦连接建立成功,双方就可以进行数据传输。  

TCP与UDP的差异

TCP与UDP流程对比 

TCP编程流程 

服务器端流程:

  1. 创建套接字(socket):使用socket()函数创建一个套接字,指定使用的协议族(如IPv4或IPv6)和socket类型(如流式socket)。
  2. 绑定套接字(bind):使用bind()函数将套接字与服务器的网络信息(如IP地址和端口号)进行绑定。
  3. 监听套接字(listen):使用listen()函数将套接字设置为监听状态,准备接收客户端的连接请求。
  4. 接受连接(accept):使用accept()函数阻塞等待客户端的连接请求,并返回一个新的套接字用于与该客户端进行通信。
  5. 进行通信(recv/send):使用recv()send()函数与客户端进行数据的接收和发送。
  6. 关闭套接字(close):通信完成后,使用close()函数关闭套接字,释放资源。

客户端流程:

  1. 创建套接字(socket):与服务器端相同,使用socket()函数创建一个套接字。
  2. 连接服务器(connect):使用connect()函数向服务器发起连接请求,指定服务器的IP地址和端口号。
  3. 进行通信(send/recv):连接建立后,使用send()recv()函数与服务器进行数据的发送和接收。
  4. 关闭套接字(close):通信完成后,使用close()函数关闭套接字,释放资源。

 二、TCP编程-socket

#include <sys/types.h>
#include <sys/socket.h>
int socket(int domain, int type, int protocol);

功能:
    创建一个套接字,返回一个文件描述符
参数:
    domain:通信域,协议族
      AF_UNIX 本地通信
      AF_INET ipv4网络协议
      AF_INET6 ipv6网络协议
      AF_PACKET 底层接口
    type:套接字的类型
      SOCK_STREAM 流式套接字(tcp)
      SOCK_DGRAM 数据报套接字(udp)
      SOCK_RAW 原始套接字(用于链路层)
    protocol:附加协议,如果不需要,则设置为0

返回值:
    成功:文件描述符
    失败:‐1

 案例

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <stdlib.h>

int main(int argc, char const *argv[])
{
    //通过socket函数创建一个TCP套接字
    int sockfd;
    if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
    {
        perror("fail to socket");
        exit(1);
    }

    printf("sockfd = %d\n", sockfd);

    return 0;
}

这段代码演示了如何使用C语言创建一个TCP套接字。在代码中,首先通过socket()函数创建了一个套接字,指定了协议族为AF_INET(IPv4),套接字类型为SOCK_STREAM(TCP)。如果创建失败,则会输出错误信息并退出程序。如果创建成功,则会输出套接字的文件描述符。最后,程序返回0表示正常退出。

 执行结果

 三、TCP客户端-connect、send、recv

3.1 connect函数

#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
功能:
    给服务器发送客户端的连接请求
参数:
    sockfd:文件描述符,socket函数的返回值
    addr:要连接的服务器的网络信息结构体(需要自己设置)
    addrlen:add的长度
返回值:
    成功:0
    失败:-1

注意:

  1. connect建立连接之后不会产生新的套接字
  2. 连接成功后才可以开始传输TCP数据
  3. 头文件:#include 

3.2 send函数

#include <sys/types.h>
#include <sys/socket.h>
ssize_t send(int sockfd, const void *buf, size_t len, int flags);

功能:
    发送数据
参数:
    sockfd:文件描述符

客户端:
    socket函数的返回值
服务器:
    accept函数的返回值

     buf:发送的数据
     len:buf的长度
     flags:标志位
       0 阻塞
       MSG_DONTWAIT 非阻塞
 
返回值:
    成功:发送的字节数
    失败:-1

注意不能用TCP协议发送0长度的数据包

3.3 recv函数

#include <sys/types.h>

#include <sys/socket.h>
ssize_t recv(int sockfd, void *buf, size_t len, int flags);
功能:
    接收数据
参数:
    sockfd:文件描述符
客户端:socket函数的返回值
服务器:accept函数的返回值
    buf:保存接收到的数据
    len:buf的长度
    flags:标志位
      0 阻塞
      MSG_DONTWAIT 非阻塞
返回值:
    成功:接收的字节数
    失败:-1
如果发送端关闭文件描述符或者关闭进程,则recv函数会返回0

3.4 客户端code

使用windows下的网络调试助手作为服务器

客户端的程序 

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <string.h>

#define N 128

int main(int argc, char const *argv[])
{
    if(argc < 3)
    {
        fprintf(stderr, "Usage: %s [ip] [port]\n", argv[0]);
        exit(1);
    }

    //第一步:创建套接字
    int sockfd;
    if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
    {
        perror("fail to socket");
        exit(1);
    }

    //printf("sockfd = %d\n", sockfd);

    //第二步:发送客户端连接请求
    struct sockaddr_in serveraddr;
    socklen_t addrlen = sizeof(serveraddr);

    serveraddr.sin_family = AF_INET;
    serveraddr.sin_addr.s_addr = inet_addr(argv[1]);
    serveraddr.sin_port = htons(atoi(argv[2]));

    if(connect(sockfd, (struct sockaddr *)&serveraddr, addrlen) == -1)
    {
        perror("fail to connect");
        exit(1);
    }

    //第三步:进行通信
    //发送数据
    char buf[N] = "";
    fgets(buf, N, stdin);
    buf[strlen(buf) - 1] = '\0';
    if(send(sockfd, buf, N, 0) == -1)
    {
        perror("fail to send");
        exit(1);
    }

    //接收数据
    char text[N] = "";
    if(recv(sockfd, text, N, 0) == -1)
    {
        perror("fail to recv");
        exit(1);
    }

    printf("from server: %s\n", text);

    //第四步:关闭套接字文件描述符
    close(sockfd);

    return 0;
}

这段代码是一个简单的TCP客户端程序。它接收两个命令行参数:服务器的IP地址和端口号。然后,程序会执行以下步骤:

  1. 创建一个TCP套接字。
  2. 使用connect()函数向服务器发送连接请求。
  3. 从标准输入读取一行数据,并将其发送给服务器。
  4. 接收服务器返回的数据,并将其打印到标准输出。
  5. 关闭套接字文件描述符。

 执行结果

 

四、TCP服务器-bind、listen、accept 

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

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

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

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

4.2 bind函数

#include <sys/types.h>

#include <sys/socket.h>
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
功能:
    将套接字与网络信息结构体绑定
参数:
    sockfd:文件描述符,socket的返回值
    addr:网络信息结构体
      通用结构体(一般不用)
      struct sockaddr
      网络信息结构体 sockaddr_in
#include <netinet/in.h>
struct sockaddr_in
addrlen:
    addr的长度
返回值:
    成功:0
    失败:-1

 简单写个例子

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>

int main() {
    // 创建一个流式套接字
    int sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd == -1) {
        perror("socket");
        return -1;
    }

    // 初始化网络信息结构体
    struct sockaddr_in addr;
    addr.sin_family = AF_INET;
    addr.sin_port = htons(8080); // 绑定到8080端口
    addr.sin_addr.s_addr = htonl(INADDR_ANY); // 绑定到任意IP地址

    // 将套接字与网络信息结构体绑定
    socklen_t addrlen = sizeof(addr);
    if (bind(sockfd, (struct sockaddr *)&addr, addrlen) == -1) {
        perror("bind");
        return -1;
    }

    printf("Bind successful\n");

    // 关闭套接字
    close(sockfd);

    return 0;
}

 在这个实例中,我们首先使用socket()函数

#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
功能:
    阻塞等待客户端的连接请求
参数:
    sockfd:文件描述符,socket函数的返回值
    addr:接收到的客户端的信息结构体(自动填充,定义变量即可)
    addrlen:addr的长度
返回值:
    成功:新的文件描述符(只要有客户端连接,就会产生新的文件描述符,这个新的文件描述符专门与指定的客户端进行通信的)
    失败:-1

创建了一个流式套接字,然后初始化了一个sockaddr_in结构体,将套接字绑定到8080端口和任意IP地址。最后,我们使用bind()函数将套接字与网络信息结构体绑定,如果绑定成功,则会输出"Bind successful"。

 4.3 listen函数

#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
int listen(int sockfd, int backlog);
功能:
    将套接字设置为被动监听状态,这样做之后就可以接收到连接请求
参数:
    sockfd:文件描述符,socket函数返回值
    backlog:允许通信连接的主机个数,一般设置为5、10
返回值:
    成功:0
    失败:-1

4.4 accept函数

#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
功能:
    阻塞等待客户端的连接请求
参数:
    sockfd:文件描述符,socket函数的返回值
    addr:接收到的客户端的信息结构体(自动填充,定义变量即可)
    addrlen:addr的长度
返回值:
    成功:新的文件描述符(只要有客户端连接,就会产生新的文件描述符,这个新的文件描述符专门与指定的客户端进行通信的)
    失败:-1

 4.5 TCP服务器例子

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <string.h>

#define N 128

int main(int argc, char const *argv[])
{
    if(argc < 3)
    {
        fprintf(stderr, "Usage: %s [ip] [port]\n", argv[0]);
        exit(1);
    }

    //第一步:创建套接字
    int sockfd;
    if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
    {
        perror("fail to socket");
        exit(1);
    }

    //第二步:将套接字与服务器网络信息结构体绑定
    struct sockaddr_in serveraddr;
    socklen_t addrlen = sizeof(serveraddr);

    serveraddr.sin_family = AF_INET;
    serveraddr.sin_addr.s_addr = inet_addr(argv[1]);
    serveraddr.sin_port = htons(atoi(argv[2]));

    if(bind(sockfd, (struct sockaddr *)&serveraddr, addrlen) == -1)
    {
        perror("fail to bind");
        exit(1);
    }

    //第三步:将套接字设置为被动监听状态
    if(listen(sockfd, 10) == -1)
    {
        perror("fail to listen");
        exit(1);
    }

    //第四步:阻塞等待客户端的链接请求
    int acceptfd;
    struct sockaddr_in clientaddr;
    if((acceptfd = accept(sockfd, (struct sockaddr *)&clientaddr, &addrlen)) == -1)
    {
        perror("fail to accept");
        exit(1);
    }

    //打印连接的客户端的信息
    printf("ip:%s, port:%d\n", inet_ntoa(clientaddr.sin_addr), ntohs(clientaddr.sin_port));

    //第五步:进行通信
    //tcp服务器与客户端通信时,需要使用accept函数的返回值
    char buf[N] = "";
    if(recv(acceptfd, buf, N, 0) == -1)
    {
        perror("fail to recv");
    }

    printf("from client: %s\n", buf);

    strcat(buf, " *_*");
    if(send(acceptfd, buf, N, 0) == -1)
    {
        perror("fail to send");
        exit(1);
    }

    //关闭套接字文件描述符
    close(acceptfd);
    close(sockfd);

    return 0;
}

 

  1. 创建一个TCP套接字。
  2. 将套接字与服务器的IP地址和端口号绑定。
  3. 将套接字设置为被动监听状态,等待客户端的连接请求。
  4. 阻塞等待客户端的连接请求,并接受连接。
  5. 打印连接的客户端的IP地址和端口号。
  6. 接收客户端发送的数据,并将其打印出来。
  7. 向客户端发送数据。
  8. 关闭与客户端的连接,并关闭套接字。

 执行结果

4.6  close关闭套接字

  1. 使用close函数即可关闭套接字 关闭一个代表已连接套接字将导致另一端接收到一个0长度的数据包
  2. 做服务器时 1>关闭监听套接字将导致服务器无法接收新的连接,但不会影响已经建立的连接 2>关闭accept返回的已连接套接字将导致它所代表的连接被关闭,但不会影响服务器 的监听
  3. 做客户端时 关闭连接就是关闭连接,不意味着其他

 

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Spring Cloud架构进化实操:Eureka、Apollo、OpenFeign、Ribbon、Zuul组件

文章目录 前言一、引出二、服务注册与发现2.1 创建Eureka注册中心2.1.1 引入pom依赖2.1.2 配置yaml2.1.3 启动服务21.4 测试访问 2.2 创建服务提供者2.2.1 配置yaml2.2.2 启动服务2.2.3 测试访问 2.3 创建服务消费者2.3.1 服务提供者接口2.3.2 服务消费者调用接口 三、负载均衡…

Docker的私有仓库部署-Harbor

目录 一. Docker原生私有仓库 Registry 1. Registry 的介绍 2. Registry 的部署过程 二. Registry 的升级——Habor 1. Harbor 简介 2. Harbor 特性 3. Harbor 的构成 4. Harbor 部署 4.1 部署 Docker-Compose 服务 4.2 部署 Harbor 服务 4.2.1 下载或上传 Harbor…

18_Scala面向对象编程trait

文章目录 trait1.定义trait2.向类中混入特质2.1没有父类2.2有父类 3.动态混入3.1动态混入查询功能到公司业务中 4.父类&#xff0c;子类&#xff0c;特质初始化优先级5.Scala功能执行顺序6.常用API trait –特质的学习需要类比Java中的接口&#xff0c;源码编译之后就是interf…

三种方法解决:检测到在集成的托管管道模式下不适用的 ASP.NET 设置

几天前配置一个IIS环境的网站时,出现500错误。根据错误提示,很快把问题解决了,现记录一下,希望能帮到遇到同样问题的网友。 问题描述 (点击图片放大) 应用程序“DEFAULT WEB SITE”中的服务器错误Internet Information Services 7.5错误摘要 HTTP 错误 500.24 - Interna…

抓包证书安装到安卓7.0+手机

前言: 首先理解一下,这个不只是证书到浏览器,而是抓包证书到安卓7.0+手机上的文章; 还有一点区分,在浏览器上装的证书,只是让抓包工具可以抓取手机浏览器的包,而不是抓取手机app上的包; 如果你的证书只是简单的在浏览器下进行安装,那么你的手机app是走不了代理网络的…

视频教程下载:为 GPTs 商店构建 10 个 GPTs获得被动收入

欢迎来到 AI 驱动的内容创作新时代 - GPT 商店。这门综合课程是您成为定制和利用 GPT 模型解决多样化应用的专家的路线图。无论你是错过了应用商店革命的初始浪潮还是乘着它取得了成功&#xff0c;这都是你站在下一个重大数字飞跃前沿的机会。 课程模块&#xff1a; - 介绍 Ch…

Dragonfly 拓扑的路由算法

Dragonfly 拓扑的路由算法 1. Dragonfly 上的路由 (1)最小路由(2)非最小路由 2. 评估 Dragonfly 拓扑的路由算法 John Kim, William J. Dally 等人在 2008 年的 ISCA 中提出技术驱动、高度可扩展的 Dragonfly 拓扑。而文章中也提到了 针对 Dragonfly 拓扑的路由算法。本文对…

java-函数式编程-语法

目录 1、函数表现形式 分类 lambda表达式 参数类型可以全写&#xff0c;也可以全不写&#xff0c;但不能一部分写&#xff0c;一部分不写lambda 的省略策略&#xff1a;凡是可推导&#xff0c;都可以省略

【c++算法篇】双指针(上)

&#x1f525;个人主页&#xff1a;Quitecoder &#x1f525;专栏&#xff1a;算法笔记仓 朋友们大家好啊&#xff0c;本篇文章我们来到算法的双指针部分 目录 1.移动零2.复写零3.快乐数4.盛水最多的容器 1.移动零 题目链接&#xff1a;283.移动零 题目描述&#xff1a; 算法…

Python量化炒股的数据信息获取—获取上市公司分红送股数据信息

Python量化炒股的数据信息获取—获取上市公司分红送股数据信息 上市公司分红送股数据&#xff0c;都存放在STK_XR_XD表中&#xff0c;该表保存在finance包中。要查看表中的数据信息&#xff0c;需要使用query()函数。 单击聚宽JoinQuant量化炒股平台中的“策略研究/研究环境”…

微服务---gateway网关

目录 gateway作用 gateway使用 添加依赖 配置yml文件 自定义过滤器 nacos上的gateway的配置文件 我们现在知道了通过nacos注册服务&#xff0c;通过feign实现服务间接口的调用&#xff0c;那对于不同权限的用户访问同一个接口&#xff0c;我们怎么知道他是否具有访问的权…

Grafana:云原生时代的数据可视化与监控王者

&#x1f407;明明跟你说过&#xff1a;个人主页 &#x1f3c5;个人专栏&#xff1a;《Grafana&#xff1a;让数据说话的魔术师》 &#x1f3c5; &#x1f516;行路有良友&#xff0c;便是天堂&#x1f516; 目录 一、引言 1、Grafana简介 2、Grafana的重要性与影响力 …

开发体育赛事直播平台,研发技术选型与架构设计实现方案

本文将深入探讨“东莞梦幻网络科技”现成体育直播源码的技术实现方案&#xff0c;如何为用户提供流畅、互动、个性化的观赛体验。 一、技术栈选择&#xff1a;强强联合的基石1、后端开发&#xff1a;采用Java与PHP作为主要开发语言。Java以其强大的企业级应用支持&#xff0c;保…

双向冒泡法,可以只求最大最小值

int BiBubbleSort(int Arr[],int n,int maxnum){int left0,rightn-1;int i;bool notDone true;int temp;if(n<2)return -1;while(left<right&&notDone){ notDone false; //设置未发生交换标志 for(ileft;i<right;i){if(Arr[i]>Arr[i1]){//swap(Arr[…

初识指针(1)<C语言>

前言 指针是C语言中比较难的一部分&#xff0c;大部分同学对于此部分容易产生“畏难情结”&#xff0c;但是学习好这部分对C语言的深入很大的帮助&#xff0c;所以此篇主要以讲解指针基础为主。 指针概念 变量创建的本质就是在内存中申请空间&#xff0c;找到这个变量就需要地址…

tiny-Tcmalloc(高并发内存池)

项目地址&#xff08;绝对可运行&#xff09; 一. 初识高并发内存池 1、项目介绍 当前项目是实现一个高并发的内存池&#xff0c;它的原型是google的一个开源项目tcmalloc&#xff0c;tcmalloc全称Thread-Caching Malloc&#xff0c;即线程缓存的malloc&#xff0c;实现了高…

Pytorch 实现 GAN 对抗网络

GAN 对抗网络 GAN&#xff08;Generative Adversarial Network&#xff09;对抗网络指的是神经网络中包括两个子网络&#xff0c;一个用于生成信息&#xff0c;一个用于验证信息。下面的例子是生成图片的对抗网络&#xff0c;一个网络用于生成图片&#xff0c;另一个网络用于验…

Bookends for Mac:文献管理工具

Bookends for Mac&#xff0c;一款专为学术、研究和写作领域设计的文献管理工具&#xff0c;以其强大而高效的功能深受用户喜爱。这款软件支持多种文件格式&#xff0c;如PDF、DOC、RTF等&#xff0c;能够自动提取文献的关键信息&#xff0c;如作者、标题、出版社等&#xff0c…

Unity 性能优化之静态批处理(三)

提示&#xff1a;仅供参考&#xff0c;有误之处&#xff0c;麻烦大佬指出&#xff0c;不胜感激&#xff01; 文章目录 前言一、静态批处理是什么&#xff1f;二、使用步骤1.勾选Static Batching2.测试静态合批效果 三、静态合批得限制1、游戏对象处于激活状态。2、游戏对象有一…

Kannala-Brandt 鱼眼相机模型

最近在学习 ORB-SLAM3 的源代码&#xff0c;并模仿、重构了相机模型的实现 在学习的过程中发现针孔相机 (Pinhole) 与鱼眼相机 (Fisheye) 都有畸变参数&#xff0c;但是鱼眼相机无法使用 cv::undistort 函数去畸变 在对鱼眼相机的深度归一化平面进行可视化后&#xff0c;发现…