项目:TCP在线云词典

news2024/10/7 19:29:04

在这里插入图片描述

一.要求

1.搭建的框架环境中实现并发,实现多个用户同时查询的功能。
2.服务器分别保存每个用户的使用记录,客户端可以查询日志的功能。
3.基本的查询单词的功能。
4.密码验证的功能,实现登录验证账号和密码是否正确。

二.流程和框架

框架
在这里插入图片描述

客户端
![2023-09-03T10:44:22.png][2]

服务器
在这里插入图片描述

三.思路

1.首先你要准备好单词文件,用于英语单词的查询。
dict.txt

2.该项目涉及多并发问题,可以使用多进程,多线程,IO多路复用中的一种,我这里采用IO多路复用的select实现。

3.实现日志功能,需要建立一个数据库,为每一个用户建立一个表,表中存储用户的各种记录。
4.密码验证,同样使用数据库完成,每次登录时,将和数据库中所有的用户信息比较,若账号密码正确,即可登录。

在这里插入图片描述
Linux的IO多路复用是一种高效的IO处理机制,通过允许一个线程同时监控多个文件描述符(包括套接字、管道等)的IO事件,从而避免了传统的多线程或多进程方式中的频繁的上下文切换和资源消耗。在Linux系统中,IO多路复用主要基于以下三种机制:select、poll和epoll。

  1. select:select是最早引入的IO多路复用机制之一。它通过select系统调用来监控多个文件描述符上的IO事件,一旦有IO事件发生,就会通知应用程序进行处理。然而,select的一个缺点是每次调用都需要将所有的文件描述符从应用程序空间复制到内核空间,造成资源浪费。

  2. poll:poll是对select的改进,它也能够监控多个文件描述符上的IO事件,并将有IO事件发生的文件描述符返回给应用程序。与select不同的是,poll使用了链表数据结构来存储文件描述符,减少了在内核空间和应用程序空间之间的数据复制。

  3. epoll:epoll是Linux特有的高性能IO多路复用机制。它通过epoll系统调用来注册、注销和监控文件描述符上的IO事件。epoll采用事件驱动的方式,只会返回有IO事件发生的文件描述符,避免了无效的遍历和资源浪费。此外,epoll还提供了三种工作模式:EPOLL_CTL_ADD(添加文件描述符)、EPOLL_CTL_MOD(修改文件描述符)和EPOLL_CTL_DEL(删除文件描述符),更加灵活和高效。

IO多路复用在网络编程中特别有用,可以用于实现高并发的服务器。通过IO多路复用,可以在单线程或少量线程的情况下同时处理多个连接的IO事件,提高服务器的并发性能和效率。

总结而言,Linux的IO多路复用是一种有效的IO处理机制,通过select、poll和epoll等机制,可以实现高效的监控和处理多个IO事件,提高系统的并发性能和效率。
四.具体实现代码

head.h

#ifndef __HEAD_H__
#define __HEAD_H__
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>
#include <semaphore.h>


/* 消息对应的结构体(同一个协议) */
typedef struct msg_t
{
    int type;       
    char name[32];  //用户名
    char text[256]; //消息正文
    char password[32]; //密码
} MSG_t;


enum type_t
{
  my__register=1,  //注册
  login,         //连接  
  word,          //查询单词
  history,       //查询日志   
  quit,      
};
#endif

client.c

#include "head.h"
struct sockaddr_in saddr;
int socked;
MSG_t msg;
//注册帐号函数
void my_register()
{
    MSG_t msg;
    msg.type = my__register;
    printf("%d\n", msg.type);
    printf("请输入帐号昵称:\n");
    scanf("%s", msg.name);
    printf("请输入帐号密码:\n");
    scanf("%s", msg.password);
    send(socked, &msg, sizeof(msg), 0);
    printf("sendis ok\n");
    int flage = recv(socked, &msg, sizeof(msg), 0);
    if (flage < 0)
    {
        perror("recv is err");
        return;
    }
    else
    {
        printf("%s\n", msg.text);
    }
}

//连接帐号函数,判断是否连接成功
int my_login()
{
    msg.type = login;
    printf("请输入帐号昵称:\n");
    scanf("%s", msg.name);
    printf("请输入帐号密码:\n");
    scanf("%s", msg.password);
    send(socked, &msg, sizeof(msg), 0);
    printf("send id ok\n");
    int flage = recv(socked, &msg, sizeof(msg), 0);
    if (flage < 0)
    {
        perror("recv is err");
        return -1;
    }
    else
    {
        printf("%s\n",msg.text);
        if (strncmp(msg.text, "ok", 2) == 0)
        {
            printf("登录成功\n");
            return 1;
        }
        else
        {
            printf("登录失败\n");
            return 0;
        }
    }
}

//查询单词函数
void my_query_word()
{
    msg.type = word;
    printf("输入quit退出\n");
    getchar();
    while (1)
    {
        msg.type = word;
        fgets(msg.text, sizeof(msg.text), stdin);
        if (msg.text[strlen((msg.text)) - 1] == '\n')
            msg.text[strlen((msg.text)) - 1] = '\0';
        if (strcmp(msg.text, "quit") == 0)
            return;
        printf("要查询的单词:%s\n",msg.text);
        send(socked, &msg, sizeof(msg), 0);
        int flage = recv(socked, &msg, sizeof(msg), 0);
        if (flage < 0)
        {
            perror("recv is err");
            return;
        }
        else
        {
            printf("%s", msg.text);
            printf("\n");
        }
    }
}

//查询日志函数
void my_history_record()
{
    msg.type = history;
    printf("\n请再次帐号密码:");
    scanf("%s", msg.password);
    send(socked, &msg, sizeof(msg), 0);
    while (1)
    {
        int flage = recv(socked, &msg, sizeof(msg), 0);
        if (flage < 0)
        {
            perror("recv is err");
            return;
        }
        else
        {
            if (strcmp(msg.text, "quit") == 0)
            {
                break;
            }
            printf("%s", msg.text);
        }
    }
    printf("************************************\n");
}

//查询函数,查询单词或者日志
void my_send()
{
    int choose;
    while (1)
    {
        printf("************************************\n");
        printf("* 1: query_word 2: history_record 3: quit *\n");
        printf("************************************\n");
        printf("请输入你的选择:");
        scanf("%d", &choose);
        switch (choose)
        {
        case 1:
            my_query_word();
            break;
        case 2:
            my_history_record();
            break;
        case 3:
            return;
        }
    }
}

int main(int argc, char const *argv[])
{
    //1.创建套接字,IPv4
    socked = socket(AF_INET, SOCK_STREAM, 0);
    if (socked < 0)
    {
        perror("socket is err\n");
        return -1;
    }

    //2.connect服务器
    saddr.sin_family = AF_INET;
    saddr.sin_port = htons(atoi(argv[2]));
    saddr.sin_addr.s_addr = inet_addr(argv[1]);
    if ((connect(socked, (struct sockaddr *)&saddr, sizeof(saddr))) < 0)
    {
        perror("connect is err");
        return -1;
    }

    //3.选择注册,登录,退出
    int choose;
    while (1)
    {
        printf("************************************\n");
        printf("* 1: register 2: login 3: quit *\n");
        printf("************************************\n");
        printf("请输入你的选择:");
        scanf("%d", &choose);
        switch (choose)
        {
        case 1:
            my_register();
            break;
        case 2:
            if (my_login())
                my_send();
            break;
        case 3:
            close(socked);
            exit(0);
        }
    }
    close(socked);
    return 0;
}

server.c

#include "head.h"
#include <sqlite3.h>
#include <sys/select.h>
#include <time.h>
int sockfp, acceptfp, flage, sockfd, flages;
MSG_t msg, msg1;
sqlite3 *db = NULL;

//读日志函数,发送给客户端
void my_history()
{
    char **result;
    int hang, lie;
    char *errmasg;
    char buf[1024];
    sprintf(buf, "select * from %s_%s;", msg.name, msg.password);
    sqlite3_get_table(db, buf, &result, &hang, &lie, &errmasg);
    printf("开始发送日志\n");
    for (int i = 0; i <= hang; i++)
    {
        for (int j = 0; j < lie; j++)
        {

            sprintf(msg.text, "%s\t", result[i * lie + j]);
            send(sockfd, &msg, sizeof(msg), 0);
        }
        strcpy(msg.text, "\n");
        send(sockfd, &msg, sizeof(msg), 0);
    }
    strcpy(msg.text, "quit");
    send(sockfd, &msg, sizeof(msg), 0);
}

//写日志
void write_history()
{
    time_t current_time;
    struct tm *local_time;
    char time_string[100];
    char perate[100];
    char buf[1024];
    char *errmsg;
    // 获取当前时间戳
    current_time = time(NULL);

    // 将时间戳转换为本地时间
    local_time = localtime(&current_time);

    // 格式化本地时间字符串
    strftime(time_string, sizeof(time_string), "%Y-%m-%d %H:%M:%S", local_time);
    switch (msg1.type)
    {
    case my__register:
        strcpy(perate, "register");
        break;
    case login:
        strcpy(perate, "login");
        break;
    case word:
        strcpy(perate, "word");
        break;
    default:
        strcpy(perate, "quit");
        break;
    }
    sprintf(buf, "insert into %s_%s values(\"%s\",\"%s\");", msg1.name, msg1.password, time_string, perate);
    if ((sqlite3_exec(db, buf, NULL, NULL, &errmsg)) != SQLITE_OK)
    {
        fprintf(stderr, "sqlite3_exec is err %s", errmsg);
        return;
    }
}

int callback(void *data, int argc, char **argv, char **azColName)
{
    printf("调用callback函数\n");
    printf("%d\n", argc);
    char *knownTableName = (char *)data;
    for (int i = 0; i < argc; i++)
    {

        printf("%s\n", argv[i]);
        if (strcmp(argv[i], knownTableName) == 0)
        {
            strcpy(msg.text, "ok");
            send(sockfd, &msg, sizeof(msg), 0);
            printf("send is ok ok\n");
            printf("%s已登录成功\n", argv[i]);
            flages = 1;
            return 0;
        }
    }
    // strcpy(msg.text, "no");
    // send(sockfd, &msg, sizeof(msg), 0);
    // printf("send is ok\n");
    return 0;
}

//注册函数
void my_register()
{
    char buf[512];
    char *errmsg;

    sprintf(buf, "create table %s_%s(time char,perate char);", msg.name, msg.password);
    printf("%s\n", buf);
    if ((sqlite3_exec(db, buf, NULL, NULL, &errmsg)) != SQLITE_OK)
    {
        fprintf(stderr, "sqlite3_exec is err %s", errmsg);
        strcpy(msg.text, "注册失败\n");
        send(sockfd, &msg, sizeof(msg), 0);
        return;
    }
    else
        strcpy(msg.text, "注册成功\n");
    send(sockfd, &msg, sizeof(msg), 0);
    printf("send is ok\n");
}

//连接函数只有昵称和密码都匹配才能连接
void my_login()
{
    printf("调用连接函数\n");
    char buf[512];
    char *errmsg;
    char knownTableName[512];
    int rc;
    sprintf(knownTableName, "%s_%s", msg.name, msg.password);
    strcpy(buf, "SELECT name FROM sqlite_master WHERE type='table';");
    printf("%s\n", buf);
    rc = sqlite3_exec(db, buf, callback, knownTableName, &errmsg);
    if (rc != SQLITE_OK)
    {
        fprintf(stderr, "SQL error: %s\n", errmsg);
    }
    if (!flages)
    {
        strcpy(msg.text, "no");
        send(sockfd, &msg, sizeof(msg), 0);
    }
    //sleep(1);
}

//查询单词函数
void my_word()
{
    char buf[2048] = {0};
    int len = strlen(msg.text), flage = 0;
    FILE *fp = fopen("dict.txt", "r");

    if (fp == NULL)
    {
        perror("fopen is err");
        strcpy(msg.text, "fopen is err");
        send(sockfd, &msg, sizeof(msg), 0);
        return;
    }

    while (fgets(buf, sizeof(buf), fp))
    {

        if ((strncmp(msg.text, buf, len)) == 0)
        {
            flage = 1;
            break;
        }
    }
    int i;
    printf("len=%d\n", len);
    for (i = len; i < 2048; ++i)
    {
        if (buf[i] != ' ')
            break;
    }
    //printf("%s\n",buf);
    printf("%s\n", msg.text);
    if (flage)
    {
        strcpy(msg.text, buf + i);
    }
    else
    {
        strcpy(msg.text, "没有这个单词");
    }
    send(sockfd, &msg, sizeof(msg), 0);
}

int main(int argc, char const *argv[])
{
    //1.创建或打开数据库
    if ((sqlite3_open("./dict.db", &db)) < 0)
    {
        fprintf(stderr, "sqlite3_open id err %s\n", sqlite3_errmsg(db));
        return -1;
    }

    //2.创建socket套接字
    sockfp = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfp < 0)
    {
        perror("socket is err");
        return -1;
    }

    //3.绑定服务器ip和端口号
    struct sockaddr_in saddr, caddr;
    saddr.sin_family = AF_INET;
    saddr.sin_port = htons(atoi(argv[1]));
    saddr.sin_addr.s_addr = inet_addr("0.0.0.0");
    socklen_t len = sizeof(struct sockaddr_in);

    if (bind(sockfp, (struct sockaddr *)&saddr, sizeof(saddr)) < 0)
    {
        perror("bind is err");
        return -1;
    }

    //4.listen监听
    if (listen(sockfp, 90))
    {
        perror("liste err");
        return -1;
    }

    //4. select多路复用
    //4.1 创建表
    fd_set readfds, tempfds;
    //4.2 清空表
    FD_ZERO(&readfds);
    FD_ZERO(&tempfds);
    //4.3 将关心的文件描述符添加表
    FD_SET(0, &readfds);
    FD_SET(sockfp, &readfds);

    int maxfd = sockfp;
    while (1)
    {
        tempfds = readfds;
        //4.4 select检测   阻塞
        select(maxfd + 1, &tempfds, NULL, NULL, NULL);
        //4.5 进行相应的逻辑处理

        //sockfp,监听套接字响应证明,有客户端要链接
        if (FD_ISSET(sockfp, &tempfds))
        {
            acceptfp = accept(sockfp, (struct sockaddr *)&caddr, &len);
            if (acceptfp < 0)
            {
                perror("acceptfp");
                exit(0);
            }
            printf("连接到%d端口号\n", acceptfp);
            printf("port:%d   ip:  %s\n", ntohs(caddr.sin_port), inet_ntoa(caddr.sin_addr));
            FD_SET(acceptfp, &readfds);
            if (acceptfp > maxfd)
                maxfd = acceptfp;
        }

        //检测客户端,检查是哪一个客户端发送的消息
        for (int i = 5; i <= maxfd; ++i)
        {
            if (FD_ISSET(i, &tempfds))
            {
                sockfd = i;
                printf("端口号%d相应\n", i);
                flage = i;
                int recvbyte = recv(i, &msg, sizeof(msg), 0);
                if (recvbyte < 0)
                {
                    perror("recv err");
                    return -1;
                }
                else if (recvbyte == 0)
                {
                    msg.type = quit;
                    write_history();
                    close(i);
                    FD_CLR(i, &readfds);
                    if (i == maxfd)
                        --maxfd;
                }
                else
                {
                    printf("recvbyte is ok\n");
                    msg1 = msg;

                    printf("******************\n");
                    switch (msg.type)
                    {
                    case my__register:
                        my_register();
                        write_history();
                        break;
                    case login:
                        my_login();
                        write_history();
                        break;
                    case word:
                        my_word();
                        write_history();
                        break;
                    case history:
                        my_history();
                        write_history();
                        break;
                    }
                }
            }
        }
    }

    return 0;
}

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

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

相关文章

【Spring容器的启动过程】

Spring容器的启动过程 Spring 在初始化过程中有二个非常重要的步骤&#xff0c;容器的初始化与刷新。 初始化流程 如果想生成 bean 对象&#xff0c;那么就需要一个 beanFactory 工厂&#xff08;DefaultListableBeanFactory&#xff09;如果想让加了特定注解&#xff08;如 …

爬虫数据存储:技术、策略与实践(一)

文章目录 &#x1f34b;引言&#x1f34b;xlrd库和xlwt库&#x1f34b;创建Excel文件&#x1f34b;通过Python代码向Excel写入数据&#x1f34b;案例实战 &#x1f34b;引言 本节主要介绍一下在使用网络爬虫技术的时候&#xff0c;如何将数据存储到Excel中去 &#x1f34b;xl…

云原生之使用Docker部署Teedy轻量级文档管理系统

云原生之使用Docker部署Teedy轻量级文档管理系统 一、Teedy介绍1.1 Teedy简介1.2 Teedy特点 二、本地环境介绍2.1 本地环境规划2.2 本次实践介绍 三、本地环境检查3.1 检查Docker服务状态3.2 检查Docker版本3.3 检查docker compose 版本 四、下载Teedy镜像五、部署Teedy轻量级文…

git 本地工作区和仓库区基本使用

(1)git 本地有三个区 工作区和暂存区和 git管理的仓库. &#xff08;自行动手实践理解,然后就入门了&#xff09;(2)本地初次使用git做的事情,需要做如下工作 git config --global user.name "xx" git config --global user.email xxxqq.com git config --globa…

山西电力市场日前价格预测【2023-09-15】

日前价格预测 预测说明&#xff1a; 如上图所示&#xff0c;预测明日&#xff08;2023-09-15&#xff09;山西电力市场全天平均日前电价为338.85元/MWh。其中&#xff0c;最高日前电价为415.06元/MWh&#xff0c;预计出现在19: 15。最低日前电价为270.06元/MWh&#xff0c;预计…

OPC是通讯协议吗安全性

目录 1 安全防护 1.1 防火墙 1.2 网闸 2 OPC是通讯协议吗 2.1 什么通讯协议 2.2 那么OPC又是什么&#xff1f; OPC官方说明文档 1 安全防护 本文阐述了控制网络以OPC接口接入信息网络应当采用的安全防护。 1.1 防火墙 防火墙是大家熟知的网络安全产品,并被用作控制网…

laravel8框架-语言包的安装和配置

1, 查找 laravel框架语言包地址&#xff1a; \根目录\resources\lang\ 默认有个 en 语言包 2&#xff0c;下载 和 安装 下载地址&#xff1a;https://packagist.org/ 搜索 laravel/lang 参考网址&#xff1a;https://packagist.org/packages/overtrue/laravel-lang 选择你…

ARM Linux DIY(十一)板子名称、开机 logo、LCD 控制台、console 免登录、命令提示符、文件系统大小

文章目录 前言板子名称uboot Modelkernel 欢迎词、主机名 开机 logoLCD 控制台console 免登录命令提示符文件系统大小 前言 经过前面十篇文章的介绍&#xff0c;硬件部分调试基本完毕&#xff0c;接下来的文章开始介绍软件的个性化开发。 板子名称 uboot Model 既然是自己的…

Games101作业0(vscode连接VB虚拟机)

作业0 配置环境: 基本配置在pa0中都有,下面介绍使用vscode连接VB虚拟机快捷开发 vscode连接VB虚拟机 1.用户名 可以看到Username为cs18并非为css180 2.密钥 在window主机生成密钥 ssh-keygen #一路回车3.VB虚拟机配置ssh sshd_config配置 cd /etc/ssh/ vim sshd_confi…

丰田工厂停产竟然因为磁盘...

丰田因磁盘空间不足关闭14家工厂 在如今的信息时代&#xff0c;无论是生活还是工作&#xff0c;我们都离不开计算机和网络。然而&#xff0c;令人惊讶的是&#xff0c;一家全球知名的汽车制造商——丰田&#xff0c;却因为磁盘空间不足的问题&#xff0c;被迫关闭了14家工厂。…

DP专题2 爬楼梯|

题目&#xff1a; 思路&#xff1a; 根据题意&#xff0c;我们先找到对应的 n 阶台阶的方案数是多少。 n 1 f(n) 1 n 2 f(n) 2 n 3 f(n) 3 n 4 f(n) 5 n 5 f(n) 8 ...... ...... n n f(n) f(n - 2) f(n - 1…

读高性能MySQL(第4版)笔记09_创建高性能索引(下)

1. 覆盖索引 1.1. 设计优秀的索引应该考虑到整个查询&#xff0c;而不单是WHERE条件部分 1.2. 如果一个索引包含&#xff08;或者说覆盖&#xff09;所有需要查询的字段的值&#xff0c;我们就称之为覆盖索引 1.3. 只有B-tree索引可以用于覆盖索引 1.4. 如果查询只需要扫描…

用Python实现链式调用

嗨喽&#xff0c;大家好呀~这里是爱看美女的茜茜呐 我们在使用Django的models查询数据库时&#xff0c;可以看到有这种写法&#xff1a; form app.models import XXX query XXX.objects.all() query query.filter(name123, age456).filter(salary999)在这种写法里面&#xf…

matlab根轨迹绘制

绘制根轨迹目的就是改变系统的闭环极点&#xff0c;使得系统由不稳定变为稳定或者使得稳定的系统变得更加稳定。 在使用PID控制器的时候&#xff0c;首先要确定的参数是Kp&#xff0c;画成框图的形式如下&#xff1a; 也就是想要知道Kp对系统性能有哪些影响&#xff0c;此时就…

go 包的引入

本文介绍下下go包的管理&#xff0c;以linux平台为例。 先看下目录结构&#xff1a; test目录下的test.go test2目录下的test.go 主函数的调用 此时执行会报错&#xff0c;需要用mod进行包的管理,执行下面命令 go mod init godir 生成go.mod文件 执行结果&#xff1a;

【MySQL】mysql中有哪几种类型的备份技术?它们各自有什么优缺点?

为什么要备份&#xff1f;备份类型&#xff08;从类型的角度&#xff09;备份技术&#xff08;从技术手段的角度&#xff09;不同备份方法的比较感谢 &#x1f496; 为什么要备份&#xff1f; 数据库或它所在的平台可能会出现问题&#xff0c;这时候数据库中的数据可能就遭到了…

春秋云镜 CVE-2014-4577

春秋云镜 CVE-2014-4577 wordpress插件 wp-amasin-the-amazon-affiliate-shop < 0.97 LFI 靶标介绍 wordpress插件 wp-amasin-the-amazon-affiliate-shop < 0.97 存在路径穿越漏洞&#xff0c;使得可以读取任意文件。 启动场景 漏洞利用 exp http://url/wp-content/…

【Transformer系列】深入浅出理解Embedding(词嵌入)

一、参考资料 一文读懂Embedding的概念&#xff0c;以及它和深度学习的关系 论文 [1] Attention is All you Need 二、Embedding相关介绍 Embedding&#xff0c;直译是词嵌入、嵌入层。 1. 引言 2. one-hot编码 假设&#xff0c;中文有10个字 “星 巴 克 喜 欢 瑞 幸 的…

LinuxFTP云盘-文件服务系统

目录 1.项目介绍 2.项目运行展示 3.实现思路 服务端&#xff1a; 客户端&#xff1a; 4.相关调用函数 socket()&#xff1a;创建一个网络通信端点原型&#xff1a;int socket(int domain, int type, int protocol); atoi()&#xff1a;将字符串转变成整型数据原型&…

【C刷题】day2

一、选择题 1、以下程序段的输出结果是&#xff08; &#xff09; #include<stdio.h> int main() { char s[] "\\123456\123456\t"; printf("%d\n", strlen(s)); return 0; } A: 12 B: 13 C: 16 D: 以上都不对【答案】&#xff1a; A 【解析】…