网络编程汇总一下

文章介绍了网络编程的基础知识,包括IP地址的分类及其作用,子网掩码的功能,以及TCP/IP模型的各层协议。重点讲解了TCP与UDP协议的区别,如TCP的三次握手和四次挥手过程,以及TCP的可靠传输特性。此外,还提到了套接字编程的相关函数,如sendto和recvfrom,以及不同的I/O模型,如阻塞I/O和非阻塞I/O。最后,简述了数据库的基本概念和SQLite的特性。
摘要由CSDN通过智能技术生成

网络编程

IP地址

  1. 在网络中,标识通信的唯一标识;
  2. 用于标识通信节点,同时具有IP报文寻址功能。
    子网掩码
  3. 用于区分IP地址中的网络部分和主机部分;
  4. 用于对网络的进一步细分,实现网络地址的浪费减小。
    IP地址分类
1)	A类地址的范围:1.0.0.0~127.255.255.255
2)	B类地址的范围:128.0.0.0~191.255.255.255
3)	C类地址的范围:192.0.0.0~223.255.255.255
4)	D类地址的范围:224.0.0.0~239.255.255.255
5)	E类地址的范围:240.0.0.0~255.255.255.255

在这里插入图片描述
OSI模型与TCP/IP模型
在这里插入图片描述

各层网络协议
传输层:TCP、UDP;
网络层:IP、ICMP;
应用层:SMTP、SNMP、HTTP、FTP;
数据链路层:ARP(通过IP地址查MAC地址);
数据封装传递过程
在这里插入图片描述

MTU与MSS
MTU:最大传输单元,IP头+TCP头+数据,数据链路层概念;
MSS:最大分段大小,数据,传输层概念;
在这里插入图片描述

TCP与UDP协议
共同点:同为传输层协议
不同点:TCP:有连接,可靠,保证可靠性的传输层协议。
UDP:无连接,不保证可靠
TCP三次握手
在这里插入图片描述

客户端与服务器端建立连接的过程。
TCP四次挥手
在这里插入图片描述

若是非三次握手就建立连接,会出现连接状态不匹配的问题。
在这里插入图片描述

TCP是可靠的传输协议
TCP会对收到的数据回响应,如果不回响应,代表数据丢失或者出错,发送端超时后重发消息。
在这里插入图片描述

TCP滑动窗口机制
在这里插入图片描述

Socket套接字
SOCK_STREAM:唯一对应TCP的套接字;
SOCK_DGRAM:唯一对应UDP的套接字;
在这里插入图片描述
TCP函数
在这里插入图片描述在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
TCP服务器

在这里插入图片描述
TCP客户端
在这里插入图片描述

IP地址
IP地址用于标识一台主机。
inet_aton()
功能:将strptr所指的字符串转换成32位的网络字节序二进制值
头文件:#include <arpa/inet.h>
int inet_aton(const char *strptr,struct in_addr *addrptr);
参数1:点分十进制的IP地址字符串
参数2:IP地址结构体指针,结构体内存放4字节的IP地址整数

inet_addr()
功能:将点分十进制的IP地址字符串转成4字节的整数,返回转换后的地址。
in_addr_t inet_addr(const char *strptr);
参数1:点分十进制的IP地址字符串

inet_ntoa()
功能:将32位网络字节序二进制地址转换成点分十进制的字符串。
char *inet_ntoa(stuct in_addr inaddr);
参数1:IP地址结构体,结构体内存放4字节的IP地址整数
端口号
用于区分同一主机上的不同进程。
建议使用端口为:5001~65535;
字节序
主机字节序与网络字节序不一定相同,因此在传输数据时,需要注意主机字节序转换为网络字节序后发送。
在这里插入图片描述

UDP协议
UDP是无连接的,不保证可靠性的传输层协议。
UDP的使用场景:

  1. 发送小尺寸数据(如对DNS服务器进行IP地址查询时)
  2. 适合于广播/组播式通信中。
  3. 对可靠性要求不高,对实时性要求较高的应用,比如视频聊天,语音通话。

UDP为什么不保证可靠性:

  1. UDP没有响应确认机制,数据报丢失后,发送端无法知道,不会重发
  2. UDP的Checksum是可以关闭的

UDP只有接收缓冲区,没有发送缓冲区,发送不会阻塞。
UDP服务器
在这里插入图片描述
在这里插入图片描述

ssize_t sendto(int socket, void *message, size_t length, int flags, struct sockaddr *dest_addr, socklen_t dest_len);

功能:发送消息
返回值:成功返回发送的字节数,失败返回-1
参数1:套接字文件描述符
参数2:消息缓冲区地址
参数3:发送消息的长度
参数4:发送方式,一般填0
参数5:发送目的地的地址和端口
参数6:参数5结构体的长度

ssize_t recvfrom(int socket, void *buffer, size_t length, int flags, struct sockaddr *address, socklen_t *address_len);

功能:接收消息
返回值:成功返回接收的字节数,失败返回-1
参数1:套接字文件描述符
参数2:接收缓冲区地址
参数3:接收缓冲区的大小
参数4:接收方式,一般填0,0代表阻塞接收。
参数5:出参,对端的地址和端口
参数6:出参,参数5结构体的长度
IO模型
阻塞I/O
缺省情况下,套接字建立后所处于的模式就是阻塞I/O 模式。
读阻塞:进程调用read函数从套接字上读取数据,当套接字的接收缓冲区中还没有数据可读,函数read将发生阻塞。
写阻塞:主要发生在要写入的缓冲区的大小小于要写入的数据量的情况下。
UDP不用等待确认,没有实际的发送缓冲区,所以UDP协议中不存在发送缓冲区满的情况,在UDP套接字上执行的写操作永远都不会阻塞。
优点:简单,常用。
缺点:在没有数据时,进程会无限制的阻塞。
非阻塞I/O

读阻塞:当一个应用程序使用了非阻塞模式的套接字,它需要使用一个循环来不停地测试是否一个文件描述符有数据可读(称做polling)。这将是一个极浪费CPU 资源的操作。这种模式使用中不普遍。

while (1) {
	ret = recv();
if (ret < 0 && errno == EAGAIN) {
	// 没有数据可读
				continue;
}

// 读到数据了,处理数据
}
写阻塞:当TCP的发送缓冲区剩余空间不足时,写函数会返回失败。
优点:在没有数据时,进程不会阻塞。
缺点:消耗CPU,不常用。
在这里插入图片描述

I/O 多路复用
Linux一个进程默认能打开1024个文件,对应1024个文件描述符。
文件描述符的特点:
文件描述符是非负整数。
文件描述符一般是从小到大分配的,0、1、2已经被系统占用。

IO多路复用的实现方式:
select:最多监控1024个文件描述符。
poll
epoll

int select(int n, fd_set *read_fds, fd_set *write_fds, fd_set *except_fds, struct timeval *timeout);

功能:监控多个文件描述符,如果一个或者多个文件描述符上有数据发生,函数就会返回有数据发生的文件描述符集合。
返回值:
大于0:有数据发生的文件描述符的数量
等于0:如果timeout参数设置了超时时间,代表超时
小于0:出错
在这里插入图片描述
select不是只针对套接字文件描述符,也可以监控普通文件描述符。
在这里插入图片描述

select IO多路复用的思想分为3步:
① 创建一个文件描述符集合,文件描述符集合里有1024个bit位,每个bit位对应一个文件描述符。将关注的文件描述符加入到文件描述符集合里,加入文件描述符集合的文件描述符,其对应的bit位置1,其余的文件描述符对应的bit位置0。
在这里插入图片描述

② 调用select监控文件描述符集合,当关注的文件描述符对应的IO通道上有数据发生时,将文件描述符对应的bit位置1,其余bit位置0,返回有数据发生的文件描述符的数量。如果关注的文件描述符对应的IO通道上都没有数据发生,select就会阻塞不返回。
在这里插入图片描述

③ 循环遍历文件描述符集合,用FD_ISSET判断关注的文件描述符上是否有数据发生,如果有数据发生FD_ISSET会返回1,处理有数据发生的文件描述符IO通道上的数据。
信号驱动I/O

void sig_handler()
{
	ret = recv();
    // 处理消息
}
signal(SIGIO, sig_handler);

循环服务器
TCP循环服务器
TCP循环服务器接受一个客户端的连接后开始处理,完成了客户的所有请求后断开连接。TCP循环服务器一次只能处理一个客户端的请求。只有在当前客户的所有请求都完成后,服务器才能处理下一个客户的连接/服务请求。如果某个客户端一直占用服务器资源,那么其它的客户端都不能被处理。TCP服务器一般很少采用循环服务器模型。

listenFd = socket(...); 
bind(...); 
listen(...); 
while(1) { 
    connFd = accept(listenFd, ...); 
    while(1) { 
          recv(connFd, ...); 
          process(...); 
          send(connFd, ...); 
    } 
    close(...); 
}

UPD循环服务器
UDP循环服务器每次从套接字上读取一个客户端的请求,处理后将结果返回给客户机。
如果UDP循环服务器在处理某个客户端的消息时耗时较长,则不适合使用UDP循环服务器,其他场景可以使用UDP循环服务器。

sockFd = socket(...);
bind(...);
while(1) { 
    recvfrom(sockFd, ...); 
    process(...); 
    sendto(sockFd, ...); 
}

并发服务器
TCP并发服务器
TCP并发服务器的设计思想是服务器接受客户端的连接请求后创建子进程或者子线程来为客户端服务。
为了响应客户机的请求,服务器要创建子进程或者子线程来处理。 如果有多个客户端的话,服务器端需要创建多个子进程或者子线程。过多的子进程或者子线程会影响服务器端的运行效率。

  1. 多线程并发服务器
  2. 多进程并发服务器
    UDP并发服务器
    UDP并发服务器模型TCP服务器模型一样,创建一个子进程来处理客户端的请求。
    除非UDP服务器在处理某个客户端的请求时所用的时间比较长,人们实际上较少用这种模型。
    IO多路复用服务器
    由于服务器是依次处理客户的请求,所以可能会导致有的客户等待时间过长。如果处理某个客户端消息的时间过长,则不建议使用IO多路复用服务器。
    网络超时检测
  3. 设置socket的属性 SO_RCVTIMEO
    struct timeval {
        time_t      tv_sec;  /* 单位秒 */
        suseconds_t tv_usec;  /* 单位微秒 */
};
struct timeval tv;
tv.tv_sec = 5;   // 设置5秒时间
tv.tv_usec = 0;
setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));  //  设置接收超时
recv() / recvfrom()          // 从socket读取数据
 

在这里插入图片描述

  1. 用select检测socket是否’ready’
struct fd_set rdfs;
struct timeval tv = {5 , 0};   // 设置5秒时间
FD_ZERO(&rdfs);
FD_SET(sockfd, &rdfs);
if (select(sockfd+1, &rdfs, NULL, NULL, &tv) > 0)   // socket就绪
{
    recv() / recvfrom()    //  从socket读取数据
}
  1. 设置定时器(timer), 捕捉SIGALRM信号
unsigned int alarm(unsigned int seconds);
alarm是一个闹钟函数,在参数指定的时间到达后会产生一个SIGALRM信号。

int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);

参数1:信号
参数2:设置新的信号的处理方式
参数3:获取原来老的信号处理方式
如果给信号设置了SA_RESTART标志,慢系统调用(比如recv/recvfrom)被该信号打断时,慢信号调用直接重启,不会返回。
如果给信号清除了SA_RESTART标志,慢系统调用(比如recv/recvfrom)被该信号打断时,慢信号调用会返回错误,errno是EINTR。

void handler(int signo)     {return;}
struct sigaction act;
sigaction(SIGALRM, NULL, &act); // 获取原来老的信号处理方式
act.sa_handler = handler;       // 设置信号处理函数
act.sa_flags &= ~SA_RESTART;  // 清除SA_RESTART标志位
sigaction(SIGALRM, &act, NULL); // 设置新的信号处理方式
alarm(5);

广播
如果同时发给局域网中的所有主机,称为广播。
只有用户数据报(使用UDP协议)套接字才能广播。
广播地址:
以192.168.1.0 (255.255.255.0) 网段为例,最大的主机地址192.168.1.255代表该网段的广播地址。
发到该地址的数据包被所有的主机接收。
255.255.255.255在所有网段中都代表广播地址。
组播
广播方式发给所有的主机。过多的广播会大量占用网络带宽,造成广播风暴,影响正常的通信。
组播(又称为多播)是一种折中的方式。只有加入某个多播组的主机才能收到数据。
多播方式既可以发给多个主机,又能避免象广播那样带来过多的负载(每台主机要到传输层才能判断广播包是否要处理)
D类地址(组播地址):
不分网络地址和主机地址,第1字节的前4位固定为1110
224.0.0.1 – 239.255.255.255
UNIX域套接字
UNIX域套接字用于一台主机上的进程间通信,其通信效率仅次于共享内存。共享内存通常需要配合信号量一起使用,用信号量做共享资源的同步和互斥,所以UNIX域套接字比共享内存使用起来更方便。
在这里插入图片描述

创建套接字时使用本地协议PF_UNIX(或PF_LOCAL)。
分为流式套接字和用户数据报套接字。

数据库

基本概念
数据(Data):能够输入计算机并能被计算机程序识别和处理的信息集合。
数据库(Database):数据库是在数据库管理系统管理和控制之下,存放在存储介质上的数据集合。
在这里插入图片描述

文件管理
优点:
(ⅰ)数据可长期保存
(ⅱ)能存储大量数据

缺点:
(ⅰ)数据冗余度(redundancy)大,数据一致性(consistency)、完整性(integrity)难以维持。
(ⅱ)数据与程序缺乏高度独立性。
数据库管理系统
(ⅰ)数据组织结构化。
(ⅱ)数据冗余度比较小,易扩充。
(ⅲ)具有较高的数据与程序之间的独立性。
(ⅳ)统一的数据控制。
SQLite基础
SQLite的源代码是C,其源代码完全开放。SQLite第一个Alpha版本诞生于2000年5月。他是一个轻量级的嵌入式数据库。
SQLite有以下特性:
零配置一无需安装和管理配置;
储存在单一磁盘文件中的一个完整的数据库;
数据库文件可以在不同字节顺序的机器间自由共享;
支持数据库大小至2TB;
足够小,全部源码大致3万行c代码,250KB;
比目前流行的大多数数据库对数据的操作要快;
安装SQLite3
sudo apt-get install sqlite3
sudo apt-get install libsqlite3-dev
SQLite3的系统命令
SQLite3的系统命令是以点开头的,又叫点命令。
打开数据库,数据库不存在,则创建数据库
sqlite3 test.db
查询数据库文件存放位置
.database
查询数据库里的所有表名称
.tables
查看表的结构
.schema
退出数据库管理系统
.quit
.exit
约束
NOT NULL:确保某列bunengyou NULL值
DEFAULT:当某列没有指定值时,有默认值
UNIQUE:某列中所有制是不同的
PRIMARY Key:唯一标识数据库表中各行/记录
CHECK:确保某列中所有值满足一定条件
SQL语句
a) 创建表

create table 表名(字段1 类型 约束, 字段2 类型 约束,);
create table stu(id integer primary key not null, name text not null, age integer);

b) 删除表

drop table 表名;
drop table stu;

c) 向表中添加新记录

insert into 表名 values(字段1的值, 字段2的值,);   // 指定所有字段的值
insert into stu values(1001, 'zhangsan', 20);

insert into 表名(字段1, 字段2,) values(字段1的值, 字段2的值,);  // 指定部分字段的值
insert into stu(id, name) values(1002, 'lisi');

d) 查询表中的记录

select * from 表名;
select * from stu;

select 字段名… from 表名;
select id, name from stu;

select * from 表名 where 查询条件;
select * from stu where name='zhangsan';
select * from stu where name='zhangsan' and id=1001;

e) 更新表中记录

update 表名 set 字段m=字段m的新值,字段n=字段n的新值,… where 查询条件;
update stu set age=21 where name='lisi';

f) 按指定条件删除表中记录

delete from 表名 where 查询条件;
delete from stu where name='lisi';

g) 在表中添加字段

alter table 表名 add column 字段m 类型 约束;
alter table stu add column score integer;

h) 在表中删除字段

SQLite中不允许删除字段,可以通过下面步骤达到同样的效果
create table 临时表名 as select 字段m, 字段n, … from 原表名;
drop table 原表名;
alter table 临时表名 rename to 原表名;

9)sqlite3编程接口

int sqlite3_open(char *path, sqlite3 **db);
功能:打开sqlite数据库,数据库文件存在则打开,数据库文件不存在则创建数据库文件。
参数1:数据库文件路径。
参数2:出参,指向sqlite句柄的指针。
返回值:成功返回0,失败返回错误码。

int sqlite3_close(sqlite3 *db);
功能:关闭sqlite数据库
参数1:数据库的句柄
返回值:成功返回0,失败返回错误码

const char *sqlite3_errmsg(sqlite3 *db);
功能:获取数据库错误信息
参数:数据库的句柄
返回值:返回错误信息

编译链接:
gcc -o test test.c -lsqlite3

int sqlite3_exec(sqlite3 *db, const char *sql, sqlite3_callback callback, void *, char **errmsg);

功能:执行SQL操作
参数1:数据库句柄
参数2:SQL语句
参数3:回调函数,用于返回SQL语句的查询结果
参数4:传递给回调函数的参数
参数5:出参,错误信息指针的地址
返回值:成功返回0,失败返回错误码

typedef int (*sqlite3_callback)(void *para, int f_num, char **f_value, char **f_name);

功能:每找到一条记录自动执行一次回调函数
参数1:传递给回调函数的参数
参数2:记录中包含的字段数目
参数3:包含每个字段值的指针数组
参数4:包含每个字段名称的指针数组
返回值:成功返回0,失败返回-1

int sqlite3_get_table(sqlite3 *db, const char *sql, char ***resultp, int *nrow, int *ncolumn, char **errmsg);

功能:执行SQL操作
参数1:数据库句柄
参数2:SQL语句
参数3:出参,用来指向sql执行结果的指针
参数4:出参,满足条件的记录的数目
参数5:出参,每条记录包含的字段数目
参数6:出参,错误信息指针的地址
返回值:成功返回0,失败返回错误码

void sqlite3_free_table(char **result);

功能:释放用sqlite3_get_table获取的查询结果。
参数1:SQL语句查询结果

void sqlite3_free(void *errmsg);

功能:释放错误信息
参数1:错误信息指针

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值