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

网络编程(4)

【0】复习

sockfd=socket();
//指定网络信息
bind();
listen();
//创建表 fd_set rfds,tempfds;
FD_ZERO();
FD_SET(sockfd);
max =sockfd
while(1)
{
    tempfds=rfds;
    select(max+1,&tempfds)
    if(FD_ISSET(scokfd,&tempfds))
    {    acceptfd=accept();
        FD_SET(acceptfd,&rfds);
        if(max<acceptfd)max=acceptfd;
    }
    for(int i=sockfd+1;i<=max;i++)
    {    if(FD_ISSET(i,&tempfds))
        {    ret=recv(i);
            if(ret==0)
            {close(i);    FD_CLR(i,&rfds);
                while(!FD_ISSET(max,&rfds))max--;        
            }
            printf();
        }
    }
}

【1】组播(多播)

理论

单播方式只能发给一个接收方。

广播方式发给所有的主机。过多的广播会大量占用网络带宽,造成广播风暴,影响正常的通信。

播是一个人发送,加入到多播组的人接收数据。

多播方式既可以发给多个主机,又能避免像广播那样带来过多的负载(每台主机要到传输层才能判断广播包是否要处理)

多播地址

D类:224.0.0.1-239.255.255.254

接收者

  1. 创建套接字(socket)
  2. 置多播属性,将自己的IP加入到多播组中

  1. 指定网络信息
  2. 绑定套接字(bind)
  3. 接收消息(recvfrom)
  4. 关闭套接字(close)
#include <stdio.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>int main(int argc, char const *argv[])
{char buf[128] = {0};int ret = 0;int acceptfd;// 1.创建数据包套接字int sockfd = socket(AF_INET, SOCK_DGRAM, 0);if (sockfd < 0){perror("socket err");return -1;}printf("sockfd:%d\n", sockfd);// 设置多播属性struct ip_mreq mreq;
    mreq.imr_multiaddr.s_addr = inet_addr(argv[2]); // 组播IP
    mreq.imr_interface.s_addr = INADDR_ANY;         // 自己IPsetsockopt(sockfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq,sizeof(mreq));// 指定网络信息struct sockaddr_in saddr, caddr;
    saddr.sin_family = AF_INET;
    saddr.sin_port = htons(atoi(argv[1])); // 端口// saddr.sin_addr.s_addr = inet_addr("192.168.50.79"); // IP//  saddr.sin_addr.s_addr = inet_addr("0.0.0.0"); // IP
    saddr.sin_addr.s_addr = INADDR_ANY; // IPint len = sizeof(caddr);// 绑定:绑定服务器信息(IP地址\端口号等)if (bind(sockfd, (struct sockaddr *)&saddr, sizeof(saddr)) < 0){perror("bind err");return -1;}printf("bind okk\n");while (1){// read(acceptfd,buf,sizeof(buf));
        ret = recvfrom(sockfd, buf, sizeof(buf), 0, (struct sockaddr *)&caddr, &len);if (ret < 0){perror("recv err");return -1;}elseprintf("ip:%s port:%d says:%s\n", inet_ntoa(caddr.sin_addr), ntohs(caddr.sin_port), buf);memset(buf, 0, sizeof(buf));}close(sockfd);return 0;
}

发送者

  1. 创建套接字(socket)
  2. 指定网络(服务器)信息
  3. 发送消息(sendto)
  4. 关闭套接字(close)

【2】数据库:SQLite

SQLITE的存储结构:B树

1. 数据库安装

测试数据库是否安装

sqlite3 -version

2. 数据库安装命令

将压缩包拿到虚拟机路径下

tar xf sqlite-autoconf-3460000.tar.gz

cd sqlite-autoconf-3460000

./configure

make

sudo make install

3.  图形化工具的安装

sudo apt-get install sqlitebrowser

2. 数据库的概念

数据库是“按照数据结构来组织、存储和管理数据的仓库”。是一个长期存储在计算机内的、有组织的、可共享的、统一管理的大量数据的集合。

数据库是存放数据的仓库。它的存储空间很大,可以存放百万条、千万条、上亿条数据。但是数据库并不是随意地将数据进行存放,是有一定的规则的,否则查询的效率会很低。当今世界是一个充满着数据的互联网世界,充斥着大量的数据。即这个互联网世界就是数据世界。数据的来源有很多,比如出行记录、消费记录、浏览的网页、发送的消息等等。

3. 常用的数据库

大型数据库 :Oracle

中型数据库 :Server是微软开发的数据库产品,主要支持windows平台

小型数据库 : MySQL是一个小型关系型数据库管理系统。开放源码 (嵌入式不需要存储太多数据)

MySQLSQLite区别:

MySQL和SQLite是两种不同的数据库管理系统,它们在多个方面有所不同。

1. 性能和规模:MySQL通常用于大型应用程序和网站,它可以处理大量数据和高并发访问。SQLite则更适合于小型应用程序或移动设备,因为它是一个轻量级的数据库引擎,不需要独立的服务器进程,可以直接访问本地文件。

2. 部署和配置:MySQL需要单独的服务器进程来运行,需要配置和管理数据库服务器。而SQLite是一个嵌入式数据库,可以直接嵌入到应用程序中,不需要单独的服务器进程。

3. 功能和特性:MySQL提供了更多的功能和高级特性,比如存储过程、触发器、复制和集群支持等。SQLite则是一个轻量级的数据库引擎,功能相对较少,但对于简单的数据存储和检索已经足够。

4. 跨平台支持:SQLite在各种操作系统上都能够运行,而MySQL需要在特定的操作系统上安装和配置数据库服务器。

总之,MySQL适用于大型应用程序和网站,需要处理大量数据和高并发访问,而SQLite适用于小型应用程序或移动设备,对性能和规模要求没有那么高。

4. SQLite基础

SQLite的源代码是C,其源代码完全开放。它是一个轻量级的嵌入式数据库。

SQLite有以下特性:

零配置一无需安装和管理配置;

储存在单一磁盘文件中的一个完整的数据库;

数据库文件可以在不同字节顺序的机器间自由共享;

支持数据库大小至2TB(1024G = 1TB);//嵌入式足够

足够小,全部源码大致3万行c代码,250KB;

比目前流行的大多数数据库对数据的操作要快;

操作方式:

手动:

使用sqlite3工具,手工输入命令

命令行输入

代码:

利用代码编程,调用接口

5. 基本语句的基本使用

【腾讯文档】SQL基础语句基本使用

SQL基础语句基本使用

sqlile3编程

官方文档:List Of SQLite Functions

中本版:SQLite Insert 语句 - SQLite 中文版 - UDN开源文档

头文件:#include <sqlite3.h>

编译:gcc sqlite.c -lsqlite3

6. 函数接口

打开数据库
 

int sqlite3_open(char *path, sqlite3 **db);

功能:打开sqlite数据库,如果数据库不存在则创建它

参数:path: 数据库文件路径

db: 指向sqlite句柄的指针

返回值:成功返回SQLITE_OK(0),失败返回错误码(非零值)

返回错误信息

char *sqlite3_errmsg(sqlite3 *db);

功能: 获取错误信息

返回值:返回错误信息

使用: fprintf(stderr,"sqlite3_open failed %s\n",sqlite3_errmsg(db));

关闭数据库

int sqlite3_close(sqlite3 *db);

功能:关闭sqlite数据库

返回值:成功返回SQLITE_OK,失败返回错误码

执行sql语句接口

int sqlite3_exec(

sqlite3 *db, /* An open database */

const char *sql, /* SQL to be evaluated */

int (*callback)(void*,int,char**,char**), /* Callback function */

void *arg, /* 1st argument to callback */

char **errmsg /* Error msg written here */

);

功能:执行SQL操作

参数:db:数据库句柄

sql:要执行SQL语句

callback:回调函数(满足一次条件,调用一次函数,用于查询)

再调用查询sql语句的时候使用回调函数打印查询到的数据

arg:传递给回调函数的参数

errmsg:错误信息指针的地址

返回值:成功返回SQLITE_OK,失败返回错误码

回调函数:

typedef int (*sqlite3_callback)(void *para, int f_num,

char **f_value, char **f_name);

功能:select:每找到一条记录自动执行一次回调函数

参数:para:传递给回调函数的参数(由 sqlite3_exec() 的第四个参数传递而来)

f_num:记录中包含的字段数目

f_value:包含每个字段值的指针数组(列值)

f_name:包含每个字段名称的指针数组(列名)

返回值:成功返回SQLITE_OK,失败返回-1,每次回调必须返回0后才能继续下次回调

不使用回调函数执行SQL语句(只用于查询)
int sqlite3_get_table(sqlite3 *db, const  char  *sql, char ***resultp,  int *nrow,  int *ncolumn, char **errmsg);功能:执行SQL操作
参数:db:数据库句柄
    sql:SQL语句
    resultp:用来指向sql执行结果的指针
    nrow:满足条件的记录的数目(但是不包含字段名(表头 id name score))
    ncolumn:每条记录包含的字段数目
    errmsg:错误信息指针的地址
返回值:成功返回SQLITE_OK,失败返回错误码

#include <sqlite3.h>
#include <stdio.h>int callback(void *buf, int num, char **value, char **name);int main(int argc, char const *argv[])
{sqlite3 *db;// 打开或创建数据库if (sqlite3_open("stu.db", &db) != SQLITE_OK){fprintf(stderr, "open err:%s\n", sqlite3_errmsg(db));return -1;}printf("open okk\n");// 创建表char *errmsg = NULL;if (sqlite3_exec(db, "create table if not exists stu (id int ,name string,score float)", NULL, NULL, &errmsg) != SQLITE_OK){fprintf(stderr, "create err:%s\n", sqlite3_errmsg(db));fprintf(stderr, "create err:%s\n", errmsg);return -1;}printf("create okk\n");// 插入数据/*if(sqlite3_exec(db,"insert into stu values(1,'lihua',0)",NULL,NULL,&errmsg)!=SQLITE_OK){fprintf(stderr, "insert err:%s\n", errmsg);return -1;}printf("insert okk\n");*/int num = 0;printf("请输入要输入的学生人数:");scanf("%d", &num);char sql[64] = {0};int id;char name[32] = {0};float score;for (int i = 0; i < num; i++){printf("请输入学生信息:");scanf("%d %s %f", &id, name, &score);sprintf(sql, "insert into stu values(%d,'%s',%f)", id, name, score);if (sqlite3_exec(db, sql, NULL, NULL, &errmsg) != SQLITE_OK){fprintf(stderr, "insert err:%s\n", errmsg);return -1;}printf("insert okk\n");}// 查找数据if (sqlite3_exec(db, "select * from stu where id=10", callback, "hello", &errmsg) != SQLITE_OK){fprintf(stderr, "select err:%s\n", errmsg);return -1;}printf("select okk\n");// 专门用于查询的函数// char **result = NULL;// int row, column;// sqlite3_get_table(db, "select * from stu where id=10", &result, &row, &column, &errmsg);// printf("row:%d column:%d\n", row, column);// int k=0;// printf("aaaa:%s ", result[k++]);// for (int i = 0; i <= row; i++)// {//     for (int j = 0; j < column; j++)//         printf("%s ", result[k++]);//     putchar(10);// }// 关闭数据库sqlite3_close(db);return 0;
}// 每查询到一条符合条件的数据,就调用一次此函数
int callback(void *buf, int num, char **value, char **name)
{static int a = 1;printf("******************** \n");printf("%d :%s \n", a++, (char *)buf);// num:列数// name:列名// value:查询到的值for (int i = 0; i < num; i++)printf("%s ", name[i]);putchar(10);for (int i = 0; i < num; i++)printf("%s ", value[i]);putchar(10);return 0; // 必须返回0,不然报错
}

练习:数据库基本语句的使用,并完成下面练习题

sqlite3 stu2.db
CREATE TABLE stu2 (
    学号 VARCHAR(10) PRIMARY KEY,
    姓名 VARCHAR(50),
    年龄 INT,
    性别 VARCHAR(10),
    家庭住址 VARCHAR(100),
    联系电话 VARCHAR(20)
);
ALTER TABLE stu2 ADD 学历 VARCHAR(50);
ALTER TABLE stu2 DROP COLUMN 家庭住址;
INSERT INTO stu2 (学号, 姓名, 年龄, 性别, 联系电话, 学历)
VALUES ('1', 'A', 22, '男', '123456', '小学'), 
('2', 'B', 20, '女', '114567', '初中'),
('3', 'C', 25, '男', '345678', '高中')
('4', 'D', 22, '男', '345678', '大专');UPDATE stu2
SET 学历 = '大专'
WHERE 联系电话 LIKE '11%';DELETE FROM stu2
WHERE 姓名 LIKE 'C%' AND 性别 = '男';SELECT 姓名, 学号
FROM stu2
WHERE 年龄 < 22 AND 学历 = '大专';SELECT 姓名, 性别, 年龄
FROM stu2
ORDER BY 年龄 DESC;

  1. 完成FTP项目

模拟FTP核心原理:客户端连接服务器后,向服务器发送一个文件。文件名可以通过参数指定,服务器端接收客户端传来的文件(文件名随意),如果文件不存在自动创建文件,如果文件存在,那么清空文件然后写入。

项目功能介绍:

均有服务器和客户端代码,基于TCP写的。

在同一路径下,将客户端可执行代码复制到其他的路径下,接下来在不同的路径下运行服务器和客户端。

相当于另外一台电脑在访问服务器。

客户端和服务器链接成功后出现以下提示:三个功能

***********put filename**********//从客户端所在路径上传文件

***********get filename**********//从服务器所在路径下载文件

**************quit***************//退出(可只退出客户端,服务器等待下一个客户端链接)

可拓展功能:客户端可以获取服务器路径下有哪些文件

list:客户端请求文件列表 → 服务器遍历目录 → 发送文件名 → 发送 "end" 结束。

putfile:客户端发送 "put filename" → 服务器创建文件 → 接收数据写入文件 → 收到 "end" 结束。

getfile:客户端发送 "get filename" → 服务器读取文件 → 发送数据 → 发送 "end" 结束。

服务器

#include <stdio.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <string.h>
#include <stdlib.h>
#include <stdlib.h>
#include <dirent.h>
#include <fcntl.h>void putfile(int, char *);
void getfile(int acceptfd, char *p);int main(int argc, char const *argv[])
{
    char buf[128] = {0};
    int ret;
    int acceptfd;
    // 1.创建套接字(socket())------------------------》有手机
    int sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd < 0)
    {        perror("socket err");
        return -1;
    }
    printf("sockfd:%d\n", sockfd); // 3
    // 2.指定网络信息--------------------------------------》有号码
    struct sockaddr_in saddr, caddr;
    saddr.sin_family = AF_INET;
    saddr.sin_port = htons(atoi(argv[1])); // 网络字节序端口号
    // saddr.sin_addr.s_addr = inet_addr("192.168.51.78"); // 虚拟机IP
    // saddr.sin_addr.s_addr = inet_addr("0.0.0.0"); // 虚拟机IP
    saddr.sin_addr.s_addr = INADDR_ANY; // 虚拟机IP
    int len = sizeof(caddr);
    // 3.绑定套接字(bind())----------------------------》绑定手机(插卡)
    if (bind(sockfd, (struct sockaddr *)&saddr, sizeof(saddr)) < 0)
    {
        perror("bind err");
        return -1;
    }    printf("bind okk\n");
    // 4.监听套接字(listen())--------------------------》待机
    // 将主动套接字变为被动套接字
    // 队列1(未完成连接队列):4 5 6
    // 队列2(已完成连接队列):3
    if (listen(sockfd, 6) < 0)
    {
        perror("listen err");
        return -1;
    }
    printf("listen okk\n");
    while (1)
    {
        // 5.阻塞等待接收客户端连接请求(accept())-------------》接电话
        acceptfd = accept(sockfd, (struct sockaddr *)&caddr, &len);
        // accept会返回一个用于通信的文件描述符
        // 在tcp服务器中,有两类套接字,一类是socket函数返回(1个),用于链接的文件描述符
        // 一类是accept函数返回的(1个或者多个),用于通信的文件描述符
        if (acceptfd < 0)
        {
            perror("acceptfd err");
            return -1;
        }
        printf("acceptfd:%d\n", acceptfd);
        printf("ip:%s    port: %d\n", inet_ntoa(caddr.sin_addr), ntohs(caddr.sin_port));
        // 6.接受发送消息(send()/recv())-------------》通话
        while (1)
        {
            ret = recv(acceptfd, buf, sizeof(buf), 0); //==>read
            if (ret < 0)
            {
                perror("recv err");
                return -1;
            }
            else if (ret == 0)
            {
                printf("client exit\n");
                break;
            }
            printf("buf:%s\n", buf);            if (!strncmp(buf, "put ", 4))
                putfile(acceptfd, buf);
            if (!strncmp(buf, "get ", 4))
                getfile(acceptfd, buf);
            memset(buf, 0, sizeof(buf));
        }        // 7.关闭套接字(close)---------------------------》挂断电话
        close(acceptfd);
    }
    close(sockfd);
    return 0;
}void putfile(int acceptfd, char *p)
{
    int ret = 0;
    int fd = open(p + 4, O_WRONLY | O_CREAT | O_TRUNC, 0666);
    if (fd < 0)
    {
        perror("open err");
        return;
    }
    char buf[128] = {0};
    while (1)
    {
        ret = recv(acceptfd, buf, sizeof(buf), 0);
        if (ret < 0)
        {
            perror("recv err");
            return;
        }
        if (!strcmp(buf, "end"))
            break;
        write(fd, buf, strlen(buf));
        memset(buf, 0, sizeof(buf));
    }
}void getfile(int acceptfd, char *p)
{
    char buf[128] = {0};
    int ret = 0;
    int fd = open(p + 4, O_RDONLY);
    if (fd < 0)
    {
        perror("open err");
        return;
    }
    while (1)
    {
        ret = read(fd, buf, sizeof(buf) - 1);
        buf[ret] = '\0';
        if (ret == 0)
            break;
        send(acceptfd, buf, sizeof(buf), 0);
    }
    send(acceptfd, "end", sizeof(buf), 0);
    close(fd);
    return;
}

客户端

#include <stdio.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <string.h>
#include <stdlib.h>
#include <stdlib.h>
#include <dirent.h>
#include <fcntl.h>void show();
void list(int sockfd);
void putfile(int, char *);
void getfile(int sockfd, char *p);int main(int argc, char const *argv[])
{
    char buf[128] = {0};
    int ret;
    // 1.创建套接字(socket())-
    int sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd < 0)
    {        perror("socket err");
        return -1;
    }
    printf("sockfd:%d\n", sockfd); // 3
    // 2.指定网络信息
    struct sockaddr_in saddr;
    saddr.sin_family = AF_INET;
    // saddr.sin_port = htons(5678);                       // 网络字节序端口号
    // saddr.sin_addr.s_addr = inet_addr("192.168.51.78"); // 虚拟机IP
    saddr.sin_port = htons(atoi(argv[2]));      // 网络字节序端口号
    saddr.sin_addr.s_addr = inet_addr(argv[1]); // 虚拟机IP
    // 3.请求连接
    if (connect(sockfd, (struct sockaddr *)&saddr, sizeof(saddr)) < 0)
    {
        perror("connect err");
        return -1;
    }
    printf("connect okk\n");
    // 4.接受发送消息(send()/recv())-------------》通话
    while (1)
    {
        show();
        fgets(buf, sizeof(buf), stdin);
        // HELLO
        // printf("buf:%s\n", buf);
        if (buf[strlen(buf) - 1] == '\n')
            buf[strlen(buf) - 1] = '\0';
        if (!strcmp(buf, "quit"))
            break;
        send(sockfd, buf, sizeof(buf), 0);        if (!strncmp(buf, "put ", 4))
            putfile(sockfd, buf);
        if (!strncmp(buf, "get ", 4))
            getfile(sockfd, buf);
    }    // 7.关闭套接字(close)---------------------------》挂断电话
    close(sockfd);
    return 0;
}void show()
{
    printf("------------put filename------------\n");
    printf("------------get filename------------\n");
    printf("----------------quit----------------\n");
}void putfile(int sockfd, char *p)
{
    char buf[128] = {0};
    int ret = 0;
    int fd = open(p + 4, O_RDONLY);
    if (fd < 0)
    {
        perror("open err");
        return;
    }
    while (1)
    {
        ret = read(fd, buf, sizeof(buf) - 1);
        buf[ret] = '\0';
        if (ret == 0)
            break;
        send(sockfd, buf, sizeof(buf), 0);
    }
    send(sockfd, "end", sizeof(buf), 0);
    close(fd);
    return;
}void getfile(int sockfd, char *p)
{
    int ret = 0;
    int fd = open(p + 4, O_WRONLY | O_CREAT | O_TRUNC, 0666);
    if (fd < 0)
    {
        perror("open err");
        return;
    }
    char buf[128] = {0};
    while (1)
    {
        ret = recv(sockfd, buf, sizeof(buf), 0);
        if (ret < 0)
        {
            perror("recv err");
            return;
        }
        if (!strcmp(buf, "end"))
            break;
        write(fd, buf, strlen(buf));
        memset(buf, 0, sizeof(buf));
    }
}

http://www.xdnf.cn/news/19441.html

相关文章:

  • windows系统中安装zip版本mysql,配置环境
  • React学习教程,从入门到精通, ReactJS - 优点与缺点(5)
  • 线段树相关算法题(5)
  • LangGraph结构化输出详解:让智能体返回格式化数据
  • Midjourney绘画创作入门操作创作(广告创意与设计)
  • XHR 介绍及实践
  • 【Game-Infra】游戏开发的流程,游戏发布的打包与构建(硬件选型,SDK与操作系统,包体管理,弹性构建,构建调优)
  • 基于 GME-Qwen2-VL-7B 实现多模态语义检索方案
  • 人工智能学习:Python相关面试题
  • 零基础学C++,函数篇~
  • Visual Studio内置环境变量有哪些
  • MQTT 连接建立与断开流程详解(一)
  • Redission 实现延迟队列
  • Verilog 硬件描述语言自学——重温数电之典型组合逻辑电路
  • 基于 Spring Boot3 的ZKmall开源商城分层架构实践:打造高效可扩展的 Java 电商系统
  • 大语言模型的“可解释性”探究——李宏毅大模型2025第三讲笔记
  • Linux kernel 多核启动
  • Tomcat 企业级运维实战系列(六):综合项目实战:Java 前后端分离架构部署
  • 〔从零搭建〕数据中枢平台部署指南
  • 汽车加气站操作工证考试的复习重点是什么?
  • 如何取得专案/设计/设定/物件的属性
  • ETCD学习笔记
  • 手表--带屏幕音响-时间制切换12/24小时
  • 从零开始学习单片机18
  • 《云原生架构从崩溃失控到稳定自愈的实践方案》
  • 消费 $83,用Claude 实现临床护理系统记录单(所见即所得版)
  • C++三方服务异步拉起
  • MySQL函数 - String函数
  • Google Protobuf初体验
  • 深层语义在自然语言处理中的理论框架与技术融合研究