Socket 编程 TCP

news2025/6/4 10:24:51

目录

1. TCP socket API 详解

1.1 socket

1.2 bind

1.3 listen

1.4 accept

1.5 read&&write

1.6 connect

1.7 recv

1.8 send

1.9 popen

1.10 fgets

2. EchoServer

3. 多线程远程命令执行

4. 引入线程池版本翻译

5. 验证TCP - windows作为client访问Linux

6. connect的断线重连


1. TCP socket API 详解

下面介绍程序中用到的socket API,这些函数都在sys/socket.h中。

1.1 socket

// main socket
NAME
       socket - create an endpoint for communication

SYNOPSIS
       #include <sys/types.h>          /* See NOTES */
       #include <sys/socket.h>

       int socket(int domain, int type, int protocol);
// domain:域,表示要进行网络通信,AF_INET
// type:套接字类型,TCP就填SOCK_STREM(流式套接字),UDP就填SOCK_DGRAM(数据包套接字)
// protocol:设置为0,默认就是TCP Socket

// 返回值
RETURN VALUE
       On success, a file descriptor for the new socket is returned.  On error, -1 is returned, and errno is set appropriately.
// 跟UDP一样,成功就是新的文件描述符,失败就返回-1

1.2 bind

// man bind
NAME
       bind - bind a name to a socket

SYNOPSIS
       #include <sys/types.h>          /* See NOTES */
       #include <sys/socket.h>

       int bind(int sockfd, const struct sockaddr *addr,
                socklen_t addrlen);
// 填充当前服务器本地的socket信息,文件信息和网络信息关联起来。

// 返回值
RETURN VALUE
       On success, zero is returned.  On error, -1 is returned, and errno is set appropriately.
// 如果成功了0被返回,否则-1被返回

1.3 listen

TCP的有连接和UDP的无连接

UDP叫面向数据报,无连接的,TCP叫面向字节流,有连接的,怎么证明UDP是无连接的呢?

只要我客户端一启动,随时就可以向服务器发消息,所以我们直接创建套接字之后,直接sendto就可以向服务器发消息了,没有所谓的连接,而TCP叫做面向连接的协议,即有连接,所以我们对应的TCP,要面向连接,如果我们的客户端(c)连接服务器(s),也叫cs模式,如果c向s发起对应的请求,就先的建立连接,连接建立成功才能通信,这就是TCP的特点,而客户点要连服务器,是不是要求这个服务器随时随地等待被连接。所以TCP是建立连接的,一定会存在协商的过程,所以tcp需要将socket设置为监听状态。

// man listen
NAME
       listen - listen for connections on a socket
       // 意思就是再等别人随时随地来连接我

SYNOPSIS
       #include <sys/types.h>          /* See NOTES */
       #include <sys/socket.h>

       int listen(int sockfd, int backlog);
// sockfd:设置的文件描述符
// backlog:服务器所对应的操作系统中所对应的全连接的个数,不建议为0,也不能太长,8,4,14,32都可以,具体设置多大,按照自己的需求来做,backlog是积压的意思,就类似于排队,但是排队也不能排太长

// 返回值
RETURN VALUE
       On success, zero is returned.  On error, -1 is returned, and errno is set appropriately.
// 成功了返回0,失败了-1被返回

1.4 accept

// man accept
NAME
       accept, accept4 - accept a connection on a socket

SYNOPSIS
       #include <sys/types.h>          /* See NOTES */
       #include <sys/socket.h>

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

       #define _GNU_SOURCE             /* See feature_test_macros(7) */
       #include <sys/socket.h>

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

// 从指定的文件描述符当中获取新连接,获取新的客户端,当别人连接你的时候,第一件是就是要知道谁来连我,
// sockfd:套接字
// addr && addrlen:输出型参数,其实就相当于给我们传进来缓冲区,当我们再进行获取新连接,这个上面所对应的sockaddr的客户端的地址就会直接给我们返回。这两个参数就等同于recvfrom,recvfrom的后两个参数。

// 返回值
RETURN VALUE
       On success, these system calls return a nonnegative integer that is a file descriptor for the accepted socket.  On error, -1 is returned, errno is set appropriately, and addrlen is left unchanged.
// 如果成功就返回非0,失败-1返回。没有人连接就会阻塞。
// 成功后,这些系统调用将返回一个非负整数,该整数是所接受套接字的文件描述符,
// 正真提供服务的是由accept的返回值来定的。而sockfd是专门用来获取新连接的。

1.5 read&&write

读和写我们用的是read和write,因为我们的accept返回的是一个文件描述符,而且TCP是面向字节流的,管道和文件也是面向字节流的,所以TCP套接字,在我们获得了新的文件描述符之后,TCP套接字往后的处理都跟我们之前学习文件跟管道一样,我们可以调用read和write这样的文件接口来直接对网络进行读和写。

UDP不敢用read和write,因为UDP是面向数据报的,报文类型上是不一样的,至于什么是面向数据报,什么是面向字节流,单纯在应用层上编写代码是感受不到的,到TCP和UDP的原理时才能感受的到。

void HandlerRequest(int sockfd) // TCP也是全双工
    {
        char inbuffer[4096];
        while(true)
        {
            size_t n = ::read(sockfd,inbuffer,sizeof(inbuffer)-1);
            if(n > 0)
            {
                LOG(LogLevel::INFO) << inbuffer;
                inbuffer[n] = 0;
                std::string echo_str = "server echo# ";
                echo_str += inbuffer;

                ::write(sockfd,echo_str.c_str(),echo_str.size());
            }
        }
    }
    void Start()
    {
        _isrunning = true;
        while(_isrunning)
        {
            // 不能直接读数据
            // 1. 获取新连接
            struct sockaddr_in peer;
            socklen_t peerlen;
            int sockfd = ::accept(_listensockfd,CONV(&peer),&peerlen);
            if(sockfd < 0)
            {
                LOG(LogLevel::WARNING) << "accept error: " << strerror(errno);
                continue;// 失败了不是报错,而是回头重新来
            }

            // 获取连接成功了
            LOG(LogLevel::INFO) << "accept success,sockfd id : " << sockfd;

            // version-0
            HandlerRequest(sockfd);
        }
        _isrunning = false;
    }

1.6 connect

// man connect
NAME
       connect - initiate a connection on a socket

SYNOPSIS
       #include <sys/types.h>          /* See NOTES */
       #include <sys/socket.h>

       int connect(int sockfd, const struct sockaddr *addr,
                   socklen_t addrlen);
// sockfd:客户端所创建的套接字
// addr && addrlen:访问目标服务器所对应的IP地址和端口号,

// 返回值
RETURN VALUE
       If the connection or binding succeeds, zero is returned.  On error, -1 is returned, and  errno  is  set  appropri‐ately.
// 创建套接字成功,返回值就是0,否则就是-1.

1.7 recv

// man recv
NAME
       recv, recvfrom, recvmsg - receive a message from a socket

SYNOPSIS
       #include <sys/types.h>
       #include <sys/socket.h>

       ssize_t recv(int sockfd, void *buf, size_t len, int flags);
// 从指定的文件描述符读取数据到buffer,长度是len,flage就是读取的标志位,默认设置为0就可以了,因为在我们当前的代码中,设置为0就表示的是阻塞的,他比read就多了一个flags,这是专门在套接字当中为我们处理IO接口,

1.8 send

// man send
NAME
       send, sendto, sendmsg - send a message on a socket

SYNOPSIS
       #include <sys/types.h>
       #include <sys/socket.h>

       ssize_t send(int sockfd, const void *buf, size_t len, int flags);
// 把特定缓冲区指定长度通过该文件描述符发出去,他也多了一个flags参数。

不管是read、write、recv还是end,他们的返回值的意思都是一样的,所以可以平滑过渡。

1.9 popen

// man popen
#include <stdio.h>
NAME
       popen, pclose - pipe stream to or from a process

SYNOPSIS
       #include <stdio.h>
       FILE *popen(const char *command, const char *type);
       int pclose(FILE *stream);
// 它的底层其实就是帮我们做了这几件事情
/*
1. 执行 Shell 命令
启动一个新的子进程,运行指定的命令行程序(如 ls, grep, python 等)。
2. 建立单向管道
根据模式(读或写),创建一个管道:
读模式("r"):从子进程的输出(如 stdout)读取数据。
写模式("w"):向子进程的输入(如 stdin)写入数据。
3. 返回文件流指针
返回一个 FILE* 指针,可通过标准文件操作函数(如 fread, fgets, fprintf)与子进程交互。
*/
// 1. pipe
// 2. fork + dup2(pipe[1],1) + exec*,执行结果给父进程,pipe[0]
// 3. return
// 返回值是FILE*的,其实就是把管道封装起来,封装成一个文件了。

// 返回值
RETURN VALUE
       popen(): on success, returns a pointer to an open stream that can be used to read or write to  the  pipe;  if  the fork(2) or pipe(2) calls fail, or if the function cannot allocate memory, NULL is returned.
       // 成功的话就返回一个文件流,失败返回空

       pclose(): on success, returns the exit status of the command; if wait4(2) returns an error, or some other error is detected, -1 is returned.

       Both functions set errno to an appropriate value in the case of an error.
       
// 一个线程执行fork就相当于当前进程创建子进程。

1.10 fgets

// man fgets
NAME
       fgetc, fgets, getc, getchar, ungetc - input of characters and strings
SYNOPSIS
       #include <stdio.h>
       char *fgets(char *s, int size, FILE *stream);
// 返回值
RETURN VALUE
       fgets() returns s on success, and NULL on error or when end of file occurs while no characters have been read.
       // 读取失败返回NULL

2. EchoServer

Linux: This repository is specifically designed to store Linux code - Gitee.comhttps://gitee.com/Axurea/linux/tree/master/2025_05_26_EchoServer

3. 多线程远程命令执行

Linux: This repository is specifically designed to store Linux code - Gitee.comhttps://gitee.com/Axurea/linux/tree/master/2025_05_28_CommandServerTcp

4. 引入线程池版本翻译

  • 上篇UDP部分写过类似的设计方案。
  • 后面我们还会涉及http相关内容,到时候在引入线程池会更方便,也很合理。

5. 验证TCP - windows作为client访问Linux

Linux: This repository is specifically designed to store Linux code - Gitee.comhttps://gitee.com/Axurea/linux/tree/master/2025_05_28_Tcp_WindowsClient&&LinxuServer

注意:一定要开放云服务器对应的端口号,在你的阿里云或者腾讯云、华为云的网站后台中开放。

我们可以发现tcp client(windows)和tcp server(Linux)可以通信。

WinSock2.h是Windows Sockets API(应用程序接口)的头文件,用于在Windows平台上进行网络编程。它包含了Windows Sockets 2(WinSock2)所需的数据类型、函数声明和结构定义,使得开发者能够创建和使用套接字(sockets)进行网络通信。
在编写使用Winsock2的程序时,需要在源文件中包含WinSock2.h头文件。这样,编译器就能够识别并理解Winsock2中定义的数据类型和函数,从而能够正确的编译和链接网络相关的代码。
此外,与WinSock2.h头文件相对应的是ws2_32.lib库文件。在链接阶段,需要将这个库文件链接到程序中,以确保运行时能够找到并调用Winsock2 API中实现的函数。
在WinSock2.h中定义了一些重要的数据类型和函数,如:
WSADATA:保存初始化Winsock库时返回的信息。
SOCKET:表示一个套接字描述符,用于在网络中唯一标识一个套接字。
sockaddr_in:IPv4地址结构体,用于存储IP地址和端口号等信息。
socket():创建一个套接字。
bind():将套接字与本地地址绑定。
listen():将套接字设置为监听模式,等待客户端的连接请求。
accept():接受客户端的连接请求,并返回一个新的套接字描述符,用于与客户端
进行通信。


WSAStartup函数是Windows Sockets API的初始化函数,它用于初始化Winsock库。该函数在应用程序或DLL调用任何Windows套接字函数之前必须首先执行,它扮演着初始化的角色。
以下是WSAStartup函数的一些关键的:
它接受两个参数:wVersionRequested和IpWSAData。wVersionRequested用于指定所请求的Winsock版本,通常使用MAKEWORD(major,minor)宏,其中major和minor分别标识请求的主版本号和次版本号。IpWSAData是一个指向WSAData结构的指针用于接收初始化信息。
如果函数调用成功,它会返回0:否则,返回错误代码。
WSAStartup函数的主要作用是向操作系统说明我们将使用哪个版本的Winsock库,从而使得该库文件能与当前的操作系统协同工作。成功调用该函数后,Winsock库的状态会被初始化,应用程序就可以使用Winsock提供的一系列套接字服务,如地址家族识别、地址转换、名字查询和连接控制等。这些服务使得应用程序可以与底层的网络协议栈进行交互,实现网络通信。
在调用WSAStartup函数后,如果应用程序完成了对请求的Socket库的使用,应调用
WSAStartup函数后,如果应用程序完成了对请求的Socket库的使用,应调用WSACleanup
函数来解除与Socket库的绑定并释放所占用的系统资源。

6. connect的断线重连

客户端会面临服务器崩溃的情况,我们可以试着写一个客户端重连的代码,模拟并理解一些客户端行为,比如游戏客户端等。

采用状态机,实现一个简单的tcp client可以实现重连的效果。

Linux: This repository is specifically designed to store Linux code - Gitee.comhttps://gitee.com/Axurea/linux/tree/master/2025_05_28_TcpClientConnect

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

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

相关文章

基于TMC5160堵转检测技术的夹紧力控制系统设计与实现

点击下面图片带您领略全新的嵌入式学习路线 &#x1f525;爆款热榜 90万阅读 1.6万收藏 一、技术背景与系统原理 在工业自动化领域&#xff0c;夹紧力控制是精密装配、机床夹具等场景的核心需求。传统方案多采用压力传感器伺服电机的闭环控制方式&#xff0c;但存在系统复杂…

XCTF-web-fileclude

解析如下 <?php include("flag.php"); // 包含敏感文件&#xff08;通常包含CTF挑战的flag&#xff09; highlight_file(__FILE__); // 高亮显示当前PHP文件源代码&#xff08;方便查看代码逻辑&#xff09;if(isset($_GET["file1"]…

OpenShift AI - 启用过时版本的 Notebook 镜像

《OpenShift / RHEL / DevSecOps 汇总目录》 说明&#xff1a;本文已经在 OpenShift 4.18 OpenShift AI 2.19 的环境中验证 文章目录 查看可用 Notebook 镜像控制台查看命令行查看 Notebook 镜像、Image Stream 和 Image Registry Repository 对应关系启用老版本的 Notebook 镜…

Redis 缓存穿透、缓存击穿、缓存雪崩详解与解决方案

在分布式系统中&#xff0c;Redis 凭借高性能和高并发处理能力&#xff0c;成为常用的缓存组件。然而&#xff0c;在实际应用中&#xff0c;缓存穿透、缓存击穿、缓存雪崩这三大问题会严重影响系统的性能与稳定性。本文将详细解析这三个问题的成因&#xff0c;并提供对应的解决…

DQN和DDQN(进阶版)

来源&#xff1a; *《第五章 深度强化学习 Q网络》.ppt --周炜星、谢文杰 一、前言 Q表格、Q网络与策略函数 Q表格是有限的离散的&#xff0c;而神经网络可以是无限的。 对于动作有限的智能体来说&#xff0c;使用Q网络获得当下状态的对于每个动作的 状态-动作值 。那么 a…

【组件】翻牌器效果

目录 效果组件代码背景素材 效果 组件代码 <template><divclass"card-flop":style"{height: typeof height number ? ${height}px : height,--box-width: typeof boxWidth number ? ${boxWidth}px : boxWidth,--box-height: typeof boxHeight nu…

CentOS 7 环境中部署 LNMP(Linux + Nginx + MySQL 5.7 + PHP)

在 CentOS 7 环境中部署 LNMP&#xff08;Linux Nginx MySQL 5.7 PHP&#xff09; 环境的详细步骤如下。此方案确保各组件版本兼容&#xff0c;并提供完整的配置验证流程。 1. 更新系统 sudo yum update -y 2. 安装 MySQL 5.7 2.1 添加 MySQL 官方 YUM 仓库 由于MySQL并不…

NX811NX816美光颗粒固态NX840NX845

NX811NX816美光颗粒固态NX840NX845 美光NX系列固态硬盘颗粒深度解析&#xff1a;技术、性能与市场全景透视 一、技术架构与核心特性解析 1. NX811/NX816&#xff1a;入门级市场的平衡之选 技术定位&#xff1a;基于176层TLC&#xff08;Triple-Level Cell&#xff09;3D NAN…

捋捋wireshark

本猿搬砖时会用到wireshark分析pcap包&#xff0c;但频率不高&#xff0c;记过一些笔记&#xff0c;今天捋捋&#xff0c;希望能给初学者节省一点时间。 wireshark是个网络封包分析软件&#xff08;network packet analyzer&#xff09;&#xff0c;可以用来抓流量包&#xff…

c++学习之---模版

目录 一、函数模板&#xff1a; 1、基本定义格式&#xff1a; 2、模版函数的优先匹配原则&#xff1a; 二、类模板&#xff1a; 1、基本定义格式&#xff1a; 2、类模版的优先匹配原则&#xff08;有坑哦&#xff09;&#xff1a; 3、缺省值的设置&#xff1a; 4、ty…

第十六章 EMQX黑名单与连接抖动检测

系列文章目录 第一章 总体概述 第二章 在实体机上安装ubuntu 第三章 Windows远程连接ubuntu 第四章 使用Docker安装和运行EMQX 第五章 Docker卸载EMQX 第六章 EMQX客户端MQTTX Desktop的安装与使用 第七章 EMQX客户端MQTTX CLI的安装与使用 第八章 Wireshark工具的安装与使用 …

新编辑器编写指南--给自己的备忘

欢迎使用Markdown编辑器 你好&#xff01; 这是你第一次使用 Markdown编辑器 所展示的欢迎页。如果你想学习如何使用Markdown编辑器, 可以仔细阅读这篇文章&#xff0c;了解一下Markdown的基本语法知识。 新的改变 我们对Markdown编辑器进行了一些功能拓展与语法支持&#x…

鸿蒙网络数据传输案例实战

一、案例效果截图 二、案例运用到的知识点 核心知识点 网络连接管理&#xff1a;connection模块HTTP数据请求&#xff1a;http模块RPC数据请求&#xff1a;rcp模块文件管理能力&#xff1a;fileIo模块、fileUri模块 其他知识点 ArkTS 语言基础V2版状态管理&#xff1a;Comp…

【JavaEE】-- 网络原理

文章目录 1. 网络发展史1.1 广域网1.2 局域网 2. 网络通信基础2.1 IP地址2.2 端口号2.3 认识协议2.4 五元组2.5 协议分层2.5.1 分层的作用2.5.2 OSI七层模型&#xff08;教科书&#xff09;2.5.3 TCP/IP五层&#xff08;或四层&#xff09;模型&#xff08;工业中常用&#xff…

1.RV1126-OPENCV 交叉编译

一.下载opencv-3.4.16.zip到自己想装的目录下 二.解压并且打开 opencv 目录 先用 unzip opencv-3.4.16.zip 来解压 opencv 的压缩包&#xff0c;并且进入 opencv 目录(cd opencv-3.4.16) 三. 修改 opencv 的 cmake 脚本的内容 先 cd platforms/linux 然后修改 arm-gnueabi.to…

PySide6 GUI 学习笔记——常用类及控件使用方法(标签控件QLabel)

文章目录 标签控件QLabel及其应用举例标签控件QLabel的常用方法及信号应用举例Python 代码示例1Python 代码示例2 小结 标签控件QLabel及其应用举例 QLabel 是 PySide6.QtWidgets 模块中的一个控件&#xff0c;用于在界面上显示文本或图像。它常用于作为标签、提示信息或图片展…

CSS (mask)实现服装动态换色:创意与技术的完美融合

在网页开发中&#xff0c;我们常常会遇到需要对图片元素进行个性化处理的需求&#xff0c;比如改变图片中特定部分的颜色。今天&#xff0c;我们就来探讨一种通过 CSS 和 JavaScript 结合&#xff0c;实现服装动态换色的有趣方法。 一、代码整体结构分析 上述代码构建了一个完…

基于51单片机的音乐盒汽车喇叭调音量proteus仿真

地址&#xff1a; https://pan.baidu.com/s/1l3CSSMi4uMV5-XLefnKoSg 提取码&#xff1a;1234 仿真图&#xff1a; 芯片/模块的特点&#xff1a; AT89C52/AT89C51简介&#xff1a; AT89C51 是一款常用的 8 位单片机&#xff0c;由 Atmel 公司&#xff08;现已被 Microchip 收…

彻底理解Spring三级缓存机制

文章目录 前言一、Spring解决循环依赖时&#xff0c;为什么要使用三级缓存&#xff1f; 前言 Spring解决循环依赖的手段&#xff0c;是通过三级缓存&#xff1a; singletonObjects&#xff1a;存放所有生命周期完整的单例对象。&#xff08;一级缓存&#xff09;earlySingleto…

【产品经理从0到1】自媒体端产品设计

后台的定义 “后台” 与“前台”都是相对独立的平台&#xff0c;前台是服务于互联网用户的平台 &#xff0c;后台主要是支撑前台页面内容、数据及对前台业务情况的统计分析的系统&#xff1b; 后台与前台的区别 第1&#xff1a;使用用户不同 前台用户&#xff1a;互联网用户…