【Linux】Socket编程——TCP版

📝前言:

上篇文章我们讲解了UDP编程,这篇文章我们来讲讲TCP编程

🎬个人简介:努力学习ing

📋个人专栏:Linux

🎀CSDN主页 愚润求学

🌄其他专栏:C++学习笔记C语言入门基础python入门基础C++刷题专栏


目录

一,TCP介绍

  • 在 UDP 通信中,客户端不需要与服务端建立连接,可直接将数据封装成 UDP 数据报发送出去。如果服务端没有准备好接收(比如没有启动监听或者监听的端口错误等 ),那么发送过去的数据报就会被丢弃,从而导致数据丢失
  • TCP(Transmission Control Protocol,传输控制协议)是面向连接的、可靠的传输层协议。在进行数据传输之前,需要通过 "三次握手" 建立连接,确保服务端处于可接收数据的状态
  • UDP直接获取的套接字就是和客户端通信的套接字,但是TCP需要获取两个套接字,一个是监听的,一个是通信的。

二,重要接口

socket

  • socket()打开一个网络通讯端口,如果成功的话,就像 open()一样返回一个文件描述符;
  • 应用程序可以像读写文件一样用 read/write 在网络上收发数据(写入套接字文件);
  • 如果 socket()调用出错则返回-1;
  • 对于 IPv4, domain 参数指定为 AF_INET;
  • 对于 TCP 协议,type 参数指定为 SOCK_STREAM, 表示面向流的传输协议
  • protocol 参数的介绍从略,指定为 0 即可。

bind

  • 服务器程序所监听的网络地址和端口号通常是固定不变的,客户端程序得知服务器程序的地址和端口号后就可以向服务器发起连接;服务器需要调用 bind 绑定一个固定的网络地址和端口号;
  • 成功返回 0, 失败返回 - 1。
  • bind () 的作用是将参数 sockfdaddr 绑定在一起,使 sockfd 这个用于网络通讯的文件描述符监听 addr 所描述的地址和端口号;
  • struct sockaddr * 是一个通用指针类型,addr参数实际上可以接受多种协议的 sockaddr 结构体,而它们的长度各不相同,所以需要第三个参数 addrlen 指定结构体的长度;

addr填充

addr 参数是这样初始化

cpp 复制代码
memset(&_addr, 0, sizeof(_addr)); // 清空
_addr.sin_family = AF_INET; // 填协议家族
_addr.sin_port = htons(_port); // 端口
// 这个 _ip 是一个点分10进制的 string ip 
inet_pton(AF_INET, _ip.c_str(), &_addr.sin_addr); // IP

// 或者:_addr.sin_addr.s_addr = INADDR_ANY; // 用于服务端监听所有IP

listen

  • 用于将套接字设置为监听状态,等待客户端连接。
  • sockfd:要监听的套接字文件描述符(通常是 socket 创建并 bind 绑定过的)。
  • backlog:等待连接队列的最大长度(即最多允许多少个客户端处于"等待连接"状态)。
  • listen 后,套接字变为被动监听状态,不能直接收发数据,只能用来接收连接 。只有调用了 listen,后续才能用 accept 接收客户端连接

accept


参数

  • sockfd:服务器端的监听 socket 描述符(通过 socket() 创建并通过 bind() 绑定、listen() 监听的 socket)。
  • addr:用于存储客户端的地址信息,是一个输出参数。
  • addrlen:输入输出参数,传入时表示 addr 缓冲区的大小,返回时表示实际存储的地址信息长度。

返回值

  • 成功:返回一个新的 socket 描述符,用于与该客户端进行数据收发(读写操作)
  • 失败:返回 -1,并设置 errno 表示错误原因。

注意

  • accept() 是阻塞函数,会一直等待直到有连接到来(非阻塞模式除外)。
  • 原监听 socket 用于监听新连接,而 accept() 返回的新 socket 才用于与特定客户端通信。
  • 一个服务器通常会循环调用 accept() 来处理多个个客户端的连接请求。

accept() 就像服务器的 "接待员",负责接待新来的客户端并安排专门的 "服务员"(新的 socket)为其服务,而自己则继续等待下一位客人

connect

connect() 函数主要用于客户端,用于向服务器发起连接请求

参数

  • sockfd:客户端创建的 socket 描述符(通过 socket() 函数生成,尚未连接)。
  • addr:指向服务器地址信息的结构体指针(需包含服务器的 IP 地址和端口号)
  • addrlen:addr 结构体的字节长度(例如 sizeof(struct sockaddr_in))。

返回值:

  • 成功:返回 0,表示 TCP 三次握手完成,连接建立成功。
  • 失败:返回 -1,并设置全局变量 errno 指示错误原因(例如
    ECONNREFUSED 表示连接被拒绝,ETIMEDOUT 表示超时)。

recv和send

建立好连接以后,我们用recv来接受信息,有别于UDP的recvfrom(发送也类似,TCP 用 send(),UDP用sendto()):

  • recv 不需要指定发送方地址,因为在面向连接的通信中,连接建立后已经知道对方地址
  • recvfrom 必须提供 src_addr 参数来存储发送方的地址信息,这对于无连接通信至关重要,因为每次接收数据可能来自不同的发送方
cpp 复制代码
// recv 函数
ssize_t recv(int sockfd, void *buf, size_t len, int flags);

// recvfrom 函数
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
                struct sockaddr *src_addr, socklen_t *addrlen);

工作流程

  • 服务器先创建监听 socket(socket()),绑定地址(bind()),并开始监听端口(listen())。
  • 调用 accept() 阻塞等待客户端连接。
  • 当客户端调用 connect() 发起连接后,accept() 返回新的 socket 描述符。
  • 服务器使用新的 socket 与客户端通信(recv()/send()),而原监听 socket 继续等待其他客户端连接。

三,代码练习

简答的回声:Github链接


🌈我的分享也就到此结束啦🌈

要是我的分享也能对你的学习起到帮助,那简直是太酷啦!

若有不足,还请大家多多指正,我们一起学习交流!

📢公主,王子:点赞👍→收藏⭐→关注🔍

感谢大家的观看和支持!祝大家都能得偿所愿,天天开心!!!

相关推荐
jokr_几秒前
C++ STL 专家容器:关联式、哈希与适配器
java·c++·哈希算法
努力学习的小廉5 分钟前
深入了解linux系统—— 线程互斥
linux·运维·服务器
zzz1006612 分钟前
CentOS 7 服务器初始化:从 0 到 1 的安全高效配置指南
服务器·安全·centos
会说话的吹风机22 分钟前
五、VSCODE SSH连接linux服务器免密登录
服务器·vscode·ssh
JioJio~z1 小时前
PLC通讯中遇到的实际场景
运维·服务器·网络
倔强的石头1 小时前
java程序员如何搭建C++windows开发环境搭建(二)
c++·后端
砂糖橘加盐1 小时前
前端需要掌握的网络基础
前端·javascript·网络协议
青衫客361 小时前
负载均衡之带权重的随机负载均衡算法详解与实现
运维·服务器·负载均衡
小米里的大麦1 小时前
034 进程间通信 —— System V 共享内存
linux
zgc12453671 小时前
Linux学习-网络编程2
linux·网络·学习