TCP服务器—实现数据通信

news2024/11/18 19:34:03

目录

前言

1.接口介绍

2.编写服务器

3.编写客户端

4.编译链接

5.测试

6.总结


前言

        今天我们要介绍的是使用TCP协议实现数据通信,相比于之前写的UDP服务器实现数据信,在主体逻辑上并没有差别。客户端向服务器发送信息,服务器接受信息并回显,因为UDP是面向数据报,而TCP是面向连接的,所以在实现的时候接口上会有一些差别,下面,我们具体来看看UDP和TCP在编码的实现上有什么不同。

1.接口介绍

因为TCP是面向连接的,所以服务器创建完套接字,然后绑定成功后,将套接字设置为监听套接字

服务器启动之后,首先需要根据监听套接字建立连接,建立连接成功后返回一个新的文件描述符,后续的通信都是按照这个新的文件描述符按照读写文件的形式进行读写数据。

对于客户端来说创建完套接字之后,客户端启动之后首先需要建立连接

listen():设置sock为监听状态

 #include <sys/types.h>       
 #include <sys/socket.h>
 int listen(int sockfd, int backlog);

sockfd:创建套接字的返回值

backlog:底层全连接队列的长度

accept():服务端建立连接

#include <sys/types.h>         
#include <sys/socket.h>

int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

sockfd:监听套接字

struct sockaddr* addr:输出型参数,可以获取服务端的IP地址和port端口号

socklen_t* addrlen:结构体的大小

返回值:返回一个新打开的文件描述符

connect():客户端建立连接

#include <sys/types.h>        
#include <sys/socket.h>
int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);

sockfd:创建套接字返回值

struct sockaddr* addr:输出型参数,用来填写需要访问的服务端的IP地址和port端口号

socklen_t addrlen:结构体的大小

2.编写服务器

tcpServer.hpp

#pragma once

#include <iostream>
#include <string>
#include <cstring>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include "log.hpp"
namespace server
{
    using namespace std;
    enum
    {
        USAGE_ERR = 1,
        SOCKET_ERR,
        BIND_ERR,
        LISTEN_ERR
    };
    static const uint16_t gport = 8080;
    static const int gback = 5;
    class TcpServer
    {
    public:
        TcpServer(const uint16_t &port = gport)
            : _port(gport), _sock(-1)
        {}
        void InitServer()
        {
            _sock = socket(AF_INET, SOCK_STREAM, 0);
            if (_sock < 0)
            {
                logMessage(FATAL, "create socket error");
                exit(SOCKET_ERR);
            }
            logMessage(NORMAL, "create socket success");
            // 绑定:
            struct sockaddr_in local;
            local.sin_family = AF_INET;
            local.sin_port = htons(_port);
            local.sin_addr.s_addr = INADDR_ANY;
            if (bind(_sock, (struct sockaddr *)&local, sizeof(local)) < 0)
            {
                logMessage(FATAL, "bind socket error");
                exit(BIND_ERR);
            }
            logMessage(NORMAL, "bind socket success");
            // 设置sock为监听状态:
            if (listen(_sock, gback) < 0)
            {
                logMessage(FATAL, "listen socket error");
                exit(LISTEN_ERR);
            }
            logMessage(NORMAL, "listen socket success");
        }
        void start()
        {
            for (;;)
            {
                // 建立连接:
                struct sockaddr_in peer;
                socklen_t len = sizeof(peer);
                int sock = accept(_sock, (struct sockaddr *)&peer, &len); 
                if (sock < 0)
                {
                    logMessage(ERROR, "accept error, next");
                    continue;
                }
                logMessage(NORMAL, "accept a new link success");
                std::cout << "sock: " << sock << std::endl;
                //未来通信全部用sock,面向字节流的,后续全部都是文件操作:
                serviceIO(sock);
                close(sock);
            }
        }
        void serviceIO(int sock)
        {
            char buffer[1024];
            while(true)
            {
                ssize_t n = read(sock,buffer,sizeof(buffer)-1);
                if(n > 0)
                {
                    buffer[n] = 0;
                    cout << "recvice message: " << buffer << endl;
                    string outbuffer = buffer;
                    outbuffer += "[server echo]";
                    write(sock,outbuffer.c_str(),outbuffer.size());
                }
                else if(n == 0)
                {
                    // 代表client退出
                    logMessage(NORMAL, "client quit, me too!");
                    break;
                }
            }
        }
        ~TcpServer()
        {}
    private:
        int _sock;
        uint16_t _port;
    };
}

tcpServer.cc:启动服务器

#include"tcpServer.hpp"
#include<memory>
using namespace server;
static void Usage(string proc)
{
    cout << "\nUsage:\n\t" << proc << " local_port\n\n";
}
int main(int argc,char* argv[])
{
    if(argc != 2)
    {
        Usage(argv[0]);
        exit(USAGE_ERR);
    }
    uint16_t port = atoi(argv[1]);
    unique_ptr<TcpServer> tcs(new TcpServer(port));
    tcs->InitServer();
    tcs->start();
    return 0;
}

3.编写客户端

tcpClient.hpp

#pragma once

#include <iostream>
#include <string>
#include <cstring>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>

namespace client
{
    using namespace std;
    class TcpClient
    {
    public:
        TcpClient(const string& serverip,const uint16_t port)
        :_serverip(serverip),_port(port),_sock(-1)
        {}
        void InitClient()
        {
            _sock = socket(AF_INET,SOCK_STREAM,0);
            if(_sock < 0)
            {
                cerr << "create sock fail" << endl;
                exit(-1);
            }
        }
        void start()
        {
            //建立连接:
            struct sockaddr_in server;
            server.sin_family = AF_INET;
            server.sin_port = htons(_port);
            server.sin_addr.s_addr = inet_addr(_serverip.c_str());
            if(connect(_sock,(struct sockaddr*)&server,sizeof(server)) != 0)
            {
                cerr << "connect fail" << endl;
            }
            else
            {
                string message;
                while(true)
                {
                    cout << "Please Enter: ";
                    getline(cin,message);
                    write(_sock,message.c_str(),message.size());
                    char buffer[1024];
                    int n = read(_sock,buffer,sizeof(buffer)-1);
                    if(n > 0)
                    {
                        buffer[n] = 0;
                        cout << "Server回复: " << buffer << endl;
                    }
                    else
                    {
                        break;
                    }
                }
            }
        }
        ~TcpClient()
        {
            if(_sock >= 0)
                close(_sock);
        }
    private:
        string _serverip;
        uint16_t _port;
        int _sock;
    };
} // namespace client

tcpClient.cc:启动客户端

#include"tcpClient.hpp"
#include<memory>
using namespace client;
static void Usage(string proc)
{
    cout << "\nUsage:\n\t" << proc << " serverip serverport\n\n";
}
int main(int argc,char* argv[])
{
    if(argc != 3)
    {
        Usage(argv[0]);
        exit(-1);
    }
    uint16_t port = atoi(argv[2]);
    string ip = argv[1];
    unique_ptr<TcpClient> tcc(new TcpClient(ip,port));
    tcc->InitClient();
    tcc->start();
    return 0;
}

4.编译链接

makefile:

.PHONY:all
all:tcpServer tcpClient
tcpServer:tcpServer.cc
	g++ -o $@ $^ -std=c++11
tcpClient:tcpClient.cc
	g++ -o $@ $^ -std=c++11
.PHONY:clean
clean:
	rm tcpServer tcpClient

5.测试

 如图所示,服务端和客户端可以完成正常的数据通信了。

6.总结

        TCP协议和UDP协议在数据通信的实现中,除了一些接口使用的不同之外,其实并没有太大的不同,在之前说的UDP是面向数据报的而TCP是面向字节流的,这些特性又是如何体现的呢?关于这个问题,博主将在后面的文章中会为大家继续进行介绍。不要错过哦!

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

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

相关文章

如何进行无线网络渗透测试?

今天我们将继续深入探讨Kali Linux的应用&#xff0c;这次我们将重点介绍如何使用Kali Linux进行无线网络渗透测试。无线网络渗透测试是评估无线网络安全性的重要步骤&#xff0c;而Kali Linux作为一款专业的渗透测试发行版&#xff0c;提供了丰富的工具来帮助你进行这项任务。…

支持https访问

文章目录 1. 打开自己的云服务器的 80 和 443 端口2. 安装 nginx3. 安装 snapd4. 安装 certbot5. 生成证书6. 拷贝生成的证书到项目工作目录7. 修改 main.go 程序如下8. 编译程序9. 启动程序10. 使用 https 和端口 8081 访问页面成功11. 下面修改程序&#xff0c;支持 https 和…

【RocketMQ】NameServer总结

NameServer是一个注册中心&#xff0c;提供服务注册和服务发现的功能。NameServer可以集群部署&#xff0c;集群中每个节点都是对等的关系&#xff08;没有像ZooKeeper那样在集群中选举出一个Master节点&#xff09;&#xff0c;节点之间互不通信。 服务注册 Broker启动的时候会…

grafana-zabbix基础操作篇------导入数据源

文章目录 一、grafana的安装1.1、下载地址1.2、下载后导入所安装机器1.3、yum安装解决依赖1.4、启动grafana1.5、查看端口是否启用&#xff08;端口默认3000&#xff09;1.6、浏览器访问 二、添加zabbix数据源2.1、导入数据源 **下一篇 我们讲讲构建仪表板的操作** 今天&#x…

SpringMVC拦截器的介绍,拦截器的基本实现,拦截器链配置

&#x1f40c;个人主页&#xff1a; &#x1f40c; 叶落闲庭 &#x1f4a8;我的专栏&#xff1a;&#x1f4a8; c语言 数据结构 javaweb 石可破也&#xff0c;而不可夺坚&#xff1b;丹可磨也&#xff0c;而不可夺赤。 拦截器 一、拦截器概念二、拦截器与过滤器的区别三、拦截器…

【ChatGLM】ChatGLM-6B模型Win+4GB显卡本地部署笔记

ChatGLM-6B是清华大学知识工程和数据挖掘小组发布的一个类似ChatGPT的开源对话机器人&#xff0c;由于该模型是经过约1T标识符的中英文训练&#xff0c;且大部分都是中文&#xff0c;因此十分适合国内使用。 预期环境 本机电脑备注&#xff1a; Win10专业版 32G内存256固态系统…

【BASH】回顾与知识点梳理(三十一)

【BASH】回顾与知识点梳理 三十一 三十一. 进程的管理31.1 给进程发送讯号kill -signal PIDlinux系统后台常驻进程killall -signal 指令名称 31.2 关于进程的执行顺序Priority 与 Nice 值nice &#xff1a;新执行的指令即给予新的 nice 值renice &#xff1a;已存在进程的 nice…

绿盾客户端文件加密不显示锁的图标,加密功能正常

环境: 绿盾客户端7.0 Win10 专业版 问题描述: 绿盾客户端文件加密不显示锁的图标,加密功能正常 解决方案: 1.查看控制台是否设置隐藏图标 (未解决) 控制台-规则中心-安全选项-“加密文件显示加密图标”和“不显示Explorer 鼠标右键菜单”是否打钩 如果没打钩,则不…

(学习笔记-进程管理)怎么避免死锁?

死锁的概念 在多线程编程中&#xff0c;我们为了防止多线程竞争共享资源而导致数据错乱&#xff0c;都会在操作共享资源之前加上互斥锁&#xff0c;只有成功获得到锁的线程&#xff0c;才能操作共享资源&#xff0c;获取不到锁的线程就只能等待&#xff0c;直到锁被释放。 那…

(分治) 剑指 Offer 16. 数值的整数次方 ——【Leetcode每日一题】

❓剑指 Offer 16. 数值的整数次方 难度&#xff1a;中等 实现 pow(x, n) &#xff0c;即计算 x 的 n 次幂函数&#xff08;即&#xff0c; x n x^n xn&#xff09;。不得使用库函数&#xff0c;同时不需要考虑大数问题。 示例 1&#xff1a; 输入&#xff1a;x 2.00000, n …

Linux系统安装Google Chrome

1.进入谷歌浏览器官网 Google Chrome - Download the Fast, Secure Browser from GoogleGet more done with the new Google Chrome. A more simple, secure, and faster web browser than ever, with Google’s smarts built-in. Download now.http://www.google.cn/intl/en_…

数学建模之“TOPSIS数学模型”原理和代码详解

一、简介 TOPSIS&#xff08;Technique for Order Preference by Similarity to Ideal Solution&#xff09;是一种多准则决策分析方法&#xff0c;用于解决多个候选方案之间的排序和选择问题。它基于一种数学模型&#xff0c;通过比较每个候选方案与理想解和负理想解之间的相…

图卷积网络:GNN 简介【01/4】

图片来源&#xff1a;作者 一、说明 图形神经网络 &#xff08;GNN&#xff09; 代表了深度学习领域最迷人、发展最迅速的架构之一。作为旨在处理结构化为图形的数据的深度学习模型&#xff0c;GNN 带来了非凡的多功能性和强大的学习能力。 在各种类型的GNN中&#xff0c;图卷…

使用Java服务器实现UDP消息的发送和接收(多线程)

目录 简介&#xff1a;1. 导入必要的库2. 创建服务器端代码3. 创建客户端代码4. 实现多线程处理5. 测试运行示例代码&#xff1a;函数说明服务器端代码说明&#xff1a;客户端代码说明&#xff1a; 总结&#xff1a; 简介&#xff1a; 在本篇博客中&#xff0c;我们将介绍如何…

Spring的三种异常处理方式

1.SpringMVC 异常的处理流程 异常分为编译时异常和运行时异常&#xff0c;编译时异常我们 try-cache 进行捕获&#xff0c;捕获后自行处理&#xff0c;而运行时异常是不 可预期的&#xff0c;就需要规范编码来避免&#xff0c;在SpringMVC 中&#xff0c;不管是编译异常还是运行…

激活函数总结(十):激活函数补充(Identity、LogSigmoid、Bent Identity)

激活函数总结&#xff08;十&#xff09;&#xff1a;激活函数补充 1 引言2 激活函数2.1 Identity激活函数2.2 LogSigmoid激活函数2.3 Bent Identity激活函数 3. 总结 1 引言 在前面的文章中已经介绍了介绍了一系列激活函数 (Sigmoid、Tanh、ReLU、Leaky ReLU、PReLU、Swish、…

【校招VIP】测试方案之等价类

考点介绍&#xff1a; 在校招中&#xff0c;等价类也是重要的考查点。等价类划分是一种典型的黑盒测试设计方法&#xff0c;使用该方法主要对测试子项进行测试规格分析&#xff0c;得到用例&#xff0c;而不用对系统内部处理进行深入了解&#xff0c;它也是目前测试设计过程中使…

Python Opencv实践 - 图像金字塔

import cv2 as cv import numpy as np import matplotlib.pyplot as pltimg cv.imread("../SampleImages/pomeranian.png", cv.IMREAD_COLOR) print(img.shape)#图像上采样 #cv.pyrUp(src, dstNone, dstsizeNone, borderTypeNone) #参考资料&#xff1a;https://blo…

大势智慧软硬件技术答疑第九期

1.在使用模方修模大面积水面时&#xff0c;原模型没有瓦片的区域修补不了&#xff0c;软件也没有新增瓦片&#xff0c;怎么解决&#xff1f; 答&#xff1a;规则分块的数据&#xff0c;然后用新版本模方处理&#xff0c;设置好对应的空间框架。 2.重建大师密集匹配-纠正影像失败…