【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
接收者
- 創建套接字(socket)
- 設置多播屬性,將自己的IP加入到多播組中
- 指定網絡信息
- 綁定套接字(bind)
- 接收消息(recvfrom)
- 關閉套接字(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;
}
發送者
- 創建套接字(socket)
- 指定網絡(服務器)信息
- 發送消息(sendto)
- 關閉套接字(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是一個小型關系型數據庫管理系統。開放源碼 (嵌入式不需要存儲太多數據)
MySQL與SQLite區別:
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;
- 完成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));
}
}