您的位置:首页 > 教育 > 培训 > 软件开发服务合同_定制网站建设公司哪家便宜_客源引流推广_抖音怎么推广引流

软件开发服务合同_定制网站建设公司哪家便宜_客源引流推广_抖音怎么推广引流

2025/3/28 22:34:00 来源:https://blog.csdn.net/weixin_45136594/article/details/146474998  浏览:    关键词:软件开发服务合同_定制网站建设公司哪家便宜_客源引流推广_抖音怎么推广引流
软件开发服务合同_定制网站建设公司哪家便宜_客源引流推广_抖音怎么推广引流

1. 整体功能概述

        实现了一个简单的用户注册和登录系统,采用客户端 - 服务器(C/S)架构。

        客户端可以选择注册或登录操作,将用户名和密码发送给服务器,服务器接收请求后处理并返回相应的结果给客户端。

        服务器使用 SQLite 数据库来存储用户信息。

2. 客户端功能实现分析

  • 功能:客户端程序,允许用户选择注册或登录操作,输入用户名和密码,将请求发送给服务器,并接收服务器的响应结果。
  • 输入:用户选择(1 表示注册,2 表示登录)、用户名、密码。
  • 输出:服务器返回的处理结果(如注册成功、用户名已存在、登录成功等)。

1.1. 详细步骤

1.1.1. 包含必要的头文件

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>

        这些头文件提供了标准输入输出、内存管理、字符串处理、套接字编程和 IP 地址转换等功能。

1.1.2. 定义协议包结构体

#define BUF_SIZE 1024typedef struct {uint8_t op;         // 操作码,1表示注册,2表示登录uint16_t data_len;  // 数据长度(网络字节序)char data[BUF_SIZE];// 存储用户名、密码等数据
} ProtocolPack;

        该结构体用于封装客户端和服务器之间传输的协议包,包含操作码、数据长度和数据内容。

1.1.3. 创建套接字并连接服务器

int sock = socket(AF_INET, SOCK_STREAM, 0);
struct sockaddr_in addr = {.sin_family = AF_INET,.sin_port = htons(8888)
};
inet_pton(AF_INET, "127.0.0.1", &addr.sin_addr);
connect(sock, (struct sockaddr*)&addr, sizeof(addr));

        使用 socket 函数创建一个 TCP 套接字,设置服务器地址和端口,然后使用 connect 函数连接到服务器。

1.1.4. 获取用户输入

int choice;
printf("1. 注册\n2. 登录\n选择: ");
scanf("%d", &choice);char username[32], password[32];
printf("用户名: ");
scanf("%31s", username);
printf("密码: ");
scanf("%31s", password);

        提示用户选择操作类型(注册或登录),并输入用户名和密码。

1.1.5. 构建协议包并发送

ProtocolPack pack = {0};
pack.op = (uint8_t)choice;
sprintf(pack.data, "%s %s", username, password);
pack.data_len = htons(strlen(pack.data));
send(sock, &pack, sizeof(ProtocolPack), 0);

        将用户选择的操作码、用户名和密码封装到协议包中,并使用 send 函数发送给服务器。

1.1.6. 接收服务器响应并输出结果

ProtocolPack resp_pack;
recv(sock, &resp_pack, sizeof(ProtocolPack), 0);
printf("结果: %s\n", resp_pack.data);

        使用 recv 函数接收服务器的响应协议包,并输出结果。

1.1.7. 关闭套接字

close(sock);

3.服务器功能实现分析

  • 功能:服务器程序,监听客户端的连接请求,接收客户端发送的注册或登录请求,处理请求并将结果返回给客户端。使用 SQLite 数据库存储用户信息。
  • 输入:客户端发送的协议包,包含操作码、用户名和密码。
  • 输出:处理结果(如注册成功、用户名已存在、登录成功等),封装在协议包中返回给客户端。

3.1. 详细步骤

3.1.1. 包含必要的头文件

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/epoll.h>
#include <sqlite3.h>

        这些头文件提供了标准输入输出、内存管理、字符串处理、套接字编程、事件驱动 I/O 和 SQLite 数据库操作等功能。

3.1.2. 定义常量和协议包结构体

#define MAX_EVENTS 100
#define BUF_SIZE 1024typedef struct {uint8_t op;         // 操作码,1表示注册,2表示登录uint16_t data_len;  // 数据长度(网络字节序)char data[BUF_SIZE];// 存储用户名、密码等数据
} ProtocolPack;

    MAX_EVENTS 定义了 epoll_wait 函数返回的最大事件数量,BUF_SIZE 定义了协议包数据部分的最大缓冲区大小。

3.1.3. 初始化数据库

sqlite3* init_db() {sqlite3* db;int rc = sqlite3_open("user.db", &db);if (rc) {fprintf(stderr, "Can't open database: %s\n", sqlite3_errmsg(db));exit(1);}const char* sql = "CREATE TABLE IF NOT EXISTS users (""id INTEGER PRIMARY KEY AUTOINCREMENT,""username TEXT UNIQUE NOT NULL,""password TEXT NOT NULL)";char* errmsg;rc = sqlite3_exec(db, sql, 0, 0, &errmsg);if (rc != SQLITE_OK) {fprintf(stderr, "SQL error: %s\n", errmsg);sqlite3_free(errmsg);}return db;
}

        打开或创建一个名为 user.db 的 SQLite 数据库,并创建一个名为 users 的表,用于存储用户信息。

3.1.4. 处理用户注册逻辑

int handle_register(sqlite3* db, const char* username, const char* password) {const char* sql = "INSERT INTO users (username, password) VALUES (?, ?)";sqlite3_stmt* stmt;int rc = sqlite3_prepare_v2(db, sql, -1, &stmt, 0);if (rc != SQLITE_OK) return -1;sqlite3_bind_text(stmt, 1, username, -1, SQLITE_STATIC);sqlite3_bind_text(stmt, 2, password, -1, SQLITE_STATIC);rc = sqlite3_step(stmt);if (rc == SQLITE_DONE) {sqlite3_finalize(stmt);return 0; // 注册成功}if (rc == SQLITE_CONSTRAINT_UNIQUE) {sqlite3_finalize(stmt);return 1; // 用户名已存在}sqlite3_finalize(stmt);return -1;
}

        将用户的用户名和密码插入到 users 表中,处理可能的错误情况,如 SQL 执行失败或用户名已存在。

3.1.5. 处理用户登录逻辑

int handle_login(sqlite3* db, const char* username, const char* password) {const char* sql = "SELECT COUNT(*) FROM users WHERE username=? AND password=?";sqlite3_stmt* stmt;int rc = sqlite3_prepare_v2(db, sql, -1, &stmt, 0);if (rc != SQLITE_OK) return -1;sqlite3_bind_text(stmt, 1, username, -1, SQLITE_STATIC);sqlite3_bind_text(stmt, 2, password, -1, SQLITE_STATIC);rc = sqlite3_step(stmt);if (rc == SQLITE_ROW) {int count = sqlite3_column_int(stmt, 0);sqlite3_finalize(stmt);return count == 1 ? 0 : -1; // 登录成功返回0,否则失败}sqlite3_finalize(stmt);return -1;
}

        检查用户的用户名和密码是否匹配数据库中的记录,返回登录结果。

3.1.6. 创建监听套接字并绑定地址

int listen_fd = socket(AF_INET, SOCK_STREAM, 0);
struct sockaddr_in addr = {.sin_family = AF_INET,.sin_port = htons(8888),.sin_addr.s_addr = INADDR_ANY
};
bind(listen_fd, (struct sockaddr*)&addr, sizeof(addr));
listen(listen_fd, 5);

        使用 socket 函数创建一个 TCP 监听套接字,绑定到指定的地址和端口,然后开始监听客户端连接。

3.1.7. 创建 epoll 实例并监听事件

int epoll_fd = epoll_create1(0);
struct epoll_event ev, events[MAX_EVENTS];
ev.events = EPOLLIN;
ev.data.fd = listen_fd;
epoll_ctl(epoll_fd, EPOLL_CTL_ADD, listen_fd, &ev);

        使用 epoll_create1 函数创建一个 epoll 实例,将监听套接字加入到 epoll 监控列表中,监听读事件。

3.1.8. 事件循环处理

while (1) {int nfds = epoll_wait(epoll_fd, events, MAX_EVENTS, -1);for (int i = 0; i < nfds; i++) {if (events[i].data.fd == listen_fd) { // 新客户端连接int client_fd = accept(listen_fd, NULL, NULL);ev.events = EPOLLIN;ev.data.fd = client_fd;epoll_ctl(epoll_fd, EPOLL_CTL_ADD, client_fd, &ev);} else { // 处理客户端数据int client_fd = events[i].data.fd;ProtocolPack pack;if (recv(client_fd, &pack, sizeof(ProtocolPack), 0) <= 0) {epoll_ctl(epoll_fd, EPOLL_CTL_DEL, client_fd, NULL);close(client_fd);continue;}char username[32] = {0}, password[32] = {0};sscanf(pack.data, "%31s %31s", username, password);ProtocolPack resp_pack = {0};if (pack.op == 1) { // 处理注册请求int result = handle_register(db, username, password);if (result == 0) strcpy(resp_pack.data, "REGISTER_SUCCESS");else if (result == 1) strcpy(resp_pack.data, "USER_EXISTS");else strcpy(resp_pack.data, "REGISTER_FAIL");} else if (pack.op == 2) { // 处理登录请求int result = handle_login(db, username, password);if (result == 0) strcpy(resp_pack.data, "LOGIN_SUCCESS");else strcpy(resp_pack.data, "LOGIN_FAIL");}resp_pack.op = pack.op;resp_pack.data_len = htons(strlen(resp_pack.data));send(client_fd, &resp_pack, sizeof(ProtocolPack), 0);}}
}

        使用 epoll_wait 函数等待事件发生,处理新客户端连接和客户端数据。根据客户端发送的操作码调用相应的处理函数,并将结果封装在协议包中返回给客户端。

3.1.9. 关闭监听套接字和数据库连接

close(listen_fd);
sqlite3_close(db);

3.2.编译运行步骤

  1. 编译服务器
    gcc server.c -o server -lsqlite3
    
  2. 编译客户端
    gcc client.c -o client
    
  3. 运行服务器
    ./server
    
  4. 新开终端运行客户端
    ./client

3.3. 功能实现结果展示

3.4. 源码

3.4.1. 服务器端代码(server.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/epoll.h>
#include <sqlite3.h>#define MAX_EVENTS 100
#define BUF_SIZE 1024// 定义通信协议包结构体
typedef struct {uint8_t op;         // 操作码,1表示注册,2表示登录uint16_t data_len;  // 数据长度(网络字节序)char data[BUF_SIZE];// 存储用户名、密码等数据
} ProtocolPack;// 初始化数据库
sqlite3* init_db() {sqlite3* db;// 打开/创建数据库文件 "user.db"int rc = sqlite3_open("user.db", &db);if (rc) {fprintf(stderr, "Can't open database: %s\n", sqlite3_errmsg(db));exit(1);}// 创建用户表(若不存在)const char* sql = "CREATE TABLE IF NOT EXISTS users (""id INTEGER PRIMARY KEY AUTOINCREMENT,""username TEXT UNIQUE NOT NULL,""password TEXT NOT NULL)";char* errmsg;// 执行建表SQLrc = sqlite3_exec(db, sql, 0, 0, &errmsg);if (rc != SQLITE_OK) {fprintf(stderr, "SQL error: %s\n", errmsg);sqlite3_free(errmsg);}return db;
}// 处理注册逻辑
int handle_register(sqlite3* db, const char* username, const char* password) {const char* sql = "INSERT INTO users (username, password) VALUES (?, ?)";sqlite3_stmt* stmt;// 准备SQL语句int rc = sqlite3_prepare_v2(db, sql, -1, &stmt, 0);if (rc != SQLITE_OK) return -1;// 绑定用户名和密码参数sqlite3_bind_text(stmt, 1, username, -1, SQLITE_STATIC);sqlite3_bind_text(stmt, 2, password, -1, SQLITE_STATIC);// 执行插入操作rc = sqlite3_step(stmt);if (rc == SQLITE_DONE) {sqlite3_finalize(stmt);return 0; // 注册成功}if (rc == SQLITE_CONSTRAINT_UNIQUE) {sqlite3_finalize(stmt);return 1; // 用户名已存在}sqlite3_finalize(stmt);return -1;
}// 处理登录逻辑
int handle_login(sqlite3* db, const char* username, const char* password) {const char* sql = "SELECT COUNT(*) FROM users WHERE username=? AND password=?";sqlite3_stmt* stmt;// 准备SQL语句int rc = sqlite3_prepare_v2(db, sql, -1, &stmt, 0);if (rc != SQLITE_OK) return -1;// 绑定用户名和密码参数sqlite3_bind_text(stmt, 1, username, -1, SQLITE_STATIC);sqlite3_bind_text(stmt, 2, password, -1, SQLITE_STATIC);// 执行查询操作rc = sqlite3_step(stmt);if (rc == SQLITE_ROW) {int count = sqlite3_column_int(stmt, 0);sqlite3_finalize(stmt);return count == 1 ? 0 : -1; // 登录成功返回0,否则失败}sqlite3_finalize(stmt);return -1;
}int main() {sqlite3* db = init_db(); // 初始化数据库// 创建监听套接字int listen_fd = socket(AF_INET, SOCK_STREAM, 0);struct sockaddr_in addr = {.sin_family = AF_INET,.sin_port = htons(8888),.sin_addr.s_addr = INADDR_ANY};// 绑定地址bind(listen_fd, (struct sockaddr*)&addr, sizeof(addr));// 开始监听listen(listen_fd, 5);// 创建epoll实例int epoll_fd = epoll_create1(0);struct epoll_event ev, events[MAX_EVENTS];ev.events = EPOLLIN;ev.data.fd = listen_fd;// 将监听套接字加入epoll监控epoll_ctl(epoll_fd, EPOLL_CTL_ADD, listen_fd, &ev);while (1) {// 等待事件发生int nfds = epoll_wait(epoll_fd, events, MAX_EVENTS, -1);for (int i = 0; i < nfds; i++) {if (events[i].data.fd == listen_fd) { // 新客户端连接int client_fd = accept(listen_fd, NULL, NULL);ev.events = EPOLLIN;ev.data.fd = client_fd;// 将客户端套接字加入epoll监控epoll_ctl(epoll_fd, EPOLL_CTL_ADD, client_fd, &ev);} else { // 处理客户端数据int client_fd = events[i].data.fd;ProtocolPack pack;// 接收协议包if (recv(client_fd, &pack, sizeof(ProtocolPack), 0) <= 0) {epoll_ctl(epoll_fd, EPOLL_CTL_DEL, client_fd, NULL);close(client_fd);continue;}char username[32] = {0}, password[32] = {0};// 解析数据内容sscanf(pack.data, "%31s %31s", username, password);ProtocolPack resp_pack = {0};if (pack.op == 1) { // 处理注册请求int result = handle_register(db, username, password);if (result == 0) strcpy(resp_pack.data, "REGISTER_SUCCESS");else if (result == 1) strcpy(resp_pack.data, "USER_EXISTS");else strcpy(resp_pack.data, "REGISTER_FAIL");} else if (pack.op == 2) { // 处理登录请求int result = handle_login(db, username, password);if (result == 0) strcpy(resp_pack.data, "LOGIN_SUCCESS");else strcpy(resp_pack.data, "LOGIN_FAIL");}resp_pack.op = pack.op;resp_pack.data_len = htons(strlen(resp_pack.data));// 发送响应协议包send(client_fd, &resp_pack, sizeof(ProtocolPack), 0);}}}close(listen_fd);sqlite3_close(db);return 0;
}

3.4.2. 客户端代码(client.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>#define BUF_SIZE 1024// 定义通信协议包结构体
typedef struct {uint8_t op;         // 操作码,1表示注册,2表示登录uint16_t data_len;  // 数据长度(网络字节序)char data[BUF_SIZE];// 存储用户名、密码等数据
} ProtocolPack;int main() {// 创建套接字int sock = socket(AF_INET, SOCK_STREAM, 0);struct sockaddr_in addr = {.sin_family = AF_INET,.sin_port = htons(8888)};// 设置服务器IPinet_pton(AF_INET, "127.0.0.1", &addr.sin_addr);// 连接服务器connect(sock, (struct sockaddr*)&addr, sizeof(addr));ProtocolPack pack = {0};int choice;printf("1. 注册\n2. 登录\n选择: ");scanf("%d", &choice);char username[32], password[32];printf("用户名: ");scanf("%31s", username);printf("密码: ");scanf("%31s", password);pack.op = (uint8_t)choice;// 构建数据内容sprintf(pack.data, "%s %s", username, password);pack.data_len = htons(strlen(pack.data));// 发送协议包send(sock, &pack, sizeof(ProtocolPack), 0);ProtocolPack resp_pack;// 接收响应协议包recv(sock, &resp_pack, sizeof(ProtocolPack), 0);printf("结果: %s\n", resp_pack.data);close(sock);return 0;
}

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com