Linux——网络通信TCP通信常用的接口和tcp服务demo

文章目录

  • TCP通信所需要的套接字
    • socket()
    • bind()
    • listen()
    • accept
    • connect()
  • 封装TCP socket

TCP通信所需要的套接字

socket()

socket()函数主要作用是返回一个描述符,他的作用就是打开一个网络通讯端口,返回的这个描述符其实就可以理解为一个文件描述符,tcp在通讯的时候是会开辟一个缓存空间的,我们发送和读取消息可以理解为在这个缓存空间中进行的。因此这里我们可以知道我们可以直接用write和read函数进行消息的写入和读取,如果socket()调用出错则返回-1;
对于IPv4, family参数指定为AF_INET;
对于TCP协议,type参数指定为SOCK_STREAM, 表示面向流的传输协议
protocol参数的介绍从略,指定为0即可。
代码如下以ipv4协议为例

int socketfd=socket(AF_INET,SOCK_STREAM,0)

bind()

bind()函数的作用是为了让socket返回的描述符与端口号进行绑定在bind函数之前我们还需要做一个工作就是将我们的信息存入一个结构体sockaddr_in中,struct sockaddr *是一个通用指针类型,myaddr参数实际上可以接受多种协议的sockaddr结构体,而它们的长度各不相同,所以需要第三个参数addrlen指定结构体的长度
在这里插入图片描述
我们的程序中对myaddr参数是这样初始化的:

sockaddr_in local;
bzero(&local, sizeof(local));//将整个结构体清0
local.sin_port = htons(port_);//porty_是我们的端口号这里是将这个端口号转化为网络序列一般我们的端口号都设置为8000以上
local.sin_family = AF_INET;//设置地址类型为AF_INET
inet_aton(ip_.c_str(), &(local.sin_addr));//网络地址为INADDR_ANY, 这个宏表示本地的任意IP地址,因为服务器可能有多个网卡,每个网卡也可能绑定多个IP 地址, 这样设置可以在所有的IP地址上监听,直到与某个客户端建立了连接时才确定下来到底用哪个IP 地址;

listen()

在这里插入图片描述
liasten函数生命socketfd正处于监听状态并且最多可以允许backlog个客户端处于连接等待状态如果收到更多连接请求就会忽略
listen成功返回0 失败则是-1

accept

在这里插入图片描述
accept函数是为了接受来自客户端的连接请求的函数它是在三次握手之后发挥作用,这里也会有一些问题比如说accept发挥作用的时候此时并没有客户发送连接请求该怎么办呢?这里会陷入阻塞等待的状态也就是一直等到有连接请求为止。

connect()

在这里插入图片描述
客户端需要调用connect()连接服务器;
connect和bind的参数形式一致, 区别在于bind的参数是自己的地址, 而connect的参数是对方的地址;
connect()成功返回0,出错返回-1

封装TCP socket

tcp_socket.hpp

#pragma once
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <string>
#include <cassert>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <fcntl.h>
typedef struct sockaddr sockaddr;
typedef struct sockaddr_in sockaddr_in;
#define CHECK_RET(exp) if (!(exp)) {\return false;\
}
class TcpSocket {
public:TcpSocket() : fd_(-1) { }TcpSocket(int fd) : fd_(fd) { }bool Socket() {fd_ = socket(AF_INET, SOCK_STREAM, 0);if (fd_ < 0) {perror("socket");return false;}printf("open fd = %d\n", fd_);return true;}bool Close() const {close(fd_);printf("close fd = %d\n", fd_);return true;}bool Bind(const std::string& ip, uint16_t port) const {sockaddr_in addr;addr.sin_family = AF_INET;addr.sin_addr.s_addr = inet_addr(ip.c_str());addr.sin_port = htons(port);int ret = bind(fd_, (sockaddr*)&addr, sizeof(addr));if (ret < 0) {perror("bind");return false;}return true;}bool Listen(int num) const {int ret = listen(fd_, num);if (ret < 0) {perror("listen");return false;}return true;}bool Accept(TcpSocket* peer, std::string* ip = NULL, uint16_t* port = NULL) const {sockaddr_in peer_addr;socklen_t len = sizeof(peer_addr);int new_sock = accept(fd_, (sockaddr*)&peer_addr, &len);if (new_sock < 0) {perror("accept");return false;}printf("accept fd = %d\n", new_sock);peer->fd_ = new_sock;if (ip != NULL) {*ip = inet_ntoa(peer_addr.sin_addr);}if (port != NULL) {*port = ntohs(peer_addr.sin_port);}return true;}bool Recv(std::string* buf) const {buf->clear();char tmp[1024 * 10] = {0};// [注意!] 这里的读并不算很严谨, 因为一次 recv 并不能保证把所有的数据都全部读完// 参考 man 手册 MSG_WAITALL 节. ssize_t read_size = recv(fd_, tmp, sizeof(tmp), 0);if (read_size < 0) {perror("recv");return false;}if (read_size == 0) {return false;}buf->assign(tmp, read_size);return true;}bool Send(const std::string& buf) const {ssize_t write_size = send(fd_, buf.data(), buf.size(), 0);if (write_size < 0) {perror("send");return false;}return true;}bool Connect(const std::string& ip, uint16_t port) const {sockaddr_in addr;addr.sin_family = AF_INET;addr.sin_addr.s_addr = inet_addr(ip.c_str());addr.sin_port = htons(port);int ret = connect(fd_, (sockaddr*)&addr, sizeof(addr));if (ret < 0) {perror("connect");return false;}return true;}int GetFd() const {return fd_;}private:int fd_;
};

tcp_server.hpp

#pragma once
#include <functional>
#include "tcp_socket.hpp"
typedef std::function<void (const std::string& req, std::string* resp)> Handler;
class TcpServer {
public:TcpServer(const std::string& ip, uint16_t port) : ip_(ip), port_(port) {}bool Start(Handler handler) {// 1. 创建 socket;CHECK_RET(listen_sock_.Socket());// 2. 绑定端口号CHECK_RET(listen_sock_.Bind(ip_, port_));// 3. 进行监听CHECK_RET(listen_sock_.Listen(5));// 4. 进入事件循环for (;;) {// 5. 进行 acceptTcpSocket new_sock;std::string ip;uint16_t port = 0;if (!listen_sock_.Accept(&new_sock, &ip, &port)) {continue;}printf("[client %s:%d] connect!\n", ip.c_str(), port);// 6. 进行循环读写for (;;) {std::string req;// 7. 读取请求. 读取失败则结束循环bool ret = new_sock.Recv(&req);if (!ret) {printf("[client %s:%d] disconnect!\n", ip.c_str(), port);// [注意!] 需要关闭 socketnew_sock.Close();break;}// 8. 计算响应std::string resp;handler(req, &resp);// 9. 写回响应new_sock.Send(resp);
printf("[%s:%d] req: %s, resp: %s\n", ip.c_str(), port,req.c_str(), resp.c_str());}}return true;}
private:TcpSocket listen_sock_;std::string ip_;uint64_t port_;
};
我们开开心心的在一起

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

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

相关文章

c高级 函数+Makefile

一、作业 1.写一个函数&#xff0c;输出当前用户的uid和gid&#xff0c;并使用变量接收结果 #!/bin/bash function fun(){retid -uret1id -gecho $ret $ret1 } retfun echo $ret二、练习回顾 1.分文件编译&#xff08;实现冒泡排序&#xff09; 正确的&#xff1a;将数组的…

HTTP/1.1 如何优化?

问你一句:「你知道 HTTP/1.1 该如何优化吗?」 我们可以从下面这三种优化思路来优化 HTTP/1.1 协议: 尽量避免发送 HTTP 请求在需要发送 HTTP 请求时&#xff0c;考虑如何减少请求次数减少服务器的 HTTP 响应的数据大小 下面&#xff0c;就针对这三种思路具体看看有哪些优化…

二叉树的统一迭代法#思路

前序遍历&#xff1a; class Solution: def preorderTraversal(self, root: TreeNode) -> List[int]: result[] st[] if root: st.append(root) while st: nodest.pop() if node ! None: if node.right: st.append(node.right) if node.left: st.append(node.left) st.appen…

git的常用命令有哪些?

Git 是一个流行的分布式版本控制系统&#xff0c;用于跟踪文件的变化、协作开发和管理代码。以下是一些常用的 Git 命令&#xff1a; 创建和克隆仓库&#xff1a; git init&#xff1a;在当前目录初始化一个新的 Git 仓库。git clone <仓库URL>&#xff1a;克隆一个远程仓…

【Java EE初阶十六】网络原理(一)

在网络原理中主要学习TCP/IP四层模型中的重点网络协议 1. 应用层 1.1 应用程序与协议 应用层是和程序员接触最密切的&#xff1b; 应用程序&#xff1a;在应用层这里&#xff0c;很多时候都是程序员自定义应用层协议&#xff08;步骤&#xff1a;1、根据需求&#xff0c;明确…

log4net的使用步骤

log4net的使用步骤 Log4net是一个基于.NET平台的开源日志记录组件&#xff0c;它可以帮助开发人员在应用程序中添加日志记录功能&#xff0c;支持多种日志级别&#xff0c;如DEBUG、INFO、WARN、ERROR等&#xff0c;这使得开发人员可以根据需要灵活地控制日志的输出。下面是lo…

SPSSAU【文本分析】|我的词库

我的词库 文本分析时&#xff0c;可能涉及到一些新词&#xff0c;比如‘内卷’&#xff0c;这个词很可能在词典中并未出现过&#xff0c;词库也不认识它。但研究者自己认识它&#xff0c;此时可将该词纳入到新词词库中&#xff0c;让系统统计词频等信息时也对该词进行统计。当…

【监控】spring actuator源码速读

目录 1.前言 2.先搂一眼EndPoint 3.EndPoint如何被注入 4.EndPoint如何被暴露 4.1.如何通过http暴露 4.2.如何通过jmx暴露 5.EndPoint是怎么实现监控能力的 6.知道这些的意义是什么 1.前言 版本&#xff1a;spring-boot-starter-actuator 2.6.3 阅读源码一定要带着疑…

小迪安全26WEB 攻防-通用漏洞SQL 注入 SqlmapOracleMongodbDB2 等

#知识点&#xff1a; 1、数据库注入-Oracle&Mongodb 2、数据库注入-DB2&SQLite&Sybase 3、SQL 注入神器-SQLMAP 安装使用拓展 数据库注入&#xff1a; 数据库注入-联合猜解-Oracle&Mongodb 1.Oracle数据库一般会在java上执行 参考:https://www.cnblog…

C/C++与汇编交互总结

1.汇编当中可以调用C函数&#xff0c;只需要"IMPORT"一下即可&#xff0c;如&#xff1a; change_vari_c PROCPUSH {R0,R1,LR}IMPORT core_res_cIMPORT temp_func_retEXPORT change_vari_cBL temp_func_retLDR R1,core_res_cSTR R0,[R1]POP {R0,R1,PC}ENDP 如果该C…

Rust HashMap详解及单词统计示例

在Rust中&#xff0c;HashMap是一种非常有用的数据结构&#xff0c;用于存储键值对。本文将深入介绍HashMap的特性&#xff0c;以及通过一个单词统计的例子展示其用法。 HashMap简介 HashMap是Rust标准库提供的用于存储键值对的数据结构。它允许通过键快速查找对应的值&#…

图数据库 之 Neo4j - 应用场景1(6)

Neo4j是一种图数据库&#xff0c;它专注于处理关系数据密集型的问题。由于其图结构的特性&#xff0c;Neo4j能够高效地存储、查询和分析连接数据。 以下是一些常见的Neo4j应用场景&#xff1a; 社交网络分析&#xff1a;通过建模和分析人际关系&#xff0c;可以揭示社交网络中…

PTA | Wifi密码

下面是微博上流传的一张照片&#xff1a;“各位亲爱的同学们&#xff0c;鉴于大家有时需要使用 wifi&#xff0c;又怕耽误亲们的学习&#xff0c;现将 wifi 密码设置为下列数学题答案&#xff1a;A-1&#xff1b;B-2&#xff1b;C-3&#xff1b;D-4&#xff1b;请同学们自己作答…

【详解】图的概念和存储结构(邻接矩阵,邻接表)

目录 图的基本概念&#xff1a; 图的存储结构 邻接矩阵&#xff08;GraphByMatrix&#xff09;&#xff1a; 基本参数&#xff1a; 初始化&#xff1a; 获取顶点元素在其数组中的下标 &#xff1a; 添加边和权重&#xff1a; 获取顶点的度&#xff1a; 打印图&#xf…

Unity所有关于旋转的方法详解

前言&#xff1a;欧拉角和四元数的简单描述 我们在Inspector面板上看到的rotation其实是欧拉角&#xff0c; 我们将Inspector面板设置成Debug模式&#xff0c;此时看到的local Rotation才是四元数。 Unity中的欧拉旋转是按照Z-X-Y顺规执行的旋转&#xff0c;一组欧拉旋转过程中…

【HarmonyOS】鸿蒙Arkts开发从入门到实战【持续更新】

鸿蒙Arkts开发从入门到实战目录 1. 工具安装 ArkTs 2.1 基础语法 2.2 状态管理 2.3 渲染控制 2.4 页面路由 ArkUI的基础类组件使用 3.1 Image组件 3.2 Text组件 3.3 TextInput组件 3.4 Button组件 3.5 Slider组件 3.6 Video组件 3.7 自定义组件 3.8 气泡提示 3.9 菜单 Stage模…

RibbonBar RibbonPage切换事件

在开发的过程中&#xff0c;我们会用到点击切换page&#xff0c;来响应对应的事件&#xff0c;例如以下事件&#xff1a; 头文件中&#xff1a; void ribboncurrentPageIndexChanged(int index); 实现文件中&#xff1a; connect(ribbonBar(), SIGNAL(currentPageIndexChang…

[力扣 Hot100]Day29 删除链表的倒数第 N 个结点

题目描述 给你一个链表&#xff0c;删除链表的倒数第 n 个结点&#xff0c;并且返回链表的头结点。 出处 思路 两个指针间隔n&#xff0c;一趟遍历解决。 代码 class Solution { public:ListNode* removeNthFromEnd(ListNode* head, int n) {ListNode* phead;ListNode* …

【数据库】Mysql索引

1、什么是索引&#xff1f;为什么要用索引&#xff1f; 1.1、索引的含义 数据库索引&#xff0c;是数据库管理系统中一个排序的数据结构&#xff0c;以协助快速查询&#xff0c;更新数据库中表的数据。索引的实现通常使用B树和变种的B树&#xff08;MySQL常用的索引就是B树&am…

跟着pink老师前端入门教程(JavaScript)-day03

四、常量 概念&#xff1a;使用 const 声明的变量称为“常量”。 使用场景&#xff1a;当某个变量永远不会改变的时候&#xff0c;就可以使用 const 来声明&#xff0c;而不是let。 命名规范&#xff1a;和变量一致 常量使用&#xff1a; 注意&#xff1a;常量不允许重新…