网络编程
1.历史
1968:阿帕网问世,,是internet的雏形,但是传输数据不能跨平台 1974:TCP协议出现,在TCP协议中没有对应得检测纠错功能 1983:TCP/IP协议:具有跨平台,能检测检错得功能
协议:事先约定好得一种规则
2.网络协议模型
2.1 OSI七层网络协议模型
应用层:用户能看到的内容 表示层:对数据进行加密,解析 会话层:建立通信节点 传输层:实现点到点得通信 网络层:路由寻址 数据链路层:将数据封装成对应得帧格式、纠错、流控 物理层:屏蔽硬件差异
2.2 TCP/IP四层协议模型
1.应用层:HTTP(超文本传输协议)、FTP(文件传输协议)、NSF 2.传输层:TCP协议、UDP协议 3.网络层:IP协议、ICMP协议、IGMP协议(组播、广播) 4.物理与网络接口层:以太网协议、ARP协议、RARP协议(MAC-->IP),ppp
TCP通信特点:
面向链接,是种安全可靠、有序的传输通信、保证传输数据的准确无误不丢失、不失序
应用场景:用户登录、传输重要文件
UDP通信特点:
无连接,是一种不安全可靠的传输方式,传输数据不能保证数据的准确性
应用场景:流媒体软件、大型的音视频传输
网络通信的封包和拆包
一、socket编程预备知识
1.IP地址:
标识网络地址
IPV4: 32bits、 4字节大小的数据 2^32 表示方法: 点分十进制:“1.1.1.1” “192.168.2.14” 二进制:00000001 00000001 00000001 00000001 IPV6: 128bits、16字节 2^128
1.1 IP地址的转换函数
inet_aton //点分式 —–>二进制 只能用于 IPV4 inet_addr inet_pton 适用IPV4 IPV6 --------------------------------------------------------------------------------------------------------- inet_ntoa //二进制 -->点分式
inet_aton()
点分式 ———> 二进制 头文件: #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> 函数原型: int inet_aton(const char *cp, struct in_addr *inp); { 参数: cp:ip地址字符串 *inp: typedef uint32_t in_addr_t; struct in_addr { in_addr_t s_addr; }; } 返回值: 成功:1 失败:0
例子:
#include <stdio.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <stdlib.h> #include <unistd.h> int main(int argc, char *argv[]) { struct in_addr addr; if(0 == inet_aton("192.168.23.128",&addr)){ perror("inet_aton"); exit(-1); } printf("%u\n",addr.s_addr); return 0; }
inet_ntoa()
二进制 --> 点分式 头文件: #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> 函数原型: char *inet_ntoa(struct in_addr in); { 参数: 结构体定义的地址; }
例子:
int main(int argc, char *argv[]) { struct in_addr addr; if(0 == inet_aton("192.168.23.128",&addr)){ perror("inet_aton"); exit(-1); } printf("%u\n",addr.s_addr); printf("%u\n",inet_addr("192.168.23.128")); char *ptr = inet_ntoa(addr); printf("%s\n",ptr); return 0; }
2.端口号 —port
一个端口对应一个进程
网络中标识进程身份的
1.本质上是一个unisgned short类型的数据,取值范围0~65536 2.1~1023:系统端口,一般不使用 3.1024~5000:常用的应用接口 4.5001~65536:用户可以使用的端口
3.字节序
1.字节序又称主机字节序,是计算的机中多字节整型数据的存储方式 2.内存存储多字节整数的方法叫做主机字节序; 方法: 大端序:低地址存高字节,高地址存低字节(网络字节序) 小端序:高地址存高字节,低地址存低字节(主机字节序) 注意:网络数据流采用的是大端序
4.套接字–socket
4.1特点:
本质是一个特殊的按顺序分配的最小的文件描述符
1.是一个编程接口 2.并不仅限于TCP/IP协议 3.面向链接(TCP) 4.无连接(UDP)
4.2类型:
流式套接字---SOCK_STREAM 特点:面向连接、安全可靠 数据报套接字---SOCK_DGRAM 特点:无连接、不安全可靠 原始套接字---SOCK_RAW 特点:访问较低层次的协议
5.TCP编程———————
步骤
5.1 创建套接字-sockeat()
头文件: #include <sys/types.h> #include <sys/socket.h> 函数原型: int socket(int domain, int type, int protocol); { 参数: domain:地址族 AF_INET IPv4 协议 AF_INET6 IPv6协议 type:套接字类型 SOCK_STREAM:流式套接字--唯一对应tcp通信 SOCK_DGRAM :数据报套接字--唯一对应udp通信 SOCK_RAW :原始套接字-- protocol:默认为 0 } 返回值: 成功:套接字 失败:-1
例子:
#include <stdio.h> #include <sys/types.h> #include <sys/socket.h> #include <stdlib.h> int main(int argc, char *argv[]) { int sockfd = socket(AF_INET,SOCK_STREAM,0); if(sockfd < 0){ perror("socket"); exit(-1); } printf("sockfd = %d\n",sockfd); return 0; }
5.2绑定ip地址和端口号bind()
头文件: #include <sys/types.h> #include <sys/socket.h> 函数原型: int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen); { 参数: sockfd:创建的套接字 --socket的返回值 *addr:服务器的地址结构首地址 addrlen:服务器地址结构大小 } 返回值: 成功: 0 失败: -1 IPV4对应的地址结构: struct sockaddr_in { sa_family_t sin_family; //地址协议族 in_port_t sin_port; //端口号 struct in_addr sin_addr; // }; struct in_addr { uint32_t s_addr; /* address in network byte order */ };
例子:
struct sockaddr_in { saddr.sin_family = AF_INET; //IPV4 saddr.sin_port = 5002; saddr.sin_addr.s_addr = inet_addr("192.168.7.220");
5.3设置监听套接字 listen()
作用:指定监听上线数---同时允许多少客户端建立连接 头文件: #include <sys/types.h> #include <sys/socket.h> 函数原型: int listen(int sockfd, int backlog); { 参数: sockfd:创建的套接字--socket的返回值 backlog: 排队建立3次握手的队列和刚刚建立3次握手的队列的连接数和 } 返回值: 成功:0 失败:-1
5.4等待客服端连接accept()
作用:接收连接请求 头文件: #include <sys/types.h> #include <sys/socket.h> 函数原型: int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen); { 参数: sockfd:创建的套接字 addr:客服端的地址结构首地址 --传出参数 addrlen:客服端地址结构的长度的首地址 --传入传出参数 } 返回值: 成功:返回全新的套接字--用于服务器的数据读写 失败:-1
5.5 建立连接 –connect()
作用:等待客户端建立连接 头文件: #include <sys/types.h> #include <sys/socket.h> 函数原型: int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen); { 参数: socket:文件描述符 addr:传入参数,指定服务器地址信息,含IP地址和端口号 addrlen:传入参数,sizeof(addr)大小 } 返回值: 成功:0 失败:-1 设置错误号
代码:
服务器–tcp_serve.c
#include <stdio.h> #include <sys/types.h> #include <sys/socket.h> #include <stdlib.h> #include <unistd.h> #include <sys/socket.h> #include <netinet/in.h> #include <netinet/ip.h> #include <arpa/inet.h> #include <string.h> #include <ctype.h> #define SIZE 64 int main(int argc, char *argv[]) { char buf[64]={0}; pid_t pid; int ret,i; //创建套接字 int sockfd = socket(AF_INET,SOCK_STREAM,0); if(sockfd < 0){ perror("socket"); exit(-1); } printf("sockfd = %d\n",sockfd); struct sockaddr_in saddr;//定义服务器地址结构 saddr.sin_addr.s_addr = inet_addr("192.168.10.128"); //查看 ifconfig saddr.sin_family = AF_INET; //IPV4 saddr.sin_port = htons(8888); //任意的端口 int s_len = sizeof(saddr); //计算服务器结构地址大小 ret = bind(sockfd,(struct sockaddr*)&saddr,s_len); //绑定 if(ret < 0){ perror("bind"); exit(-1); } printf("bind successful\n"); ret = listen(sockfd,128); //监听套接字 if(ret < 0){ perror("listen"); exit(-1); } printf("listen successful!\n"); struct sockaddr_in caddr; memset(&caddr,0,sizeof(caddr)); int c_len = sizeof(caddr); int connfd = accept(sockfd,(struct sockaddr*)&caddr,&c_len); if(connfd < 0){ perror("accept"); exit(-1); } printf("successful! ip: %s port:%d\n",inet_ntoa(caddr.sin_addr),ntohs(caddr.sin_port)); while(1){ memset(buf,0,sizeof(buf)); ret = read(connfd,buf,64); if(ret < 0){ perror("read"); exit(-1); }else if(ret == 0){ close(connfd); break; } printf("ret->%d buf->%s\n",ret,buf); } close(sockfd); return 0; }
客服端–tcp_client
#include <stdio.h> #include <sys/types.h> #include <sys/socket.h> #include <stdlib.h> #include <unistd.h> #include <sys/socket.h> #include <netinet/in.h> #include <netinet/ip.h> #include <arpa/inet.h> #include <string.h> #define SIZE 64 int main(int argc, char *argv[]) { //创建套接字 int connfd = socket(AF_INET,SOCK_STREAM,0); if(connfd < 0){ perror("socket"); exit(-1); } printf("connfd = %d\n",connfd); struct sockaddr_in saddr;//定义服务器地址结构 saddr.sin_addr.s_addr = inet_addr("192.168.10.128"); //查看 ifconfig saddr.sin_family = AF_INET; //IPV4 saddr.sin_port = htons(8888); int s_len = sizeof(saddr); //计算服务器结构地址大小 int ret = connect(connfd,(struct sockaddr*)&saddr,s_len); //绑定 if(ret < 0){ perror("connect"); exit(-1); } printf("connect successful\n"); char buf[SIZE]={0}; while(1){ printf("请输入\n"); fgets(buf,sizeof(buf),stdin); buf[sizeof(buf)-1] = '\0'; ret=write(connfd,buf,strlen(buf)); if(ret < 0){ perror("read"); exit(-1); } } close(connfd); return 0; }
※tcp三次握手
1.第一次握手:客户端发送一个带有SYN(待确认数据包)标志的数据包给服务器。客户端进入 SYN_SEND状态 (待发送) 2.第二次握手:服务器接收后回传一个带有SYN/ACK标志的数据包给客户端。服务器进入SYN_RECV状态 (待接收) 3.第三次握手:最后,客户端回传一个带有ACK标志的数据包给服务器端,建立TCP链接,进入ESTABLIAHEND状态
※tcp四次挥手
意义:当服务器收到客户端FIN报表时,不确认是否所有数据都发送给了客户端,所以服务器不会马上管理链接,会发送ACK,在发送FIN给客户端,之后关闭连接。 1.第一次挥手:客户端发送FIN,用来关闭客户端和服务端的数据传输,这个时候客户端进入结束等待状态。 2.第二次挥手:当服务器端收到FIN时,会发送给客户端一个AKN,这个时候服务器端进入关闭等待状态。 3.第三次挥手:服务器端会发送一个FIN,用来关闭服务器端和客户端的数据传输,这个时候服务器端会进入最后确认状态。 4.第四次挥手:当客户端收到FIN时会进入最后等待状态,同时给服务端发送一个AKN,这个时候服务端会进入关闭状态。完成四次挥手。
6.服务器模型—————
6.1循环服务器
概念:
指每个服务器端依次处理每个客户端,直到当前客户端的所有请求处理完毕,再处理下一个客户端
优点:
简单
缺点:
容易造成其他客户端等待时间过长的情况
6.2多进程并发服务器
概念:
服务器端采用多任务机制(多进程或多线程)分别为每个客户端创建一个任务来处理,极大的提高了服务器的并发能力
工作流程
1.服务器端父进程从连接请求队列中提取请求,建立连接并返回新的已连接套接字 2.服务器端父进程创建子进程为客户端服务,客户端关闭连接时,子进程结束 3.服务器端父进程关闭已连接套接字,返回步骤(1)
创建步骤:
pid_t pid; int sockfd = socket(); bind() //绑定 listen() //监听 signal(17,func) while(1) { int connfd = accept() pid = fork() if(pid == 0) { while(1) { read/write } } close(connfd) } void func{ wait(NULL) }
代码:
server.c
#include <stdio.h> #include <sys/types.h> #include <sys/socket.h> #include <stdlib.h> #include <unistd.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <string.h> #include <ctype.h> #include <sys/wait.h> void wait_child(int signo) { while(waitpid(0,NULL,WNOHANG) > 0); return ; } int main(int argc, char *argv[]) { pid_t pid; char buf[64] = {0}; int sockfd = socket(AF_INET,SOCK_STREAM,0); if(sockfd < 0){ perror("socket"); exit(-1); } printf("sockfd:%d\n",sockfd); struct sockaddr_in saddr; bzero(&saddr,sizeof(saddr)); saddr.sin_family = AF_INET; saddr.sin_port = htons(8888); saddr.sin_addr.s_addr = inet_addr("192.168.10.128"); int s_len =sizeof(saddr); int ret = bind(sockfd,(struct sockaddr *)&saddr,s_len); if(ret < 0){ perror("bind"); exit(-1); } printf("bing successful\n"); ret = listen(sockfd,128); if(ret < 0){ perror("listen"); exit(-1); } printf("listen .......\n"); while(1){ struct sockaddr_in caddr; int c_len = sizeof(caddr); int connfd = accept(sockfd,(struct sockaddr*)&caddr,&c_len); if(connfd < 0){ perror("accept"); exit(-1); } printf("accept succesufl\n"); printf("client: IP-> %s,port->%d\n",inet_ntoa(caddr.sin_addr),ntohs(caddr.sin_port)); pid = fork(); if(pid < 0){ perror("foek"); exit(-1); }else if(pid == 0){ close(sockfd); break; }else{ close(connfd); signal(SIGCHLD,wait_child); } } if(pid == 0){ while(1){ int connfd; ret = read(connfd,buf,sizeof(buf)); if(ret == 0){ close(connfd); return 0; }else if(ret == -1){ perror("read error"); exit(-1); }else{ for(int i = 0;i<ret;i++) buf[i] = toupper(buf[i]); write(connfd,buf,ret); } } } return 0; }
6.3多线程的并发服务器
创建步骤
int sockfd = socket(); bind(); listen(); int connfd; pthread_t thread; while(1) { connfd = accept(); pthread_create(&thread, NULL, func, &connfd); //pthread_join(thread, NULL); pthread_detach(thread); } void *func(void *arg) { int connfd = *(int *)arg; while(1) { read(connfd); printf(""); } }
代码:
server.c
#include <stdio.h> #include <stdio.h> #include <sys/types.h> #include <sys/socket.h> #include <stdlib.h> #include <unistd.h> #include <sys/socket.h> #include <netinet/in.h> #include <netinet/ip.h> #include <arpa/inet.h> #include <string.h> #include <ctype.h> #include <pthread.h> void *func(void *arg); int main(int argc, char *argv[]) { int sockfd =socket(AF_INET,SOCK_STREAM,0); if(sockfd < 0){ perror("socket"); exit(-1); } printf("sockfd->%d\n",sockfd); struct sockaddr_in saddr; memset(&saddr,0,sizeof(saddr)); saddr.sin_family = AF_INET;; saddr.sin_port = htons(8888); saddr.sin_addr.s_addr = inet_addr("192.168.10.128"); int s_len = sizeof(saddr); int ret = bind(sockfd,(struct sockaddr*)&saddr,s_len); if(ret < 0){ perror("bind"); exit(-1); } printf("bind successful!\n"); ret = listen(sockfd,128); if(ret < 0){ perror("listen"); exit(-1); } printf("listen......\n"); int connfd; char buf[128] = {0}; pthread_t tid; while(1){ struct sockaddr_in caddr; int c_len = sizeof(caddr); connfd = accept(sockfd,(struct sockaddr*)&caddr,&c_len); if(connfd < 0){ perror("accept"); exit(-1); } printf("client->IP:%s,client->port:%d\n",inet_ntoa(caddr.sin_addr),ntohs(caddr.sin_port)); } tid = pthread_create(&tid,NULL,func,&connfd); if(tid < 0){ perror("pthread_create"); exit(-1); } if(0 != pthread_detach(tid)){ perror("pthread_detach"); exit(-1); } close(sockfd); return 0; } void *func(void *arg) { int connfd = *(int*)arg; while(1){ char buf[128]={0}; memset(buf,0,128); int ret = read(connfd,buf,128); if(ret < 0){ perror("read"); exit(-1); } if(ret == 0){ printf("a client leave!\n"); break; } printf("ret->%d buf->%s\n",ret,buf); } close(connfd); }
7.UDP编程————————
传输层协议
特点
无连接,是一种不安全可靠(不保证数据传输的准确性、无误、不失序)的通信协议
工作流程
7.1 UDP的相关函数
sendto()
头文件: #include <sys/types.h> #include <sys/socket.h> 函数原型: ssize_t send(int sockfd, const void *buf, size_t len, int flags); ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen); { 参数: sockfd:数据报套接字 buf:发送数据的缓冲区首地址 len:请求发送的字节数 flag:发送方式,默认填0 dest_addr:接收方的地址结构首地址 addrlen:地址结构的长度 }
recvfrom()
头文件: #include <sys/types.h> #include <sys/socket.h> 函数原型: ssize_t recv(int sockfd, void *buf, size_t len, int flags); ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen); { 参数: sockfd:数据报套接字 buf:发送数据的缓冲区首地址 len:请求发送的字节数 flag:发送方式,默认填0 src_addr:接收方的地址结构首地址 addrlen:地址结构的长度的地址 }
代码:
服务器端
#include <stdio.h> #include <stdio.h> #include <sys/types.h> #include <sys/socket.h> #include <stdlib.h> #include <unistd.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <string.h> #include <ctype.h> #include <sys/wait.h> int main(int argc, char *argv[]) { //1.创建套接子 int sockfd = socket(AF_INET,SOCK_DGRAM,0); if(socket < 0){ perror("socket"); exit(-1); } //绑定IP struct sockaddr_in saddr,caddr; saddr.sin_family = AF_INET; saddr.sin_port = htons(8888); saddr.sin_addr.s_addr = htons(INADDR_ANY); int s_len = sizeof(saddr); int ret = bind(sockfd,(struct sockaddr*)&saddr,s_len); if(ret < 0){ perror("bind"); exit(-1); } printf("a new client ......\n"); //发送接受数据 memset(&caddr,0,sizeof(caddr)); int c_len =sizeof(caddr); char buf[64] = {0}; while(1){ memset(buf,0,64); ret = recvfrom(sockfd,buf,64,0,(struct sockaddr*)&caddr,&c_len); if(ret < 0){ perror("recvfrom"); exit(-1); } printf("IP->%s port->%d\n",inet_ntoa(caddr.sin_addr),ntohs(caddr.sin_port)); printf("%s\n",buf); } close(sockfd); return 0;
客户端
#include <stdio.h> #include <stdio.h> #include <sys/types.h> #include <sys/socket.h> #include <stdlib.h> #include <unistd.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <string.h> #include <ctype.h> #include <sys/wait.h> int main(int argc, char *argv[]) { //创建套接子 int sockfd = socket(AF_INET,SOCK_DGRAM,0); if(sockfd < 0){ perror("socket"); exit(-1); } //数据的发送 struct sockaddr_in caddr; caddr.sin_family = AF_INET; caddr.sin_port = htons(8888); caddr.sin_addr.s_addr = htons(INADDR_ANY); int c_len = sizeof(caddr); char buf[64]={0}; int ret; while(1){ printf("请给服务器发送消息\n"); fgets(buf,sizeof(buf),stdin); ret = sendto(sockfd,buf,64,0,(struct sockaddr*)&caddr,c_len); if(ret < 0){ perror("sendto"); exit(-1); } } close(sockfd); return 0; }
练习:
回传时间应答
服务器端 #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 <string.h> #include <unistd.h> #include <stdlib.h> #include <time.h> int main(int argc, char *argv[]) { time_t t; //1、创建套接字 int sockfd = socket(AF_INET, SOCK_DGRAM, 0); if(sockfd < 0) { perror("socket"); exit(-1); } //2、绑定IP地址和端口号 struct sockaddr_in saddr, caddr; saddr.sin_family = AF_INET; saddr.sin_port = htons(8888); saddr.sin_addr.s_addr = htons(INADDR_ANY); int s_len = sizeof(saddr); int ret = bind(sockfd, (struct sockaddr*)&saddr, s_len); if(ret < 0) { perror("bind"); exit(-1); } //3、发送接收数据 memset(&caddr, 0, sizeof(caddr)); int c_len = sizeof(caddr); char buf[64] = {0}; while(1) { memset(buf, 0, 64); ret = recvfrom(sockfd, buf, 64, 0, (struct sockaddr*)&caddr, &c_len); if(ret < 0) { perror("recvfrom"); exit(-1); } //printf("ip -- %s, port -- %d: %s\n", inet_ntoa(caddr.sin_addr), ntohs(caddr.sin_port), buf); if(strncmp(buf, "time", 4) == 0) { memset(buf, 0, 64); time(&t); strcpy(buf, ctime(&t)); sendto(sockfd, buf, 64, 0, (struct sockaddr*)&caddr, c_len); } else { memset(buf, 0, 64); strcpy(buf, "input error! plz input <'time'>"); sendto(sockfd, buf, 64, 0, (struct sockaddr *)&caddr, c_len); } } //4、关闭套接字 close(sockfd); return 0; } ----------------------------------------------------- 客户端 #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 <string.h> #include <unistd.h> #include <stdlib.h> int main(int argc, char *argv[]) { int sockfd = socket(AF_INET, SOCK_DGRAM, 0); if(sockfd < 0) { perror("socket"); exit(-1); } struct sockaddr_in saddr, caddr; saddr.sin_family = AF_INET; saddr.sin_port = htons(8888); saddr.sin_addr.s_addr = inet_addr("192.168.7.216"); int s_len = sizeof(saddr); int c_len = sizeof(caddr); char buf[64] = {0}; fgets(buf, 64, stdin); int ret = sendto(sockfd, buf, strlen(buf), 0, (struct sockaddr*)&saddr, s_len); if(ret < 0) { perror("sendto"); exit(-1); } memset(buf, 0, 64); ret = recvfrom(sockfd, buf, 64, 0, (struct sockaddr*)&caddr, &c_len); if(ret < 0) { perror("recvfrom"); exit(-1); } printf("recv:%s\n", buf); close(sockfd); return 0; }
8.IO模型—————————
1.阻塞IO
读阻塞:当进行读操作没有相关资源,程序阻塞,当有资源的时候,程序继续执行
写阻塞:当写缓冲区满时,会出现写阻塞,当缓冲区有足够空间时候,程序继续执行
缺点:阻塞IO会阻碍其他程序的执行
2.非阻塞IO
fcntl()
设置描述符的属性
头文件: #include <unistd.h> 函数原型: int fcntl(int fd, int cmd, ... /* arg */ ); { 参数: fd:文件描述符 cmd:命令 F_GETFL:获取指定文件描述符的属性 F_SETFL:设置指定文件描述符的属性 arg: 取决于cmd,如果是 F_GETFL,arg可忽略 如果是F_SETFL,arg表示设置属性 } 返回值: 成功: 失败:-1 例: 1、获取的对应文件描述符的属性 int flag = fcntl(fd, F_GETFL, 0); 2、改变状态标志 flag |= O_NONBLOCK; //将该文件描述符的属性设置为非阻塞 3、设置对应文件描述符的属性 fcntl(fd, F_SETFL, flag); 缺点:轮询进行遍历,消耗系统资源
代码:
#include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> int main(int argc, char *argv[]) { int fd = open("/dev/input/mice", O_RDWR); if(fd < 0) { perror("open"); return -1; } char buf[64] = {0}; char buf1[64] = {0}; int flag = fcntl(0, F_GETFL, 0); flag |= O_NONBLOCK; fcntl(0, F_SETFL, flag); /* flag = fcntl(fd, F_GETFL, 0); flag |= O_NONBLOCK; fcntl(fd, F_SETFL, flag); */ while(1) { fgets(buf, 64, stdin); printf("buf = %s\n", buf); read(fd, buf1, 64); printf("%d -- %d -- %d\n", buf1[0], buf1[1], buf1[2]); } return 0; }
3.IO多路复用
IO多路复用可以同时监控多个文件描述符,找到其中“活动的”描述符,所谓活动的就是指可以操作读写错误异常,实现IO多路复用
3.1 select()
1.准备好要监控的描述符 2.将要监控的描述符放到描述符集合中 void FD_CLR(int fd, fd_set *set); 功能:从指定检测表中,删除某个文件描述符 int FD_ISSET(int fd, fd_set *set); 功能:判断指定的文件描述符是否有响应 void FD_SET(int fd, fd_set *set); 功能:从指定的检测表中,添加某个文件描述符 void FD_ZERO(fd_set *set); 功能:清空描述符集合 3.调用select函数集合 函数原型: int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout); { 参数: int nfds:所有检测表中,文件描述符最大+1 fd_set *readfds:读检测表的首地址(是否可读) fd_set *writefds:写检测表的首地址(是否可写) fd_set *exceptfds:异常检测表的首地址(是否异常) struct timeval *timeout:设置超时检测的时间(不超时传NULL) struct timeval{ long tv_sec; long tv_usec; } } 返回值: 成功:返回活动的描述符个数 超时:返回 0 出错:返回 -1 4.处理活动的描述符
缺点: select机制检测的文件描述符的上线为1024个 select机制中初始和返回表是分离的
代码1:
#include <stdio.h> #include <sys/types.h> #include <sys/socket.h> #include <stdlib.h> #include <unistd.h> #include <sys/socket.h> #include <netinet/in.h> #include <netinet/ip.h> #include <arpa/inet.h> #include <string.h> #include <ctype.h> #include <sys/select.h> #include <sys/time.h> #define SIZE 64 int main(int argc, char *argv[]) { char buf[1000]={0}; int ret; //创建套接字 int sockfd = socket(AF_INET,SOCK_STREAM,0); if(sockfd < 0){ perror("socket"); exit(-1); } printf("sockfd = %d\n",sockfd); struct sockaddr_in saddr;//定义服务器地址结构 saddr.sin_addr.s_addr = inet_addr("192.168.10.128"); //查看 ifconfig saddr.sin_family = AF_INET; //IPV4 saddr.sin_port = htons(8888); //任意的端口 int s_len = sizeof(saddr); //计算服务器结构地址大小 ret = bind(sockfd,(struct sockaddr*)&saddr,s_len); //绑定 if(ret < 0){ perror("bind"); exit(-1); } printf("bind successful\n"); ret = listen(sockfd,128); //监听套接字 if(ret < 0){ perror("listen"); exit(-1); } printf("listen successful!\n"); //准备描述符集合 fd_set set; int *fds = malloc(1000*sizeof(int)); //设置所有的值为-1 memset(fds,-1,1000*sizeof(int)); int maxfd; //最大的描述符 int i; while(1){ maxfd = sockfd; FD_ZERO(&set); FD_SET(sockfd,&set); //将监听的描述符加入set //连接上客户端的描述符加入set for(i=0;i<1000;i++){ if(fds[i] != -1){ FD_SET(fds[i],&set); //记录最大的描述符 if(fds[i] > maxfd){ maxfd = fds[i]; } } //select if(select(maxfd+1,&set,NULL,NULL,NULL)<=0){ printf("error....\n"); continue; } } //处理活动的描述符 //有客户端连上来 if(FD_ISSET(sockfd,&set)){ struct sockaddr_in client; socklen_t len = sizeof(client); int newfd = accept(sockfd,(struct sockaddr*)&client,&len); if(newfd < 0){ perror("accept"); continue; } //将新连接上来的描述符加入到fds中 for(i=0;i<1000;i++){ if(fds[i] == -1) break; } fds[i]=newfd; printf("%s到此一遇\n",inet_ntoa(client.sin_addr)); } //有客户端发消息 for(i=0;i<1000;i++){ if(fds[i] != -1 && FD_ISSET(fds[i],&set)){ ret =read(fds[i],buf,sizeof(buf)); if(ret <= 0){ close(fds[i]); fds[i] = -1; continue; } printf("%s\n",buf); if(strcmp(buf,"quit") == 0){ close(fds[i]); fds[i] = -1; continue; } //原路发回 write(fds[i],buf,ret); } } } close(sockfd); return 0; }
代码2:
服务器 #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 <string.h> #include <unistd.h> #include <stdlib.h> #include <sys/wait.h> #include <sys/select.h> #include <sys/time.h> int main(int argc, char *argv[]) { int sockfd = socket(AF_INET, SOCK_STREAM, 0); if(sockfd < 0) { perror("socket"); exit(-1); } struct sockaddr_in saddr; saddr.sin_family = AF_INET; saddr.sin_port = htons(8888); //saddr.sin_addr.s_addr = inet_addr("192.168.7.220"); saddr.sin_addr.s_addr = htons(INADDR_ANY); int s_len = sizeof(saddr); int ret = bind(sockfd, (struct sockaddr*)&saddr, s_len); if(ret < 0) { perror("bind"); exit(-1); } ret = listen(sockfd, 5); if(ret < 0) { perror("listen"); exit(-1); } struct sockaddr_in caddr; memset(&caddr, 0, sizeof(caddr)); int c_len = sizeof(caddr); char buf[64] = {0}; fd_set rfds, tmp; //创建读监测表 FD_ZERO(&rfds); //将读监测表的每一位清0 FD_SET(sockfd, &rfds); //将套接字加入读监测表 int maxfd = sockfd+1; //获取文件描述符最大值+1 int connfd = -1; while(1) { //printf("wait for a new client...\n"); tmp = rfds; ret = select(maxfd, &tmp, NULL, NULL, NULL); //监测所有表 if(ret < 0) { perror("select"); exit(-1); } for(int i = 0; i < maxfd; i++) //循环遍历所有表 { if(FD_ISSET(i, &tmp)) //判断是否有文件描述符响应 { if(i == sockfd) //判断响应的文件描述符是否为sockfd { connfd = accept(i, (struct sockaddr *)&caddr, &c_len); if(connfd < 0) { perror("connect"); exit(-1); } printf("link successful! ip -- %s port -- %d \n", inet_ntoa(caddr.sin_addr), ntohs(caddr.sin_port)); FD_SET(connfd, &rfds); // 将accept得到的新的套接字添加到读监测表中 (connfd+1) > maxfd ? (maxfd = maxfd+1):(maxfd = maxfd); //改变maxfd的值 } else if(i > sockfd) { memset(buf, 0, 64); ret = read(i, buf, 64); if(ret < 0) { perror("read"); exit(-1); } if(ret == 0) { printf("a client leave!\n"); close(i); FD_CLR(i, &rfds); //将关闭的文件描述符从读监测表中删除 break; } printf("recv %d:%s\n", ret, buf); } } } } close(sockfd); return 0; }
3.2 poll()
头文件: #include <poll.h> 函数原型: int poll(struct pollfd *fds, nfds_t nfds, int timeout); { 参数: struct pollfd *fds:本质是数组,检测文件的最大个数 }
注意:select函数与poll函数的区别是,前者底层是数组,所以有最大连接数的限制,后者是链表,无最大连接数的限制)
练习:
poll实现并发服务器
server.c
#include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <sys/socket.h> #include <fcntl.h> #include <netinet/in.h> #include <arpa/inet.h> #include <string.h> #include <unistd.h> #include <stdlib.h> #include <sys/time.h> #include <sys/select.h> #include <poll.h> #define SIZE 64 int server_init(char *ipaddr, unsigned short port, int backlog) { int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建网络通信接口 if(-1 == sockfd) { perror("socket"); return -1; } //printf("sockfd=%d\n",sockfd); struct sockaddr_in saddr;//服务器的地址结构 bzero(&saddr,sizeof(saddr));//memset() saddr.sin_family = AF_INET;//指定协议族ipv4 saddr.sin_port = htons(port);//端口号:5001~65535 saddr.sin_addr.s_addr = (ipaddr == NULL)? htonl(INADDR_ANY) : inet_addr(ipaddr);//ip地址 点分式 -> 二进制网络字节序 int ret = bind(sockfd, (struct sockaddr *)&saddr, sizeof(saddr));//绑定 将服务器的ip和port与sockfd绑定 if(-1 == ret) { perror("bind"); close(sockfd); return -1; } //printf("bind success\n"); ret = listen(sockfd, backlog);//监听是否有客户端请求连接 sockfd:监听套接字 if(-1 == ret) { perror("listen"); close(sockfd); return -1; } return sockfd; } //设置非阻塞 int set_nonblock(int fd) { int flags = fcntl(fd, F_GETFL);//获取fd的状态标志位 if(-1 == flags) { perror("fcntl"); return -1; } flags |= O_NONBLOCK;//设置非阻塞状态 return (fcntl(fd, F_SETFL, flags)); } int main(int argc, char *argv[]) { if(argc < 3) { printf("Usage:%s <ip> <port>\n",argv[0]); return -1; } int sockfd = server_init(argv[1], atoi(argv[2]), 1024); if(-1 == sockfd) { printf("server_init error\n"); return -1; } printf("listen....\n"); struct sockaddr_in caddr;//保存客户端的信息(ip port protocol) bzero(&caddr, sizeof(caddr)); socklen_t clen = sizeof(caddr); //rws:用于服务器和连接的客户端通信 int rws = accept(sockfd, NULL, NULL);//阻塞等待客户端请求连接 if(-1 == rws) { perror("accept"); close(sockfd); return -1; } printf("rws=%d\n",rws); //通信 char buf[SIZE] = {0}; struct pollfd fd[1024] = {0};//定义文件描述符集合 //将0和rws文件描述符添加到fd中 fd[0].fd = 0; fd[0].events = POLLIN;//监测读事件 fd[1].fd = rws; fd[1].events = POLLIN;//监测读事件 int nfds = 2;//监测的文件描述符的个数 int ret; while(1) { ret = poll(fd,nfds,-1);//监测是否有文件描述符准备就绪 if(-1 == ret) { perror("poll"); break; } else if(0 == ret) { printf("timeout....\n"); continue; } if(fd[0].revents & POLLIN)//判断读标准输入是否准备就绪 { bzero(buf,sizeof(buf)); fgets(buf,sizeof(buf),stdin); //回传消息给客户端 ret = send(rws,buf,sizeof(buf),0); if(-1 == ret) { perror("send"); break; } } if(fd[1].revents & POLLIN)//rws准备就绪 { bzero(buf, sizeof(buf)); int ret = recv(rws,buf,sizeof(buf),0);//读客户端消息 if(-1 == ret) { perror("recv"); close(rws); fd[1].fd = -1; break; } else if(0 == ret)//表示客户端关闭 { printf("client closed\n"); close(rws); fd[1].fd = -1; break; } puts(buf); } } close(sockfd); return 0; }
client.c
#include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <sys/socket.h> #include <fcntl.h> #include <netinet/in.h> #include <arpa/inet.h> #include <string.h> #include <unistd.h> #include <poll.h> #define SIZE 64 int main(int argc, char *argv[]) { int sockfd = socket(AF_INET, SOCK_STREAM, 0);//创建网络通信接口 if(-1 == sockfd) { perror("socket"); return -1; } printf("sockfd=%d\n",sockfd); struct sockaddr_in saddr;//服务器的地址结构 bzero(&saddr,sizeof(saddr));//memset() saddr.sin_family = AF_INET;//指定协议族ipv4 saddr.sin_port = htons(8888);//端口号:5001~65535 saddr.sin_addr.s_addr = inet_addr("192.168.1.12");//ip地址 点分式 -> 二进制网络字节序 int ret = connect(sockfd, (struct sockaddr *)&saddr, sizeof(saddr));//绑定 将服务器的ip和port与sockfd绑定 if(-1 == ret) { perror("connect"); close(sockfd); return -1; } printf("connect success\n"); struct pollfd fd[1024] = {0};//定义文件描述符集合 //将0和sockfd文件描述符添加到fd中 fd[0].fd = 0; fd[0].events = POLLIN;//监测读事件 fd[1].fd = sockfd; fd[1].events = POLLIN;//监测读事件 int nfds = 2;//监测的文件描述符的个数 //通信 char buf[SIZE] = {0}; do{ ret = poll(fd,nfds,-1);//监测是否有文件描述符准备就绪 if(-1 == ret) { perror("poll"); break; } else if(0 == ret) { printf("timeout....\n"); continue; } if(fd[0].revents & POLLIN)//0准备就绪 { bzero(buf,sizeof(buf)); fgets(buf,sizeof(buf),stdin);//标准输入获取字符串 ret = write(sockfd,buf,sizeof(buf));//给服务器写请求 if(-1 == ret) { perror("write"); break; } } if(fd[1].revents & POLLIN)//sockfd可读 { bzero(buf,sizeof(buf)); ret = read(sockfd, buf, sizeof(buf));//读取服务器回传的内容 if(-1 == ret) { perror("read"); break; } puts(buf); } }while(strncmp(buf,"quit",4) != 0); close(sockfd); return 0; }
3.3epoll()
适用于linux
※epoll_create()
头文件: #include <sys/epoll.h> 函数原型: int epoll_create(int size); { 参数: size: 一般填1 } 返回值: 成功:返回一个操作epoll文件描述符表的句柄(文件描述符) 失败:返回-1
※epoll_ctl()
头文件: #include <sys/epoll.h> 函数原型: int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event); { 参数: epfd: 操作文件描述符表的相关句柄 op: EPOLL_CTL_ADD:添加事件 EPOLL_CTL_DEL:删除事件 fd: 相关的文件描述符 event:操作事件的首地址(文件描述符中某个操作元素的首地址) typedef union epoll_data { void *ptr; int fd; uint32_t u32; uint64_t u64; } epoll_data_t; struct epoll_event { uint32_t events; /* Epoll events */ epoll_data_t data; /* User data variable */ };
※检测epoll_wait()
#include <sys/epoll.h> int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout); { 参数: epfd:操作文件描述符相关句柄 events:文件描述符表的首地址 maxevents:监测事件的总项数 timeout:用于超时检测 }
代码:
#include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <poll.h> #include <unistd.h> #include <stdlib.h> #include <sys/epoll.h> int main(int argc, char *argv[]) { int fd1 = open("/dev/input/mice", O_RDONLY); char buf1[64] = {0}; char buf2[64] = {0}; int epfd = epoll_create(2); struct epoll_event pfd[1000]; for(int i = 0; i < 1000; i++) { pfd[i].data.fd = -1; } int pos = -1; pfd[++pos].events = EPOLLIN; pfd[pos].data.fd = 0; epoll_ctl(epfd, EPOLL_CTL_ADD, 0, &pfd[pos]); pfd[++pos].events = EPOLLIN; pfd[pos].data.fd = fd1; epoll_ctl(epfd, EPOLL_CTL_ADD, fd1, &pfd[pos]); int ret; while(1) { ret = epoll_wait(epfd, pfd, pos+1, -1); if(ret < 0) { perror("poll"); exit(-1); } for(int i = 0; i <= pos; i++) { if(pfd[i].events & EPOLLIN) { if(pfd[i].data.fd == 0) { fgets(buf1, 64, stdin); printf("buf1 = %s\n", buf1); } else if(pfd[i].data.fd == fd1) { read(fd1, buf2, 64); printf("%d -- %d -- %d\n", buf2[0], buf2[1], buf2[2]); } } } } return 0; }
9.设置套接字属性
设置setsockopt()
头文件: #include <sys/types.h> #include <sys/socket.h> 函数原型: int setsockopt(int sockfd, int level, int optname, const void * optval, socklen_t optlen); { 参数: sockfd:套接字 level:设置属性层 SOL_SOCKET:通用套接字层 IPPROTO_IP:IP层 IPPRO_TCP:TCP层 optname:指定操作(宏) optval:1 optlen: 指针指向无法存取的内存空间. } 返回值: 成功:则返回 0, 失败:则返回-1, 错误原因存于 errno.
获取getsockopt()
头文件: #include <sys/types.h> #include <sys/socket.h> 函数原型: int getsockopt(int sockfd, int level, int optname, const void * optval, socklen_t optlen); { 参数: sockfd:套接字 level:设置属性层 SOL_SOCKET:通用套接字层 IPPROTO_IP:IP层 IPPRO_TCP:TCP层 optname:指定操作(宏) optval:1 optlen: 指针指向无法存取的内存空间. } 返回值: 成功:则返回 0, 失败:则返回-1, 错误原因存于 errno.
※检测超时检测的三种方法
1.信号量 捕捉信号 signal 2.io多路复用 3.设置套接字属性
10.广播和组播(多播)—UDP
1.ip地址分类
A类地址: 最高位为0,主机号占24位 地址范围:1.0.0.1~126.255.255.254 B类地址: 最高位为10,主机号占16位 地址范围:128.0.0.1~191.254.255.254 C类地址: 最高位为110,主机号占8位 地址范围:192.0.1.1~123.255.254.254 D类地址:(又称为组播地址) 最高位为1110 地址范围:224.0.0.1~239.255.255.254 E类地址保留
2.广播
应用:投屏软件,ip获取
服务器: 1、创建套接字 -- socket(); 2、绑定IP地址和端口号 -- bind(); while(1) { 3、发送、接收数据 -- sendto()/recvfrom(); } 4、关闭套接字 -- close(); 客户端: 1、创建套接字 -- socket(); 2、设置套接字属性,允许发送广播数据 -- setsockopt(); while(1) { 3、向广播地址发送数据 -- sendto()/recvfrom(); } 4、关闭套接字 -- close();
3.组播
//服务器 1.创建套接字---socket 2.setsockopt()--将当前主机加入到组播IP组中去 3.将当前IP加入到指定的组播中 -- setsockopt() 4.绑定IP和端口号--bind while(1) { 5.发送、接收数据---sendto,recvfrom } 6.关闭套接字---close //客户端 1.创建套接字---socket 3.while(1) { 3.发送、接收数据---sendto,recvfrom } 4.关闭套接字---close struct ip_mreq { struct in_addr imr_multiaddr; //组播IP struct in_addr imr_interface; //本机IP };
代码:
server.c
#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 <string.h> #include <unistd.h> #include <stdlib.h> int main(int argc, char *argv[]) { //1、创建套接字 int sockfd = socket(AF_INET, SOCK_DGRAM, 0); if(sockfd < 0) { perror("socket"); exit(-1); } struct ip_mreq ip_group; ip_group.imr_multiaddr.s_addr = inet_addr("239.10.0.1"); ip_group.imr_interface.s_addr = inet_addr("192.168.7.216"); int g_len = sizeof(ip_group); if(setsockopt(sockfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &ip_group, g_len) < 0) { perror("setsockopt IP_ADD_MEMBERSHIP"); exit(-1); } //2、绑定IP地址和端口号 struct sockaddr_in saddr, caddr; saddr.sin_family = AF_INET; saddr.sin_port = htons(8888); saddr.sin_addr.s_addr = htons(INADDR_ANY); int s_len = sizeof(saddr); int ret = bind(sockfd, (struct sockaddr*)&saddr, s_len); if(ret < 0) { perror("bind"); exit(-1); } //3、发送接收数据 memset(&caddr, 0, sizeof(caddr)); int c_len = sizeof(caddr); char buf[64] = {0}; while(1) { memset(buf, 0, 64); ret = recvfrom(sockfd, buf, 64, 0, (struct sockaddr*)&caddr, &c_len); if(ret < 0) { perror("recvfrom"); exit(-1); } printf("ip -- %s, port -- %d: %s\n", inet_ntoa(caddr.sin_addr), ntohs(caddr.sin_port), buf); } //4、关闭套接字 close(sockfd); return 0; }
client.c
#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 <string.h> #include <unistd.h> #include <stdlib.h> int main(int argc, char *argv[]) { int sockfd = socket(AF_INET, SOCK_DGRAM, 0); if(sockfd < 0) { perror("socket"); exit(-1); } struct ip_mreq ip_group; ip_group.imr_multiaddr.s_addr = inet_addr("239.10.0.1"); ip_group.imr_interface.s_addr = inet_addr("192.168.7.216"); int g_len = sizeof(ip_group); if(setsockopt(sockfd, IPPROTO_IP, IP_MULTICAST_IF, &ip_group, g_len) < 0) { perror("setsockopt IP_MULTICAST_IF"); exit(-1); } struct sockaddr_in saddr; saddr.sin_family = AF_INET; saddr.sin_port = htons(8888); saddr.sin_addr.s_addr = inet_addr("239.10.0.1"); int s_len = sizeof(saddr); char buf[64] = {0}; fgets(buf, 64, stdin); int ret = sendto(sockfd, buf, strlen(buf), 0, (struct sockaddr*)&saddr, s_len); if(ret < 0) { perror("sendto"); exit(-1); } close(sockfd); return 0; }
3.域套接字
通信流程跟TCP通信一致
域套接字相关地址结构
struct sockaddr_un { sa_family_t sun_family; /* AF_UNIX */ char sun_path[108]; /* Pathname */ };
代码:
服务器
#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 <string.h> #include <unistd.h> #include <stdlib.h> #include <sys/un.h> int main(int argc, char *argv[]) { int sockfd = socket(AF_UNIX, SOCK_STREAM, 0); if(sockfd < 0) { perror("socket"); exit(-1); } struct sockaddr_un saddr; saddr.sun_family = AF_UNIX; strcpy(saddr.sun_path, "mysocket"); int s_len = sizeof(saddr); int ret = bind(sockfd, (struct sockaddr*)&saddr, s_len); if(ret < 0) { perror("bind"); exit(-1); } ret = listen(sockfd, 6); if(ret < 0) { perror("listen"); exit(-1); } int connfd = accept(sockfd, NULL, NULL); if(connfd < 0) { perror("accept"); exit(-1); } char buf[64] = {0}; while(1) { memset(buf, 0 , 64); read(connfd, buf, 64); printf("buf:%s\n", buf); } close(connfd); close(sockfd); return 0; }
客户端
#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 <string.h> #include <unistd.h> #include <stdlib.h> #include <sys/un.h> int main(int argc, char *argv[]) { int sockfd = socket(AF_UNIX, SOCK_STREAM, 0); if(sockfd < 0) { perror("socket"); exit(-1); } struct sockaddr_un saddr; saddr.sun_family = AF_UNIX; strcpy(saddr.sun_path, "mysocket"); int s_len = sizeof(saddr); int ret = connect(sockfd, (struct sockaddr*)&saddr, s_len); if(ret < 0) { perror("connect"); exit(-1); } char buf[64] = {0}; while(1) { fgets(buf, 64, stdin); write(sockfd, buf, strlen(buf)); } close(sockfd); return 0; }
11.数据库
sqlite3
数据库名字以 .db 结尾
相关的基础命令
.help:查看帮助手册 .exit:退出 .tables:查看 .quit:退出
sql语句
所有sql语句都要以 分号 结尾
创建表: create table <tablename>; 删除表: drop table <tablename>; 插入数据: insert into <tablename> values(); 删除数据: delete from <tablename> where <info>=<information>; 查看数据: select *from <tablename>; 修改数据: update <tablename> set <info>=<information_new> where <info>=<information_old>;