【网络】套接字 -- TCP
🥁作者: 华丞臧.
📕专栏:【网络】
各位读者老爷如果觉得博主写的不错,请诸位多多支持(点赞+收藏+关注)。如果有错误的地方,欢迎在评论区指出。
推荐一款刷题网站 👉 LeetCode刷题网站
文章目录
- 一、TCP协议
- 1.1 socket 常见API
- listen
- accept
- connect
- 1.2 TCP服务器
- init()
- start()
- 1.3 TCP客户端
- 1.4 测试
一、TCP协议
对TCP(Transmission Control Protocol 传输控制协议)有一个直观的认识:
- 传输层协议
- 有连接
- 可靠传输
- 面向字节流
1.1 socket 常见API
// 创建 socket 文件描述符 (TCP/UDP, 客户端 + 服务器)
int socket(int domain, int type, int protocol);
- socket()打开一个网络通讯端口,如果成功的话,就像open()一样返回一个文件描述符;
- 应用程序可以像读写文件一样用read/write在网络上收发数据;
- 如果socket()调用出错则返回-1;
- 对于IPv4, family参数指定为AF_INET;
- 对于TCP协议,type参数指定为SOCK_STREAM, 表示面向流的传输协议
- protocol参数的介绍从略,指定为0即可。
// 绑定端口号 (TCP/UDP, 服务器)
int bind(int socket, const struct sockaddr *address,socklen_t address_len);
- 服务器程序所监听的网络地址和端口号通常是固定不变的,客户端程序得知服务器程序的地址和端口号后就可以向服务器发起连接; 服务器需要调用bind绑定一个固定的网络地址和端口号;
- bind()成功返回0,失败返回-1。
- bind()的作用是将参数sockfd和myaddr绑定在一起, 使sockfd这个用于网络通讯的文件描述符监听
- myaddr所描述的地址和端口号;
- 前面讲过,struct sockaddr *是一个通用指针类型,myaddr参数实际上可以接受多种协议的sockaddr结构体,而它们的长度各不相同,所以需要第三个参数addrlen指定结构体的长度;
程序中对 sockaddr 参数是这样初始化的:
#include <netinet/in.h>
#include <arpa/inet.h>// 2.填充域
struct sockaddr_in local;
bzero(&local, sizeof(local));
local.sin_family = AF_INET;
local.sin_port = htons(port_);
- 将整个结构体清零;
- 设置地址类型为AF_INET;
- 网络地址为INADDR_ANY, 这个宏表示本地的任意IP地址,因为服务器可能有多个网卡,每个网卡也可能绑定多个IP 地址, 这样设置可以在所有的IP地址上监听,直到与某个客户端建立了连接时才确定下来到底用哪个IP 地址;
- 端口号为SERV_PORT, 我们定义为9999。
listen
// 开始监听socket (TCP, 服务器)
int listen(int socket, int backlog);
- listen()声明sockfd处于监听状态, 并且最多允许有backlog个客户端处于连接等待状态, 如果接收到更多的连接请求就忽略, 这里设置不会太大(一般是5);
- listen()成功返回0,失败返回-1。
accept
// 接收请求 (TCP, 服务器)
int accept(int socket, struct sockaddr* address,socklen_t* address_len);
- 三次握手完成后, 服务器调用accept()接受连接;
- 如果服务器调用accept()时还没有客户端的连接请求,就阻塞等待直到有客户端连接上来;
- addr是一个传出参数,accept()返回时传出客户端的地址和端口号;
- 如果给addr 参数传NULL,表示不关心客户端的地址;
- addrlen参数是一个传入传出参数(value-result argument), 传入的是调用者提供的, 缓冲区addr的长度
- 以避免缓冲区溢出问题, 传出的是客户端地址结构体的实际长度(有可能没有占满调用者提供的缓冲区);
connect
// 建立连接 (TCP, 客户端)
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
- 客户端需要调用connect()连接服务器;
- connect和bind的参数形式一致, 区别在于bind的参数是自己的地址, 而connect的参数是对方的地址;
- connect()成功返回0,出错返回-1;
1.2 TCP服务器

#include <iostream>
#include <stdio.h>
#include <strings.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <pthread.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>#include "Log.hpp"volatile bool quitSer = false;void Usage(void *vgs)
{std::cout << "Usage:./tcpserver port ip" << std::endl;
}
class server
{
public:server(int port, std::string ip = ""): sockfd_(-1), ip_(ip), port_(port){}~server(){}public:void init(){}void start(){}private:int sockfd_;uint16_t port_;std::string ip_;
};// ./tcpserver port ip
int main(int argc, char *argv[])
{if (argc < 2 || argc > 3){Usage(argv[0]);exit(USAGE_ERR);}uint16_t port = atoi(argv[1]);std::string ip;if (argc == 3)ip = argv[2];server tcpSer(port, ip);tcpSer.init();tcpSer.start();return 0;
}
init()
TCP套接字服务器端的 init() ,与UDP类似,只不过多了最后一步监听套接字,TCP套接字设置步骤如下:

void init()
{// 1. 创建套接字sockfd_ = socket(AF_INET, SOCK_STREAM, 0);if (sockfd_ < 0){logMessage(FATAL, "socket:%s[%d]", strerror(errno), sockfd_);exit(SOCK_ERR);}logMessage(DEBUG, "socket success..");// 2.填充域struct sockaddr_in local;bzero(&local, sizeof(local));local.sin_family = AF_INET;local.sin_port = htons(port_);ip_.empty() ? (local.sin_addr.s_addr = INADDR_ANY) : (inet_aton(ip_.c_str(), &local.sin_addr));// 3. 绑定网络信息if (bind(sockfd_, (const struct sockaddr *)&local, sizeof(local)) < 0){logMessage(FATAL, "bind:%s[%d]", strerror(errno), sockfd_);exit(BIND_ERR);}logMessage(DEBUG, "bind success...");// 4. 监听套接字// 为什么要监听套接字? 因为TCP是面向连接的,在任何时候都可能请求连接 if (listen(sockfd_, 5) < 0){logMessage(FATAL, "listen:%s[%d]", strerror(errno), sockfd_);exit(LISTEN_ERR);}logMessage(DEBUG, "listen success...");// 完成
}
start()
在TCP套接字提供服务之前,需要使用accept获取连接,accept返回值是一个新的套接字文件描述符,使用该套接字文件描述符来进行网络通信。start()是server提供服务的接口,因此该函数必须是一个死循环(服务器都是在一个死循环当中)以给用户提供持续的服务;在start函数中,主要完成接收用户发送的消息并且将消息提取出来,其主要步骤如下图:

void start()
{char inbuffer_[1024]; // 用来接收客户端发来的消息// 提供服务while (true){quitSer = false;struct sockaddr_in peer;socklen_t len = sizeof(peer);// 5. 获取连接, accept 的返回值是一个新的 socketfdint serviceSock = accept(sockfd_, (struct sockaddr *)&peer, &len);if (serviceSock < 0){// 获取链接失败logMessage(WARINING, "accept: %s[%d]", strerror(errno), serviceSock);continue;}while (!quitSer){memset(inbuffer_, 0, sizeof(inbuffer_));ssize_t s = recvfrom(serviceSock, inbuffer_, sizeof(inbuffer_) - 1, 0,(struct sockaddr *)&peer, &len);if (s > 0){std::cout << s << std::endl;// 接收成功inbuffer_[s] = '\0';}else if (s == -1){//logMessage(WARINING, "recvfrom fialed:%s[%d]", strerror(errno), sockfd_);continue;}uint16_t peerPort = ntohs(peer.sin_port);std::string peerIp = inet_ntoa(peer.sin_addr);if(s > 0)logMessage(NOTICE, "[%s:%d]# %s", peerIp.c_str(), peerPort, inbuffer_);if (strcmp(inbuffer_, "quit") == 0){quitSer = true;}else{sendto(serviceSock, inbuffer_, strlen(inbuffer_), 0,\(const struct sockaddr *)&peer, sizeof(peer)); }}logMessage(DEBUG, "quit server...");close(serviceSock);}
}
1.3 TCP客户端
TCP客户端与UDP不同的是,在进行网络通信之前需要对服务器发起连接请求,连接成功后才能进行网络通信。

#include <iostream>
#include <string>
#include <cstring>
#include <cstdlib>
#include <cassert>
#include <ctype.h>
#include <unistd.h>
#include <strings.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "Log.hpp"volatile bool quit = false;static void Usage(std::string proc)
{std::cerr << "Usage:\n\t" << proc << " serverIp serverPort" << std::endl;std::cerr << "Example:\n\t" << proc << " 127.0.0.1 8081\n"<< std::endl;
}
// ./clientTcp serverIp serverPort
int main(int argc, char *argv[])
{if (argc != 3){Usage(argv[0]);exit(USAGE_ERR);}std::string serverIp = argv[1];uint16_t serverPort = atoi(argv[2]);// 1. 创建socket SOCK_STREAMint sock = socket(AF_INET, SOCK_STREAM, 0);if (sock < 0){std::cerr << "socket: " << strerror(errno) << std::endl;exit(SOCK_ERR);}// 2. connect,发起连接请求,你想谁发起请求呢??当然是向服务器发起请求喽// 2.1 先填充需要连接的远端主机的基本信息struct sockaddr_in server;memset(&server, 0, sizeof(server));server.sin_family = AF_INET;server.sin_port = htons(serverPort);inet_aton(serverIp.c_str(), &server.sin_addr);// 2.2 发起请求,connect 会自动帮我们进行bind!if (connect(sock, (const struct sockaddr *)&server, sizeof(server)) != 0){std::cerr << "connect: " << strerror(errno) << std::endl;exit(CONN_ERR);}std::cout << "info : connect success: " << sock << std::endl;std::string message;while (!quit){message.clear();std::cout << "请输入你的消息>>> ";std::getline(std::cin, message);if (strcasecmp(message.c_str(), "quit") == 0)quit = true;ssize_t s = write(sock, message.c_str(), message.size());logMessage(DEBUG, "write success...");if (s > 0){message.resize(1024);ssize_t s = read(sock, (char *)(message.c_str()), 1024);if (s > 0)message[s] = 0;std::cout << "Server Echo>>> " << message << std::endl;}else if (s <= 0){break;}}close(sock);return 0;
}
由于客户端不需要固定的端口号,因此不必调用bind(),客户端的端口号由内核自动分配。
注意:
- 客户端不是不允许调用bind(),只是没有必要调用bind()固定一个端口号。否则如果在同一台机器上启动多个客户端, 就会出现端口号被占用导致不能正确建立连接;
- 服务器也不是必须调用bind(),但如果服务器不调用bind(),内核会自动给服务器分配监听端口,每次启动服务器时端口号都不一样,客户端要连接服务器就会遇到麻烦。
1.4 测试

相关文章:
【网络】套接字 -- TCP
🥁作者: 华丞臧. 📕专栏:【网络】 各位读者老爷如果觉得博主写的不错,请诸位多多支持(点赞收藏关注)。如果有错误的地方,欢迎在评论区指出。 推荐一款刷题网站 👉 LeetCode刷题网站 文章…...
NDK C++ map容器
map容器// TODO map容器 #include <iostream> #include <map>using namespace std;int main() {// TODO map<int, string>按key值排序,同一个key不可以重复插入map<int, string> map1;map1.insert(pair<int, string>(1, "111&qu…...
linux(Centos)安装docker
官网地址:Install Docker Engine on CentOS 首先检查linux系统版本及内核: 安装docker要求系统版本至少为7.x版本,内核至少为3.8以上 cat /etc/redhat-release # 查看系统版本号uname -r #查看linux系统内核 检查系统是否能连上外网&#…...
Delphi 中 FireDAC 数据库连接(处理错误)
参见:Delphi 中 FireDAC 数据库连接(总览)本主题描述了如何用FireDAC处理数据库错误。一、概述EFDDBEngineException类是所有DBMS异常的基类。单个异常对象是一个数据库错误的集合,可以通过EFDDBEngineException.Errors[]属性访问…...
算法小抄3-理解使用Python容器之列表
引言 首先说一个概念哈,程序算法数据结构,算法是条件语句与循环语句组成的逻辑结构,而数据结构也就是容器. 算法决定数据该如何处理,而容器则决定如何数据如何存储. 不同的语言对容器有不同的实现方式, 但他们的功能都是相似的, 打好容器基础,你就可以在各式各样的语言中来回横…...
Vue3中watch的value问题
目录前言一,ref和reactive的简单复习1.ref函数1.2 reactive函数1.3 用ref定义对象类型数据不用reactive二,watch的value问题2.1 ref2.1.1 普通类型数据2.1.2 对象类型数据2.1.3 另一种方式2.2 reactive三,总结后记前言 在Vue3中,…...
【线性筛+DP】最大和
看错题了,呃呃,其实就是个简单DP最大和 - 蓝桥云课 (lanqiao.cn)题意:思路:设dp[i]为以1为终点的最大和,然后枚举状态和决策就行了主要是线性筛的应用,它可以预处理出一个数的最小质因子是多少Code…...
openpnp - configure - 丢弃(Discard)位置的设置
文章目录openpnp - configure - 丢弃(Discard)位置的设置概述笔记设置丢弃位置吸取元件失败后, 吸嘴一直吸气的处理ENDopenpnp - configure - 丢弃(Discard)位置的设置 概述 测试时, 吸取了一个元件, 吸取成功了, 现在想将这个料丢掉. 点击控制面板-Special页中的Discard不好…...
java Object 万字详解 (通俗易懂)
基本介绍构造方法成员方法hashCode()getClass()toString()equals()finalize()JavaBean重写Object类的方法重写toString重写equals一、基本介绍Object类是java类层次最顶层的基类(父类),所有类都是直接或间接继承自Object类,因此&a…...
Java并发简介(什么是并发)
文章目录并发概念并发和并行同步和异步阻塞和非阻塞进程和线程竞态条件和临界区管程并发的特点提升资源利用率程序响应更快并发的问题安全性问题缓存导致的可见性问题线程切换带来的原子性问题编译优化带来的有序性问题保证并发安全的思路互斥同步(阻塞同步…...
团队API管理工具-YAPI
团队API管理工具-YAPI 推荐一款接口管理平台,操作简单、界面友好、功能丰富、支持markdown语法、可使用Postman导入、Swagger同步数据展示、LDAP、权限管理等功能。 YApi是高效、易用、功能强大的api管理平台,旨在为开发、产品、测试人员提供更优雅的接…...
学习记录 --- Pytorch优化器
文章目录参考文献什么是优化器optimizer的定义optimizer的属性defaultsstateparam_groupsoptimizer的方法zero_grad()step()add_param_group()state_dict()、load_state_dict()优化一个网络同时优化多个网络当成一个网络优化当成多个网络优化只优化网络的某些指定的层调整学习率…...
Flink State 状态后端分析
flink状态实现分析 state * State* |* -------------------InternalKvState* | |* MergingState |* | |* …...
和年薪30W的阿里测开工程师聊过后,才知道我的工作就是打杂的...
前几天和一个朋友聊面试,他说上个月同时拿到了腾讯和阿里的offer,最后选择了阿里。 阿里内部将员工一共分为了14个等级,P6是资深工程师,P7是技术专家。 其中P6和P7就是一个分水岭了,P6是最接近P7的不持股员工&#x…...
C#开发的OpenRA的界面布局数据加载
C#开发的OpenRA的界面布局数据加载 当显示完成加载界面之后,就是进行其它内容处理。 因为后面内容的加载会比较长时间,所以首先显示加载界面是一种非常友好的方法。 因此在软件设计里,尽可能先显示界面,让用户先看到程序正在运行, 然后再处理时间长的加载。如果不这样做,…...
并查集结构
文章目录并查集特点构建过程查找两个元素是否是同一集合优化查找领头元素设置两个元素为同一集合构建结构应用场景并行计算集合问题并查集特点 对于使用并查集构建的结构,可以使得查询两个元素是否在同一集合,以及合并集合的操作无限接近O(1) 构建过程…...
全国CSM敏捷教练认证将于2023年3月25-26开班,报名从速!
CSM,即Certified Scrum Master,是Scrum联盟发起的Scrum认证。 CSM可以帮助团队正确使用Scrum,从而提高项目整体成功的可能性。 CSM深刻理解Scrum的价值观、实践以及Scrum框架。 CSM是“服务型领导”,帮助Scrum团队一起紧密合作。 …...
JavaEE进阶第六课:SpringBoot ⽇志⽂件
上篇文章介绍了SpringBoot配置文件,这篇文章我们将会介绍SpringBoot ⽇志⽂件 荔枝1.日志有什么用2.自定义日志输出2.1获取程序日志对象2.2使用相关方法输出日志2.3日志级别2.3.1日志级别的作用2.3.2日志级别如何设置2.4日志格式3.持久化日志4.更简单的日志输出4.1使…...
外置MOS管平均电流型LED降压恒流驱动器
产品描述 AP5125 是一款外围电路简单的 Buck 型平均电 流检测模式的 LED 恒流驱动器,适用于 8-100V 电压 范围的非隔离式大功率恒流 LED 驱动领域。芯片采用 固定频率 140kHz 的 PWM 工作模式, 利用平均电 流检测模式,因此具有优异的负载调整…...
python+pytest接口自动化(6)-请求参数格式的确定
我们在做接口测试之前,先需要根据接口文档或抓包接口数据,搞清楚被测接口的详细内容,其中就包含请求参数的编码格式,从而使用对应的参数格式发送请求。例如某个接口规定的请求主体的编码方式为 application/json,那么在…...
Python爬虫(一):爬虫伪装
一、网站防爬机制概述 在当今互联网环境中,具有一定规模或盈利性质的网站几乎都实施了各种防爬措施。这些措施主要分为两大类: 身份验证机制:直接将未经授权的爬虫阻挡在外反爬技术体系:通过各种技术手段增加爬虫获取数据的难度…...
汇编常见指令
汇编常见指令 一、数据传送指令 指令功能示例说明MOV数据传送MOV EAX, 10将立即数 10 送入 EAXMOV [EBX], EAX将 EAX 值存入 EBX 指向的内存LEA加载有效地址LEA EAX, [EBX4]将 EBX4 的地址存入 EAX(不访问内存)XCHG交换数据XCHG EAX, EBX交换 EAX 和 EB…...
企业如何增强终端安全?
在数字化转型加速的今天,企业的业务运行越来越依赖于终端设备。从员工的笔记本电脑、智能手机,到工厂里的物联网设备、智能传感器,这些终端构成了企业与外部世界连接的 “神经末梢”。然而,随着远程办公的常态化和设备接入的爆炸式…...
rnn判断string中第一次出现a的下标
# coding:utf8 import torch import torch.nn as nn import numpy as np import random import json""" 基于pytorch的网络编写 实现一个RNN网络完成多分类任务 判断字符 a 第一次出现在字符串中的位置 """class TorchModel(nn.Module):def __in…...
[ACTF2020 新生赛]Include 1(php://filter伪协议)
题目 做法 启动靶机,点进去 点进去 查看URL,有 ?fileflag.php说明存在文件包含,原理是php://filter 协议 当它与包含函数结合时,php://filter流会被当作php文件执行。 用php://filter加编码,能让PHP把文件内容…...
Python 实现 Web 静态服务器(HTTP 协议)
目录 一、在本地启动 HTTP 服务器1. Windows 下安装 node.js1)下载安装包2)配置环境变量3)安装镜像4)node.js 的常用命令 2. 安装 http-server 服务3. 使用 http-server 开启服务1)使用 http-server2)详解 …...
tomcat指定使用的jdk版本
说明 有时候需要对tomcat配置指定的jdk版本号,此时,我们可以通过以下方式进行配置 设置方式 找到tomcat的bin目录中的setclasspath.bat。如果是linux系统则是setclasspath.sh set JAVA_HOMEC:\Program Files\Java\jdk8 set JRE_HOMEC:\Program Files…...
协议转换利器,profinet转ethercat网关的两大派系,各有千秋
随着工业以太网的发展,其高效、便捷、协议开放、易于冗余等诸多优点,被越来越多的工业现场所采用。西门子SIMATIC S7-1200/1500系列PLC集成有Profinet接口,具有实时性、开放性,使用TCP/IP和IT标准,符合基于工业以太网的…...
大数据治理的常见方式
大数据治理的常见方式 大数据治理是确保数据质量、安全性和可用性的系统性方法,以下是几种常见的治理方式: 1. 数据质量管理 核心方法: 数据校验:建立数据校验规则(格式、范围、一致性等)数据清洗&…...
[拓扑优化] 1.概述
常见的拓扑优化方法有:均匀化法、变密度法、渐进结构优化法、水平集法、移动可变形组件法等。 常见的数值计算方法有:有限元法、有限差分法、边界元法、离散元法、无网格法、扩展有限元法、等几何分析等。 将上述数值计算方法与拓扑优化方法结合&#…...
