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

news2024/12/25 13:52:02

        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.0
    serveraddr.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.coloradmin.cn/o/1362660.html

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

相关文章

学习笔记——C++中的循环结构 while语句

while循环语句 作用&#xff1a;满足循环条件&#xff0c;执行循环语句 语法&#xff1a;while&#xff08;循环条件&#xff09;{循环语句} 解释&#xff1a;只要循环条件的结果为真&#xff0c;就执行循环语句 以打印0-9这十个数字为例&#xff0c;特别需要注意的是&…

MySQL之视图索引执行计划

目录 一.视图 二.执行计划 2.1.什么是执行计划 2.2.执行计划的作用 三.使用外连接、内连接和子查询进行举例 四.思维导图 好啦今天就到这里了哦&#xff01;&#xff01;&#xff01;希望能帮到你哦&#xff01;&#xff01;&#xff01; 一.视图 含义 &#xff1a;在数…

Nginx快速入门:Nginx实现高可用|结合keepalived实现主备节点(九)

0. 引言 在生产中我们要尽可能避免单点故障&#xff0c;nginx也不例外&#xff0c;因此搭建主备节点必不可少&#xff0c;今天我们来学习下如何利用keepalived实现主备 1. keepalived简介 keepalived 是一个LINUX系统中开源的负载均衡和故障转移软件&#xff0c;它主要用于高…

解决 POST http://x.x.x.x:8000/aaa/ net::ERR_CONNECTION_TIMED_OUT

记录一下我遇到的问题和解决办法 我的项目前后端分离&#xff0c;在前端用的vue访问后端时连接不上后端&#xff0c;一切操作都触发不了后端&#xff0c;数据也传不到后端去&#xff1b; 原因&#xff1a;url有问题&#xff0c;url地址写的不是本机&#xff0c;所以导致连接超…

Nginx快速入门:worker、master进程的作用和热部署原理(十)

0. 引言 我们通过查询nginx进程&#xff0c;可以发现nginx有两个进程&#xff1a;worker和master。一个程序启动了两个进程&#xff0c;那么这两个进程的作用和区别是什么呢&#xff1f;nginx又是如何利用这两个进程进行工作的呢&#xff1f;nginx不停机热部署又是如何实现的&…

如何使用UUP从windows更新服务器下载windows10原版镜像

UUP是指Windows 10中的一种更新技术&#xff0c;全称为Unified Update Platform。UUP的目标是提供更快、更高效的更新体验&#xff0c;它通过增量更新的方式来更新操作系统&#xff0c;只下载和安装实际变化的部分&#xff0c;而不是整个更新包。这样可以节省带宽和时间&#x…

LLM漫谈(二)| QAnything支持任意格式文件或数据库的本地知识库问答系统

一、QAnything介绍 QAnything (Question and Answer based on Anything) 是致力于支持任意格式文件或数据库的本地知识库问答系统&#xff0c;可断网安装使用。 您的任何格式的本地文件都可以往里扔&#xff0c;即可获得准确、快速、靠谱的问答体验。 目前已支持格式: PDF&…

Mac Parallels19.1.0 Install CentOS7.9

0、资源准备 # centos7.9镜像一份 链接: https://pan.baidu.com/s/1acIjUnsTGhk_2cYCZLSoGg?pwd6666 提取码: 6666 --来自百度网盘超级会员v7的分享1、打开PD 2、选择镜像进行安装 指定镜像名称 创建 进行密码设置 安装目的地点开后直接点击完成 网络和主机名称 开…

SonarQube 漏洞扫描

SonarQube 漏洞扫描 一、部署服务 1.1 docker方式部署 #安装docker curl -L download.beyourself.org.cn/shell-project/os/get-docker-latest.sh | sh yum install -y docker-compose #进去输入:set paste可以保证不穿行 [rootlocalhost sonar]# vim docker-compose.yml v…

什么猫粮便宜又好?什么牌子的主食冻干猫粮健康又实惠?

冻干对于猫咪的好处&#xff0c;相信许多养猫的朋友们都非常了解。冻干猫粮是将含有水分的新鲜肉在低温且真空的环境中急速冻结&#xff0c;使得冰直接升华成水蒸气&#xff0c;从而排掉水分&#xff0c;让肉变得干燥。这种方式可以保证蛋白质和维生素等物质的生物活力&#xf…

Zabbix实现网络响应超时监控

一、简介 在IT运维管理过程中&#xff0c;网络响应超时是比较常见的故障之一。尽管网络响应超时的原因多种多样&#xff0c;解决方案各不相同&#xff0c;但归根结底解决故障的首要前提是发现问题。 在网络超时监控方面&#xff0c;Zabbix能够实时捕获并响应网络设备的超时事…

技术方向:比较与选择

针对未入行、刚入行、工作好几年依然不停切换择业方向、长期技术无法突破的人。 1 技术栈 一切的开始其实是对编程的兴趣&#xff0c;兴趣指引你跨过所有障碍。 编程语言是基础&#xff0c;编程语言之上&#xff0c;发展为两个主要方向&#xff0c;服务和大数据。 2 编程语言…

【一】达梦数据库安装和使用-Windows

达梦数据库安装和使用-Windows 简介&#xff1a; 新能源行业关系到国计民生&#xff0c;保障能源安全的意识不容懈怠&#xff0c;近些年各行各业都在推进数字化进程&#xff0c;能源行业在国家3060双碳目标提出之后更是进行的如火如荼&#xff0c;能源互联网方面在数字化的同时…

软件测试|Python函数参数之必传参数、默认参数、可变参数、关键字参数的详细使用

在Python中&#xff0c;函数参数是定义在函数头部的变量&#xff0c;用于接收传递给函数的数据。Python函数参数有四种类型&#xff1a;必传参数、默认参数、可变参数和关键字参数。每种类型都有不同的使用方式和适用场景。本文将详细介绍这四种函数参数的使用方法。 Python函…

如何做一个炫酷的Github个人简介(3DContribution)

文章目录 前言3D-Contrib第一步第二步第三步第四步第五步第六步 前言 最近放假了&#xff0c;毕设目前也不太想做&#xff0c;先搞一点小玩意玩玩&#xff0c;让自己的github看起来好看点。也顺便学学这个action是怎么个事。 3D-Contrib 先给大家看一下效果 我的个人主页&am…

RH850P1X芯片学习笔记-A/D Converter (ADCF)

文章目录 Features of RH850/P1x-C ADCFNumber of UnitsRegister Base AddressClock SupplyInterrupts and DMAHardware ResetExternal Input/Output SignalsVirtual Channel OverviewFunctional OverviewBlock DiagramPhysical Channels, Virtual Channels and Scan Groups Re…

【airsim】python控制airsim

使用airsim 1.8.1编译完成&#xff0c;进过block项目在cpp测试后&#xff0c;开始踩坑使用python。 使用AirSim\PythonClient\setup.py或者pip安装airsim。 python setup.py install或者 pip install airsim此时&#xff0c;windows电脑的环境信息 (air_py38) D:\code\Gith…

UV胶水与聚氯乙烯PVC材料的塑料粘接,效果如何?

UV胶水可以与聚氯乙烯PVC很好地粘接。 PVC是一种常见的塑料材料&#xff0c;UV胶水通常对PVC具有良好的粘接性能。UV胶水可以在紫外线照射下迅速固化&#xff0c;形成坚固的粘接&#xff0c;因此通常被用于PVC制品的粘接和修复。 UV胶水与PVC粘接的优点&#xff1a; 1. 快速固…

Java并发集合详解

第1章&#xff1a;引言 大家好&#xff0c;我是小黑&#xff0c;在这篇博客中&#xff0c;咱们将一起深入探索Java中的并发集合。多线程编程是一个不可或缺的部分&#xff0c;它能让程序运行得更快&#xff0c;处理更多的任务。但同时&#xff0c;多线程也带来了一些挑战&…

Linux 编译安装 Nginx

目录 一、前言二、四种安装方式介绍三、本文安装方式&#xff1a;源码安装3.1、安装依赖库3.2、开始安装 Nginx3.3、Nginx 相关操作3.4、把 Nginx 注册成系统服务 四、结尾 一、前言 Nginx 是一款轻量级的 Web 服务器、[反向代理]服务器&#xff0c;由于它的内存占用少&#xf…