【网络】TCP协议的简单使用

目录

echo_service

server

单进程单线程

多进程

多线程

线程池

client

echo_service_code


echo_service

还是跟之前UDP一样,我们先通过实际的代码来实现一些小功能,简单的来使用TCP协议进行简单的通信,话不多说,我们先实现一个echo_serve,就是客户端向服务器发消息,服务器把客户端发的消息返回给客户端

server

首先,我们还是先写服务器,先创建socket套接字,只不过与UDP的选项不同,第二个选项我们选SOCK_STREAM,因为TCP是面向字节流的

其次,还是要bind,bind之前要填充服务器相关信息

由于TCP是面向连接的,所以通信前要先建立连接,因为服务器是被连接的一端,所以要将服务器设置为监听状态

这里第二个参数我们暂且先用上,后面会有讲解

至此,我们初始化的工作就做完了

下面就是要开始我们的服务,我们上面说要先建立连接,所以不能直接接收数据,要先获取连接,我们用下面的接口

可以看到,它的返回值是一个文件描述符,可是socket函数的返回值不也是一个文件描述符吗?它们之间有什么区别呢?我么可以认为socket函数的返回值是一个饭店中站在门口拉客的,它拉到了客人就交给饭店内某个人去一对一服务,这个就是accept的返回值

做完了上面的一系列工作就可以进行通信了,由于TCP是面向字节流的,而文件也是面向字节流的,所以可以用read和write进行通信

网络中read的返回值有不同的含义,大于0就是读到的字节的个数,小于0是读取出错,等于0是客户端退出并且关闭连接,我们就可以用返回值去判断客户端的状态

上面就是大体的过程,下面我们要具体的来考虑一下服务器与客户端的交互过程应该如何写:

我们分为单进程单线程、多进程、多线程、线程池

单进程单线程

首先这个方案是绝对不可以的,这样只能处理一个请求,在和一个客户端交互的时候就不可能去accept新连接

多进程

单进程单线程肯定是不行的,我们必须多创建几个执行流来保证聊天和获取连接都要有执行流去执行

此时我们就可以创建子进程让子进程去和客户端进行交互,父进程重新去获取连接,这样就是可以的,但是父进程是要回收子进程的,父进程也不可能去一直等待,那应该如何做呢?我们之前学过一个信号SIG_CHLD,可以把这个信号的处理动作改为SIG_IGN

这样子进程在退出后会自动清理掉,不会产生僵尸,也不会通知父进程。

还有一种方法就是子进程马上创建孙进程,让孙进程去执行任务,之后子进程立马退出,父进程就可以直接wait到子进程,并且孙进程成为了孤儿进程,会由系统领养,我们就不用关心孙进程的回收问题了

并且要注意,因为与客户端的echo服务已经交给了子进程,那么父进程就没有必要保留这个文件描述符了,直接关掉即可,防止文件描述符泄露(只要是不用,但是占着资源的都叫泄露)

子进程是继承父进程的文件描述符表的,继承后各自独立,互不影响

多线程

我们可以使用原生的线程创建的接口来实现多线程的创建,虽然主线程也是要等待新线程的,但是我们可以将新线程设置为分离状态,这样主线程就不用等待了

线程池

当然除了上面的方式,我们自然可以引入线程池,但是其实线程池适合那些短的任务,就是很快做完又可以领新任务,像这种echo的长服务不适合线程池来做,但是我们还是写一下用一下

thread_t 就是线程池中要放的任务的类型

client

客户端跟UDP一样,先创建socket套接字,然后填充服务器相关信息,之后关键的一步是向服务器发起连接,因为服务器正等着人连接呢

收发消息不止可以用read和write,还可以用recv和send,用法也是很简单的

其实客户端就是如此简单

echo_service_code

//TcpServer.hpp
#pragma once
#include <iostream>
#include <string>
#include<functional>
#include <cstdlib>
#include <cstring>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include<sys/wait.h>
#include <unistd.h>
#include<pthread.h>
#include "Log.hpp"
#include "InetAddr.hpp"
#include"Threadpool.hpp"enum ERRor
{USAGE_ERR = 1,SOCKET_ERR,BIND_ERR,LISTEN_ERR,};
class TcpServer;//声明一下struct threaddata
{threaddata(int fd,InetAddr&addr,TcpServer*tp):_fd(fd),_addr(addr),_self(tp){}int _fd;InetAddr _addr;TcpServer* _self;
};class TcpServer
{
public:TcpServer(uint16_t port) : _port(port), _listensock(-1){}void InitServer(){_listensock = socket(AF_INET, SOCK_STREAM, 0);if (_listensock < 0){LOG(FATAL, "socket err");exit(SOCKET_ERR);}LOG(INFO, "socket success");struct sockaddr_in server;bzero(&server, sizeof(server));server.sin_family = AF_INET;server.sin_port = htons(_port);server.sin_addr.s_addr = INADDR_ANY;int n = bind(_listensock, (struct sockaddr *)&server, sizeof(server));if (n < 0){LOG(FATAL, "bind err");exit(BIND_ERR);}LOG(INFO, "bind success");n = listen(_listensock, 16);if (n < 0){LOG(FATAL, "listen err");exit(LISTEN_ERR);}LOG(INFO, "listen success");}void service(int fd, InetAddr addr){LOG(INFO, "server get a new link,ip is %s,port is %d", addr.Ip().c_str(), addr.Port());while (true){char buffer[1024];int n = read(fd, buffer, sizeof(buffer) - 1);if (n > 0){buffer[n] = 0;LOG(INFO, "[%s:%d]#%s", addr.Ip().c_str(), addr.Port(), buffer);std::string message = "server echo#";message += buffer;write(fd, message.c_str(), message.size());}else if (n < 0) // 读取出错{LOG(ERROR, "read error");break;}else // n==0 client退出&&关闭连接{LOG(INFO, "[%s,%d] quit", addr.Ip().c_str(), addr.Port());break;}}}static void*newthreaddo(void*arg){pthread_detach(pthread_self());threaddata*ptd=static_cast<threaddata*>(arg);ptd->_self->service(ptd->_fd,ptd->_addr);delete ptd;return nullptr;}void StartServer(){while (true){struct sockaddr_in client;socklen_t len = sizeof(client);int fd = accept(_listensock, (struct sockaddr *)&client, &len);if (fd < 0){LOG(WARNING, "accept err");continue;}InetAddr addr(client);// service(fd, addr);单进程单线程// close(fd);// int f = fork();//多进程// if (f == 0)// {//     // son//     if (fork() == 0)//     {// grandson//         close(_listensock);//         service(fd, addr);//         close(fd);//     }//     exit(0);//son创建完孙子后立马退出,孙子有由系统领养// }// // father// close(fd);// waitpid(f,nullptr,0);// pthread_t t;//多线程// threaddata* ptd=new threaddata(fd,addr,this);// pthread_create(&t,nullptr,newthreaddo,ptd);thread_t t=std::bind(&TcpServer::service,this,fd,addr);//线程池ThreadPool<thread_t>::Getinstance()->Enqueue(t);}}~TcpServer(){}private:uint16_t _port;int _listensock;
};//TcpClient.cc
#include <iostream>
#include <string>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <unistd.h>
#include <cstring>
using std::cout;
using std::endl;void Usage(const char *arg)
{cout << "USAGE:" << endl;cout << "#         please enter:  " << arg << "serverip serverport" << endl;
}
// ./client 127.0.0.1 8888
int main(int argc, char *argv[])
{if (argc != 3){Usage(argv[0]);exit(1);}std::string ip(argv[1]);uint16_t port = std::stoi(argv[2]);int sockfd = socket(AF_INET, SOCK_STREAM, 0);if (sockfd < 0){cout << "socket err" << endl;exit(1);}cout << "socket success" << endl;struct sockaddr_in server;bzero(&server, sizeof(server));server.sin_family = AF_INET;server.sin_port = htons(port);server.sin_addr.s_addr = INADDR_ANY;int n = connect(sockfd, (struct sockaddr *)&server, sizeof(server));if (n < 0){cout << "connect err" << endl;exit(1);}cout << "connect success" << endl;while (true){cout << "please enter#";std::string what;std::getline(std::cin, what);send(sockfd, what.c_str(), what.size(), 0);char buffer[1024];int num = recv(sockfd, buffer, sizeof(buffer), 0);if (num > 0){buffer[num] = 0;cout << buffer << endl;}else if (num < 0){cout << "recv error" << endl;continue;}}return 0;
}

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

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

相关文章

Redis面试真题总结(四)

文章收录在网站&#xff1a;http://hardyfish.top/ 文章收录在网站&#xff1a;http://hardyfish.top/ 文章收录在网站&#xff1a;http://hardyfish.top/ 文章收录在网站&#xff1a;http://hardyfish.top/ AOF 持久化&#xff1f; AOF&#xff08;Append Only File&#x…

Selenium4.0实现自动搜索功能

01.Selenium4.0实现搜索功能 1.安装Selenium及查看Selenium版本 pip install selenium pip show seleniumfrom selenium import webdriver from chromedriver_py import binary_path import time from selenium.webdriver.common.by import By from selenium.webdriver.commo…

智能农业系统——土壤养分运移转化

橙蜂智能公司致力于提供先进的人工智能和物联网解决方案&#xff0c;帮助企业优化运营并实现技术潜能。公司主要服务包括AI数字人、AI翻译、AI知识库、大模型服务等。其核心价值观为创新、客户至上、质量、合作和可持续发展。 橙蜂智农的智慧农业产品涵盖了多方面的功能&#x…

鸿蒙OpenHarmony【轻量系统内核扩展组件(C++支持)】子系统开发

C支持 基本概念 C作为目前使用最广泛的编程语言之一&#xff0c;支持类、封装、重载等特性&#xff0c;是在C语言基础上开发的一种面向对象的编程语言。 运行机制 C代码的识别主要由编译器支持&#xff0c;系统主要对全局对象进行构造函数调用&#xff0c;进行初始化操作。…

H264-NAL

目录 错误日志NAL简介参考资料 错误日志 拉流时存在如下日志,会因为拉流失败导致之后的任务也停止 missing picture in access unit with size 16384 Invalid NAL unit size Error splitting the input into NAL units. 之后只要设置抓取异常后&#xff0c;重新拉流&#xff…

zabbix email 告警

配置媒介、触发器动作&#xff08;动作、操作&#xff09; 为用户 定义媒体&#xff0c;比如电子邮件地址 动作 - 条件

手机在网状态查询接口如何用PHP进行调用?

一、什么是手机在网状态查询接口&#xff1f; 手机在网状态查询接口&#xff0c;即输入手机号码查询手机号在网状态&#xff0c;返回有正常使用、停机、在网但不可用、不在网&#xff08;销号/未启用/异常&#xff09;、预销户等多种状态。 二、手机在网状态查询适用哪些场景…

数据结构(7.3_4)——红黑树的定义和性质

红黑树和平衡排序二叉树的查插删时间 平衡二叉树的适用场景&#xff1a;适用以查为主、很少插入/删除vd场景 红黑树&#xff1a;适用于频繁插入、删除的场景&#xff0c;实用性更强 红黑树的考点 红黑树的定义&#xff1a; 红黑树的二叉排序树&#xff1a;左子树结点值<…

EvilScience靶机详解

主机发现 arp-scan -l 得到靶机ip 192.168.229.152 端口扫描 nmap -sV -A -T4 192.168.1.20 这段代码使用 nmap 命令来扫描目标主机 192.168.1.20&#xff0c;并执行以下操作&#xff1a;-sV&#xff1a;探测开放的端口&#xff0c;以确定服务/版本信息。-A&#xff1a;启…

【软件工程】可行性研究

一、目的 二、任务 三、步骤 四、结果&#xff1a;可行性研究报告 例题 选择题

Java基础-零拷贝

文章目录 什么是零拷贝&#xff1f;传统IO执行过程零拷贝的意义零拷贝的主要实现方式实际应用场景零拷贝的优势零拷贝的局限性 Java 中的零拷贝实现FileChannel.transferTo()FileChannel.transferFrom() 相关知识点解释什么是DMA内核空间和用户空间什么是用户态、内核态什么是上…

前端——实现时钟 附带小例子

创建日期对象 toLocaleDateString() 获取日期 console.log(date.toLocaleDateString()) toLocaleTimeString() 获取时间 console.log(date.toLocaleTimeString()) toLocaleString() 获取日期和时间 console.log(date.toLocaleString()) date.getDay() 获取星期几 周日为…

计算机毕业设计选题推荐-基于python+Django的全屋家具定制服务平台

精彩专栏推荐订阅&#xff1a;在下方主页&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb; &#x1f496;&#x1f525;作者主页&#xff1a;计算机毕设木哥&#x1f525; &#x1f496; 文章目录 一、全屋家具定制…

H264参考帧列表管理

P/SP/B帧的参考帧列表的初始化 参考H.264标准文档的8.2.4.2章节&#xff0c;暂不研究场编码。在初始化P/SP帧或B帧的参考帧列表过程中&#xff0c;DPB中至少要存在一个有效的、即被标记为“用于短期或长期参考”的参考帧。 P/SP Slice 参考帧列表初始化 P/SP Slice参考帧列表…

《算法岗面试宝典》正式发布

大家好&#xff0c;历时半年完善&#xff0c;《算法岗面试宝典》 终于可以跟大家见面了。 最近 ChatGPT 爆火&#xff0c;推动了技术圈对大模型算法场景落地的热情&#xff0c;就业市场招聘人数越来越多&#xff0c;算法岗一跃成为竞争难度第一的岗位。 岗位方向 从细分方向…

汽车总线之----CAN总线

Introduction 早期的车辆网络是点对点的模式&#xff0c;臃肿繁杂且效率低下 现在是以总线的模式&#xff0c;很明显线路简洁清爽了很多。 高速CAN可以支持1M/s的速率&#xff0c;低速CAN可以支持125k/s的速率 CAN节点的内部结构图(Structure of CAN-Bus and electronic C…

*C++:list

一.list简介 1. list 是可以在常数范围内在任意位置进行插入和删除的序列式容器&#xff0c;并且该容器可以前后双向迭代。 2. list 的底层是双向链表结构&#xff0c;双向链表中每个元素存储在互不相关的独立节点中&#xff0c;在节点中通过指针指向其前一个元素和后一个元素…

redisson 延迟队列实现任务过期监听

一、需求&#xff1a; 任务超过一个小时以后&#xff0c;如果还为待执行状态&#xff0c;则自动转为结束状态。 二、实现: 创建延迟队列的监听任务RedisDelayedQueueListener&#xff0c;消费延迟队列&#xff1b;创建新增延迟队列的类&#xff0c;用于创建延迟队列&#xf…

ACM MM24 | Hi3D: 3D生成领域再突破!新视角生成和高分辨率生成双SOTA(复旦智象等)

文章链接&#xff1a;https://arxiv.org/pdf/2409.07452 Github 链接&#xff1a;https://github.com/yanghb22-fdu/Hi3D-Official 亮点直击 本文提出了高分辨率图像到3D模型&#xff08;Hi3D&#xff09;&#xff0c;这是一种基于视频扩散的新范式&#xff0c;将单个图像重新定…

Codeforces Round 974 (Div. 3) G. Milky Days

题目 题解 #include<bits/stdc.h> using namespace std; #define int long long #define ll long long #define ld long double #define pb push_back #define fi first #define se second #define pii pair<int, int> #define lson p << 1 #define rson p …