UDP套接字的通信(实现英汉互译/程序替换/多线程聊天室/Windows与Linux通信)

news2024/9/27 23:32:40

实现英汉互译

思路

我们在客户端发英文,服务端做翻译工作,让翻译好的中文再次发给我们的客户端,然后打印出来。

服务端代码

翻译的操作

创建一个txt文件里面包含英汉互译的数据

dict.txt

banana:香蕉
apple:苹果
pig:猪
beef:牛肉
hello:你好

对txt中的数据进行操作

分割函数

将英汉通过冒号分开。

// 分割函数
static bool cutString(const string &target, string *s1, string *s2, const string &sep)
{
    // apple:苹果
    auto pos = target.find(sep);

    if (pos == string::npos)
    {
        return false;
    }

    *s1 = target.substr(0, pos);
    *s2 = target.substr(pos + sep.size());

    return true;
}

将文件数据插入map里面

// 按行将文件里面的数据给插入到map里面
static void initDict()
{
    dict.clear();
    ifstream in(dictTxt, std::ios::binary);
    if (!in.is_open())
    {
        std::cerr << "open file " << dictTxt << " error" << endl;
        exit(OPEN_ERR);
    }

    string line;
    std::string key, value;

    while (getline(in, line))
    {
        // cout << line << endl;
        if (cutString(line, &key, &value, ":"))
        {
            dict.insert(make_pair(key, value));
        }
    }

    in.close();

    cout << "load dict success" << endl;
}

重新加载文件

通过捕捉2号(ctrl c)信号来进行重新加载文件。

void reload(int signo)
{
    (void)signo;
    initDict();
}

// ./udpClient server_ip server_port
int main(int argc, char *argv[])
{
   
    signal(2, reload); // 通过发2号信号来使dict.txt中的数据进行更新

    
}

网络通信的操作

将翻译后的数据发送给客户端

void handlerMessage(int sockfd, string message, uint16_t clientport, string clientip)
{
    // 就可以对message进行特定的业务处理,而不关心message怎么来的 --- server通信和业务逻辑解耦!
    // 婴儿版的业务逻辑
    string response_message;
    auto iter = dict.find(message);
    if (iter == dict.end())
        response_message = "unknown";
    else
        response_message = iter->second;

    // 开始返回
    struct sockaddr_in client;
    bzero(&client, sizeof(client));

    client.sin_family = AF_INET;
    client.sin_port = htons(clientport);
    client.sin_addr.s_addr = inet_addr(clientip.c_str());

    // 在服务端收到客户端数据的时候我们获取到了客户端的端口号和ip,因此回调到了这个函数中,
    // 此时我们就可以使用客户端的端口号和ip来给客户端发送翻译后的信息
    sendto(sockfd, response_message.c_str(), response_message.size(), 0, (struct sockaddr *)&client, sizeof(client));
}

客户端代码

创建socket

void initClient()
{
    // 1. 创建socket
    _sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if (_sockfd == -1)
    {
        cerr << "socket error: " << errno << " : " << strerror(errno) << endl;
        exit(2);
    }
    cout << "socket success: " << _sockfd << endl;

    // 2. client要不要bind[不需要的]  , client要不要显示的bind,需不需要程序员自己bind? 不需要
    // 写服务器的一家公司,写客户端是无数家公司 -- 因此让OS自动形成端口进行bind! -- OS在什么时候,如何bind
}

数据处理

将用户输入的数据发送给服务端,并且接受服务端翻译后的数据并进行打印。

void run()
{

    struct sockaddr_in server;
    memset(&server, 0, sizeof(server));
    server.sin_family = AF_INET;
    server.sin_addr.s_addr = inet_addr(_serverip.c_str());
    server.sin_port = htons(_serverport);

    string message;
    char buffer[1024];
    while (!_quit)
    {
        // fprintf(stderr, "Please Enter# ");
        // fflush(stderr);

        // fgets(buffer, sizeof(buffer), stdin);

        cout << "Please Enter# ";
        cin >> message;

        // buffer[strlen(buffer) - 1] = 0;

        // message = buffer;

        sendto(_sockfd, message.c_str(), message.size(), 0, (struct sockaddr *)&server, sizeof(server));

        char recv_buffer[1024];
        // 接受翻译后的信息
        struct sockaddr_in peer;
        socklen_t len = sizeof(peer);
        //
        ssize_t s = recvfrom(_sockfd, recv_buffer, sizeof(recv_buffer) - 1, 0, (struct sockaddr *)&peer, &len);
        if (s > 0)
        {
            // 读取数据
            buffer[s] = 0;
        }
        cout << "服务器翻译成# " << recv_buffer << endl;
        // recv_buffer[0] = 0;
        memset(recv_buffer, 0, sizeof(buffer)); // 清空缓冲区
    }
}

成果展示

程序替换

思路

主要使用popen接口同时实现管道+创建子进程+程序替换的功能。将我们客户端输入的命令信息发送给服务端。服务端将该命令经过popen接口让它执行命令运行出来的结果放在一个文件中,然后在将文件中的内容读取出来发送给客户端。

popen接口

#include <stdio.h>

FILE *popen(const char *command,const char *type);  // 相当于pipe+fork+exec* 

int pclose(FILE *stream);

参数

const char *command

未来要执行的命令字符串: 

ls -a -l 等  可以将这些命令执行的结果返回到一个文件当中

const char *type

对文件的操作方式"r" "w" "a" 等

返回值

返回 nullptr 说明执行失败了

服务端代码

void execCommand(int sockfd, string cmd, uint16_t clientport, string clientip)
{
    // 1. com解析,ls -a -l
    // 2. 如果必要,可能需要fork,exec*

    if (cmd.find("rm") != string::npos || cmd.find("mv") != string::npos || cmd.find("remdir") != string::npos)
    {
        cerr << clientip << " : " << clientport << " 正在做一个非法的操作: " << cmd << endl;
        return;
    }

    string response;
    FILE *fp = popen(cmd.c_str(), "r");

    if (fp == nullptr)
        response = cmd + " exec failed";

    char line[1024];
    // 按行读取
    while (fgets(line, sizeof(line), fp))
    {
        response += line;
    }

    pclose(fp);

    // 开始返回
    struct sockaddr_in client;
    bzero(&client, sizeof(client));

    client.sin_family = AF_INET;
    client.sin_port = htons(clientport);
    client.sin_addr.s_addr = inet_addr(clientip.c_str());

    // 在服务端收到客户端数据的时候我们获取到了客户端的端口号和ip,因此回调到了这个函数中,
    // 此时我们就可以使用客户端的端口号和ip来给客户端发送翻译后的信息
    sendto(sockfd, response.c_str(), response.size(), 0, (struct sockaddr *)&client, sizeof(client));
}

成果展示 

多线程聊天室

思路

通过在客户端的角度创建多线程,一个线程进行发消息,一个线程进行读消息。这样我们就能让多个线程(用户)一起聊天。

让用户的id(ip + "-" + port)作为key值,User作为value值。来形成一个个unordered_map类型的容器。如果用户上线了就将该对象添加到unordered_map容器里面,下线就从unordered_map容器删除。

通过输入"online"来将该用户添加到unordered_map容器里面,意味上线。

通过输入"offline"来将该用户从unordered_map容器里面删除,意味下线。

我们服务端收到客户端发来的数据的时候通过isOnline函数来判断该用户是否在unordered_map容器里面,如果在就将该信息发送给客户端并且客户端接受后打印出来,如果不在直接打印"未上线"。

用户信息代码代码 onlineUser.hpp

#pragma once

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

using namespace std;

class User
{
public:
    User(const string &ip, const uint16_t &port)
        : _ip(ip), _port(port)
    {
    }

    ~User()
    {
    }

    string ip()
    {
        return _ip;
    }
    uint16_t port()
    {
        return _port;
    }

private:
    string _ip;
    uint16_t _port;
};

class OnlineUser
{
public:
    OnlineUser() {}
    ~OnlineUser() {}
    void addUser(const string &ip, const uint16_t &port)
    {
        string id = ip + "-" + to_string(port);
        users.insert(make_pair(id, User(ip, port)));
    }
    void delUser(const string &ip, const uint16_t &port)
    {
        string id = ip + "-" + to_string(port);
        users.erase(id);
    }

    bool isOnline(const string &ip, const uint16_t &port)
    {
        string id = ip + "-" + to_string(port);
        return users.find(id) == users.end() ? false : true;
    }

    void broadcastMessage(int sockfd, const string &ip, const uint16_t &port, const string &message)
    {
        for (auto &user : users)
        {
            // 开始返回
            struct sockaddr_in client;
            bzero(&client, sizeof(client));

            client.sin_family = AF_INET;
            client.sin_port = htons(user.second.port());
            client.sin_addr.s_addr = inet_addr(user.second.ip().c_str());
            // 将用户id也加入
            string s = ip + "-" + to_string(port) + "# ";
            s += message;
            sendto(sockfd, s.c_str(), s.size(), 0, (struct sockaddr *)&client, sizeof(client));
        }
    }

private:
    unordered_map<string, User> users;
};

服务端代码

// demo 3
void routeMessage(int sockfd, string message, uint16_t clientport, string clientip)
{
    // 判断是否上线
    // 上线就将该用户添加到onlineuser
    if (message == "online")
        onlineuser.addUser(clientip, clientport);
    // 下线就将该用户在onlineuser中删除
    if ((message == "offline"))
        onlineuser.delUser(clientip, clientport);

    // 如果以下if为真 那么说明用户已经上线,因此需要将用户发的信息进行路由
    if (onlineuser.isOnline(clientip, clientport))
    {
        // 消息的路由
        onlineuser.broadcastMessage(sockfd, clientip, clientport, message);
    }
    else
    {
        // 开始返回
        struct sockaddr_in client;
        bzero(&client, sizeof(client));

        client.sin_family = AF_INET;
        client.sin_port = htons(clientport);
        client.sin_addr.s_addr = inet_addr(clientip.c_str());

        string response_message = "你还没有上线,请先上线,运行: online";

        sendto(sockfd, response_message.c_str(), response_message.size(), 0, (struct sockaddr *)&client, sizeof(client));
    }

客户端代码(多线程)

static void *readMessage(void *args)
{
    int sockfd = *(static_cast<int *>(args));
    pthread_detach(pthread_self());

    while (true)
    {
        char buffer_recv[1024];
        struct sockaddr_in peer;
        socklen_t len = sizeof(peer);

        ssize_t s = recvfrom(sockfd, buffer_recv, sizeof(buffer_recv) - 1, 0, (struct sockaddr *)&peer, &len);

        if (s >= 0)
            buffer_recv[s] = 0;

        cout << buffer_recv << endl;
    }
    return nullptr;
}
void run()
{
    pthread_create(&_reader, nullptr, readMessage, (void *)&_sockfd);

    struct sockaddr_in server;
    memset(&server, 0, sizeof(server));
    server.sin_family = AF_INET;
    server.sin_addr.s_addr = inet_addr(_serverip.c_str());
    server.sin_port = htons(_serverport);

    string message;
    char buffer[1024];
    while (!_quit)
    {
        fprintf(stderr, "Please Enter# ");
        fflush(stderr);

        fgets(buffer, sizeof(buffer), stdin);

        buffer[strlen(buffer) - 1] = 0;

        message = buffer;

        sendto(_sockfd, message.c_str(), message.size(), 0, (struct sockaddr *)&server, sizeof(server));
    }

成果展示

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

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

相关文章

Liux上使用POI将Word转PDF中文字体无法显示问题解决

错误信息&#xff1a; Windows测试下载PDF没有任何问题&#xff0c;打包上传Linux服务器以后下载PDF只显示数字。 解决方法&#xff1a; 1、Word转换PDF成功但是中文显示不出都是因为字体的问题 下面的方法简单粗暴&#xff0c;但是解决了我的问题&#xff1a; a、将C:\Wind…

面试官:“你还有什么想问我的吗?”(攻略版,建议收藏系列)

面试官&#xff1a;“你还有什么想问我的吗&#xff1f;”&#xff08;攻略版&#xff0c;建议收藏系列&#xff09; 此类问题大致分为三种情形&#xff1a;部门负责人面、HR面、高层领导面。 一、部门负责人面 【判断自己的业务能力和性格特点是否与岗位匹配度高】 此时&am…

ADB usage

查看手机设备的信息 获取设备的Android版本号 adb shell getprop ro.build.version.release 获取设备的API版本号 adb shell getprop ro.build.version.sdkAdb 获得 sdk版本 adb shell getprop ro.build.version.sdk27 Adb 获得Android版本 adb shell getprop ro.build.vers…

深入了解glibc的互斥锁的加锁过程

深入了解glibc的互斥锁 互斥锁是多线程同步时常用的手段&#xff0c;使用互斥锁可以保护对共享资源的操作。共享资源也被称为临界区&#xff0c;当一个线程对一个临界区加锁后&#xff0c;其他线程就不能进入该临界区&#xff0c;直到持有临界区锁的线程释放该锁。 本文以gli…

品达通用权限系统-Day03

文章目录 1. 概述2. lombok&#xff08;编码效率工具&#xff09;2.1 lombok 简介2.2 安装lombok插件2.3 lombok常用注解2.4 lombok入门案例 3. Nacos&#xff08;服务注册和配置中心&#xff09;4. Redis&#xff08;Windows版安装及使用&#xff09; 1. 概述 本节主要讲述&a…

同城上门送酒小程序 uniapp用户端+vue/php后端+商家端+配送端源码

前端uniapp 跨平台框架 后端php vue.js框架 php7.2 mysql数据库 mysql5.6 <template> <view> <view id"mainPage" :style"{height:MainPageHeightrpx}"> <PageHome v-show"showPageinitIndex" …

序列到序列学习

将最后时刻的隐藏状态传给解码器。特定的“”表示序列开始词元&#xff0c;表示开始翻译。将此次翻译的结果作为下一次的输入&#xff0c;并将隐藏状态传递给下一时刻。最后可以拿到整个语言句子的输出。 将编码器最后一次的隐藏状态与解码器的第一次的输入&#xff0c;放在一…

工具篇9--Window 虚拟机安装

文章目录 前言一、虚拟机是什么&#xff1f;二、虚拟机安装1.下载虚拟机软件&#xff1a;2.下载centos 系统镜像&#xff1a;3.虚拟机安装&#xff1a;3.1 关闭杀毒软件&#xff1a;3.2 重启后继续安装&#xff1a;3.3 修改vm 安装的位置&#xff1a;3.4 勾掉用户体验后下一步完…

PostgreSQL Log 日志模块详解

本文讲的是操作日志&#xff0c;非 WAL 日志。 文章目录 背景日志模块原理Syslogger 核心模块日志消息通信日志轮转问题一问题二问题三问题四问题五 存在的问题刷盘性能日志轮转 参考资料 背景 PG 的日志模块是一个相对独立的模块&#xff0c;主要功能就是打印用户的操作日志以…

【MATLAB第46期】基于MATLAB的改进模糊卷积神经网络IFCNN分类预测模型

【MATLAB第46期】基于MATLAB的改进模糊卷积神经网络IFCNN多分类预测模型 一、展示效果 二、思路 在正常CNN卷积神经网络训练阶段之后&#xff0c;使用进化算法&#xff08;蜜蜂算法&#xff09;拟合深度学习权重和偏差。 本文案例数据中&#xff0c; 用深度模型进行4分类预测…

vmware安装centos将home磁盘合并至root下

使用vmware安装centos后&#xff0c;发现分的盘60G&#xff0c;其中有17G分到了home盘&#xff0c;现在想只用一个盘进行统一管理&#xff0c;于是将home盘删除掉&#xff0c;再合并到root盘下&#xff0c;这里是直接删除掉home,没有备份数据&#xff0c;步骤如下&#xff1a; …

时间表R(t) 和 学习曲线learning curve

import numpy as np import matplotlib.pyplot as plt# 设置参数 a1 2 a2 0.1 a3 0.1 a4 1 T 10# 生成曲线数据 t np.linspace(0, 20, 1000) y np.exp(-a2 * t**a1) a3 * (t / T)**a4# 绘制曲线 plt.plot(t, y) plt.xlabel(t) plt.ylabel(R(t)) plt.title(Evolution of…

亚马逊云科技发起“可持续发展伙伴计划” ,实现降本增效、安全合规的上云价值

6月27日&#xff0c;“2023亚马逊云科技中国峰会”在上海世博中心盛大启幕&#xff01; 在与全球客户的交流中&#xff0c;亚马逊云科技发现很多企业都在三个方面不断创建未雨绸缪&#xff1a;首先&#xff0c;降本增效&#xff1b;其次&#xff0c;保证业务安全合规&#xff…

vscode如何创建自定义快捷键模板(typescript React示例)

1.vs面板左下角设置-配置用户代码片段 2. 弹出搜索框中输入typescript会出来2个选项&#xff0c;选择第二个react 3.在代码片段中添加自己的快捷键设置片段&#xff08;用$TM_FILENAME_BASE$1可以获取当前文件的名称&#xff09; {// Place your snippets for typescriptreact…

python基础案例题(进制转换、字符串加密的实现、猜拳游戏、多种方法计算π)

前言 大家早好、午好、晚好吖 ❤ ~欢迎光临本文章 环境使用: Python 3.8 Pycharm 专业版 1.进制转换 功能&#xff1a;获取十进制整数的二进制串&#xff0c;相当于内置函数bin。 算法分析&#xff1a; 对2辗转相除&#xff0c;直到商为0 每次所得余数逆序即可 流程图…

playerdemo开源项目win运行详细配置

playerdemo开源项目win运行详细配置 在项目同目录建立文件夹lib 一、下载ffmpeg 下载32位的ffmpeg&#xff0c;放在lib/ffmpeg路径下 二、下载sdl2 下载sdl2也放在 lib/sdl2路径下 三、配置 .pro文件 win32 { LIBS -L$$PWD/lib/SDL2/lib/x86 \-L$$PWD/lib/ffmpeg-4.2.…

springboot增加logback日志记录ip

1、增加logback配置文件&#xff1a; public class IPLogConfig extends ClassicConverter {Overridepublic String convert(ILoggingEvent event) {RequestAttributes requestAttributes RequestContextHolder.getRequestAttributes();if (requestAttributes null) {return…

could not read ok from ADB Server

ADB不能连接&#xff1a; D:\adb\platform-tools>adb.exe devices * daemon not running; starting now at tcp:5037 could not read ok from ADB Server * failed to start daemon adb.exe: failed to check server version: cannot connect to daemon关闭防火墙可以解决。…

星辰秘典:揭示Python项目的宇宙奥秘——宇宙星空模拟器

✨博主&#xff1a;命运之光 &#x1f338;专栏&#xff1a;Python星辰秘典 &#x1f433;专栏&#xff1a;web开发&#xff08;html css js&#xff09; ❤️专栏&#xff1a;Java经典程序设计 ☀️博主的其他文章&#xff1a;点击进入博主的主页 前言&#xff1a;你好&#x…

Unity | HDRP高清渲染管线学习笔记:Post-processing后处理效果

目录 一、后处理效果顺序 二、16个后处理效果 1. Tonemapping&#xff08;色调映射&#xff09; 2.White Balance&#xff08;白平衡&#xff09; 3. Bloom&#xff08;泛光&#xff09; 3.1 Quality 3.2 Bloom 3.2.1 Threshold&#xff08;临界值&#xff09; 3.2.2 I…