当前位置: 首页 > news >正文

计算机网络(tcp_socket )

计算机网络(tcp_socket )(一)

  • 1 有关tcp socket API
    • 1.1 创建套接字
    • 1.2 bind
    • 1.3 建立连接
    • 1.4 获取新的连接(accept)
    • 1.5 读、写信息(read,write)
    • 1.6 客户端发消息
  • 2 代码一(单进程客户端通信)
  • 3 代码一(多进程客户端通信)

1 有关tcp socket API

1.1 创建套接字

(1)TCP通信和UDP通信一样我们首先需要创建套接字(socket)

在这里插入图片描述

参数一:网络通信(AF_INET)
参数二:套接字类型(SOCK_STREAM)流式套接
参数三:0(默认就为TCP通信)

1.2 bind

在这里插入图片描述

这里跟UDP是一样的

在这里插入图片描述

总的来说,初始化服务器端,基本与UDP一致,仅有流式和数据报式的差别

1.3 建立连接

(1)将socket设置为监听状态

在这里插入图片描述

参数一:文件描述符(socket)
参数二:后序介绍(这里该参数不能为0,最好设置为16/32)

        // 3 TCP是面向连接的,所以TCP随时随地等待被连接n = ::listen(_listensockfd, BACKLOG); // 需要将socket设置为监听状态if(n<0){LOG(LogLevel::FATAL) << "listen error";Die(LISTEN_ERR);}

1.4 获取新的连接(accept)

(1)accept:从指定文件描述符(只负责接收新的连接)中获取新的连接 / 客户端

在这里插入图片描述

参数一:监听文件描述符(这里的fd只负责接收新的连接)
参数二:相当于UDP中recvfrom的倒数第二个参数
参数三:相当于UDP中recvfrom的最后一个参数
返回值:
成功:非0(文件描述符,真正提供服务的),失败:-1,没有客户端连接默认阻塞

1.5 读、写信息(read,write)

(1)TCP是通过文件描述符来实现功能的,所以TCP比UDP更像文件,且接口也是read

在这里插入图片描述

(2)写文件也是直接通过文件描述符来写

在这里插入图片描述

1.6 客户端发消息

(1)我们之前说过客户端是不需要显示的进行bind的,因为端口号是OS自动帮我们分配的,之前UDP发消息有sendto(参数里包含IP和PORT),但是TCP用的是write和read,明显用这个接口IP和PORT传入不了
(2)connect

在这里插入图片描述

参数一、文件描述符
参数二、ip地址和端口号
参数三、sockaddr的长度

2 代码一(单进程客户端通信)

(1)makefile

在这里插入图片描述

(2)TcpServer.hpp

#pragma once
#include <iostream>
#include <memory>
#include <cstring>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>#include "Log.hpp"
#include "Comm.hpp"
#include "InetAddr.hpp"using namespace LogModule;
const static uint16_t gport = 8080;
#define BACKLOG 8class TcpServer
{
public:TcpServer(int port = gport) : _port(port), _isrunning(false){}void InitServer(){// 1 创建socket_listensockfd = ::socket(AF_INET, SOCK_STREAM, 0);if (_listensockfd < 0){LOG(LogLevel::FATAL) << "socket error!";Die(SOCK_ERR);}LOG(LogLevel::INFO) << "socket create success, sockfd is : " << _listensockfd;// 2 bindstruct sockaddr_in local;memset(&local, 0, sizeof(local));local.sin_family = AF_INET;local.sin_port = htons(_port);local.sin_addr.s_addr = INADDR_ANY; // 云服务器建议绑任意IP地址int n = ::bind(_listensockfd, CONV(&local), sizeof(local));if (n < 0){LOG(LogLevel::FATAL) << "bind error";Die(BIND_ERR);}LOG(LogLevel::INFO) << "bind success";// 3 TCP是面向连接的,所以TCP随时随地等待被连接n = ::listen(_listensockfd, BACKLOG); // 需要将socket设置为监听状态if (n < 0){LOG(LogLevel::FATAL) << "listen error";Die(LISTEN_ERR);}LOG(LogLevel::INFO) << "listen success";}void HandlerRequest(int sockfd){char inbuff[4096];while (1){ssize_t n = ::read(sockfd, inbuff, sizeof(inbuff) - 1);LOG(LogLevel::INFO) << inbuff;if (n > 0){inbuff[n] = 0;std::string echo_str = "server echo# ";echo_str += inbuff;::write(sockfd, echo_str.c_str(), echo_str.size());}else if (n == 0)std::cout << "client quite" << std::endl;elsebreak;}::close(sockfd); // 文件描述符是有限的,如果不关会出现fd泄漏}void Start(){_isrunning = true;while (_isrunning){// 1、不断获取新的连接struct sockaddr_in peer;socklen_t peerlen=sizeof(peer);// 我们可以通过peer来获取客户端的ip和端口号int sockfd = ::accept(_listensockfd, CONV(&peer), &peerlen);if (sockfd < 0){LOG(LogLevel::WARNING) << "accept error";continue;}// 获取连接成功LOG(LogLevel::INFO) << "accept success, sockfd is: " << sockfd;InetAddr addr(peer);LOG(LogLevel::INFO) << "client info: " << addr.Addr();HandlerRequest(sockfd); // 处理请求}}void Stop(){_isrunning = false;}~TcpServer() {}private:int _listensockfd;uint16_t _port;bool _isrunning;
};

(3)TcpServerMain.cc

在这里插入图片描述

(4)TcpClientMain.cc

#include <iostream>
#include <string>
#include <cstring>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>// ./client server_ip server_port
int main(int argc, char *argv[])
{if (argc < 3){std::cout << "Usage error : ./client server_ip server_port" << std::endl;return 1;}std::string server_ip = argv[1];int server_port = std::stoi(argv[2]);int sockfd = ::socket(AF_INET, SOCK_STREAM, 0);if (sockfd < 0){std::cout << "socket create error" << std::endl;return 2;}// client 客户端不需要显示的bindstruct sockaddr_in server_addr;memset(&server_addr, 0, sizeof(server_addr));server_addr.sin_family = AF_INET;server_addr.sin_port = htons(server_port);server_addr.sin_addr.s_addr = inet_addr(server_ip.c_str());// connect 底层会自动进行bindint n = ::connect(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr));if (n < 0){std::cout << "connect error" << std::endl;return 3;}// echo clientstd::string message;while (1){char inbuff[1024];std::cout << "Please Enter $ ";std::getline(std::cin, message);n = ::write(sockfd, message.c_str(), message.size());if (n > 0){int m = ::read(sockfd, inbuff, sizeof(inbuff));if (m > 0){inbuff[m] = 0;std::cout << inbuff << std::endl;}elsebreak;}elsebreak;}::close(sockfd);return 0;
}

(5)需要的文件目录

在这里插入图片描述

3 代码一(多进程客户端通信)

只需要更改

#pragma once
#include <iostream>
#include <memory>
#include <cstring>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <sys/wait.h>
#include<signal.h>#include "Log.hpp"
#include "Comm.hpp"
#include "InetAddr.hpp"using namespace LogModule;
const static uint16_t gport = 8080;
#define BACKLOG 8class TcpServer
{
public:TcpServer(int port = gport) : _port(port), _isrunning(false){}void InitServer(){// 1 创建socket_listensockfd = ::socket(AF_INET, SOCK_STREAM, 0);if (_listensockfd < 0){LOG(LogLevel::FATAL) << "socket error!";Die(SOCK_ERR);}LOG(LogLevel::INFO) << "socket create success, sockfd is : " << _listensockfd;// 2 bindstruct sockaddr_in local;memset(&local, 0, sizeof(local));local.sin_family = AF_INET;local.sin_port = htons(_port);local.sin_addr.s_addr = INADDR_ANY; // 云服务器建议绑任意IP地址int n = ::bind(_listensockfd, CONV(&local), sizeof(local));if (n < 0){LOG(LogLevel::FATAL) << "bind error";Die(BIND_ERR);}LOG(LogLevel::INFO) << "bind success";// 3 TCP是面向连接的,所以TCP随时随地等待被连接n = ::listen(_listensockfd, BACKLOG); // 需要将socket设置为监听状态if (n < 0){LOG(LogLevel::FATAL) << "listen error";Die(LISTEN_ERR);}LOG(LogLevel::INFO) << "listen success";}void HandlerRequest(int sockfd){char inbuff[4096];while (1){ssize_t n = ::read(sockfd, inbuff, sizeof(inbuff) - 1);LOG(LogLevel::INFO) << inbuff;if (n > 0){inbuff[n] = 0;std::string echo_str = "server echo# ";echo_str += inbuff;::write(sockfd, echo_str.c_str(), echo_str.size());}else if (n == 0){std::cout << "client quite" << std::endl;break;}elsebreak;}::close(sockfd); // 文件描述符是有限的,如果不关会出现fd泄漏::signal(SIGCHLD,SIG_IGN);//忽略子进程的退出信号//OS会自动回收资源,不用waitpid了}void Start(){_isrunning = true;while (_isrunning){// 1、不断获取新的连接struct sockaddr_in peer;socklen_t peerlen=sizeof(peer);// 我们可以通过peer来获取客户端的ip和端口号int sockfd = ::accept(_listensockfd, CONV(&peer), &peerlen);if (sockfd < 0){LOG(LogLevel::WARNING) << "accept error";continue;}// 获取连接成功LOG(LogLevel::INFO) << "accept success, sockfd is: " << sockfd;InetAddr addr(peer);LOG(LogLevel::INFO) << "client info: " << addr.Addr();// HandlerRequest(sockfd); // 处理请求pid_t id = fork();if (id == 0){// child::close(_listensockfd); // 关闭子进程多余的文件描述符HandlerRequest(sockfd);exit(0);}::close(sockfd); // 关闭父进程多余的文件描述符}}void Stop(){_isrunning = false;}~TcpServer() {}private:int _listensockfd;uint16_t _port;bool _isrunning;
};
http://www.dtcms.com/a/498852.html

相关文章:

  • 【小白笔记】在编程中,如何将概念上的数据结构(比如“树”)转化为代码中具体的数据类型和对象
  • 【STM32项目开源】STM32单片机智能农业大棚控制系统
  • github开源笔记应用程序项目推荐-Joplin
  • 【Swift】LeetCode 438. 找到字符串中所有字母异位词
  • 【SoC】【W800】基于WM IoT SDK的环境搭建
  • BFS 与 DFS——力扣102.二叉树的层序遍历
  • 使用IOT-Tree的OPC UA Client连接器对接OPC UA Server获取数据到系统中
  • 优质网站建设在哪里wordpress分类目录名称
  • 专题一 之 【双指针】
  • 将Windows应用上架至Microsoft Store
  • 对LlamaFactory的一点见解
  • 紫金保险车险官方网站关键词优化营销
  • 大模型-智能体-【篇一:单智能体框架】
  • LLMs之MultiAgent:OpenAgents(创建AI智能体网络)的简介、安装和使用方法、案例应用之详细攻略
  • IDEA 中 Tomcat 部署 Java Web 项目(2)
  • [SCADE编译原理] 状态机到数据流的源到源翻译(2005)
  • 小迪安全v2023学习笔记(一百三十四讲)—— Windows权限提升篇数据库篇MySQLMSSQLOracle自动化项目
  • 2023年10月份04741计算机网络原理真题及答案
  • Room 概要
  • 元宇宙中的数字身份与数据主权:个体权益的守护与边界
  • 函数模板与类模板:C++泛型编程核心解析
  • [GO]Go语言包访问控制与导入机制
  • Flink细粒度滑动窗口性能优化与解决方案深度解析
  • Flink SQL 窗口函数详细
  • 成都网站建设的公司哪家好网站怎么推广出去
  • 【Go】--gin框架基本使用
  • [优选算法专题四.前缀和——NO.25一维前缀和]
  • openharmony之分布式相机开发:预览\拍照\编辑\同步\删除\分享教程
  • LeetCode 402 - 移掉 K 位数字
  • 皮卡丘XSS