网络编程知识点总结

物理链路网络运输会话表示应用

物链网运会表应

实际的数据帧

TCP和UDP的异同(笔试面试)

主机:host

转换:to

网络:network

uint32_t htonl(uint32_t hostlong); //将4字节无符号整数的主机字节序转换为网络字节序,参数是主机字节序,返回值是网络字节序

uint16_t htons(uint16_t hostshort); //将2字节无符号整数的主机字节序转换为网络字节序,参数是主机字节序,返回值是网络字节序

uint32_t ntohl(uint32_t netlong); //将4字节无符号整数的网络字节序转换为主机字节序,参数是网络字节序,返回值是主机字节序

uint16_t ntohs(uint16_t netshort); //将2字节无符号整数的网络字节序转换为主机字节序,参数是网络字节序,返回值是主机字节序

IP地址类型

A类地址

1.0.0.0~127.255.255.255

2^7(网络号)

2^24(主机号)

已经保留不在供给

B类地址

128.0.0.0~191.255.255.255

2^14

2^16

名地址网管中心

C类地址

192.0.0.0~223.255.255.255

2^21

2^8

校园网或企业网、家庭网

D类地址

224.0.0.0~239.255.255.255

组播地址

E类地址

240.0.0.0~255.255.255.255

保留

1-128-192-224-240-255

A        B        C        D        E

子网掩码的使用:IP地址 & 子网掩码 --->子网网段

为了区分同一主机上的多个进程,使用端口号来进行处理

端口号是一个2字节的无符号整数存储,取值范围【0,65535】

网络通信中两个决定性因素:IP + 端口号

TCP和UDP的端口号是相互独立的

可以使用的:1024~49151,就是我们平时编写服务器使用的端口号

临时端口号:49152~65535,这部分是客户端运行时候动态选择的

TCP协议:

struct sockaddr_in sin;       
sin.sin_family = AF_INET;       //通信域
sin.sin_port = htons(SER_PORT);    //端口号
sin.sin_addr.s_addr = inet_addr(SER_IP);    //ip地址

int socket([ipv4/v6],[TCPUDP], 0);

int bind([套接字名],[地址信息] , sizeof([地址信息结构体]));

int listen([套接字名],[长度一般填128]);

int accept([套接字名], [地址信息接收指针] , [地址信息的长度]);

ssize_t recv([套接字名], void *buf, sizeof (buf), [阻塞或非阻塞]);

ssize_t send([新套接字名], void *buf, sizeof (buf), [阻塞或非阻塞]);

int connect([套接字名] ,[对端地址信息结构体], sizeof([对端地址信息结构体]));

服务器端模型

客户端模型:


1.cfd = socket(AF_INET, SOCK_STREAM, 0);

//2.1 填充地址信息结构体
    struct sockaddr_in cin;       
    cin.sin_family = AF_INET;       //通信域
    cin.sin_port = htons(CLI_PORT);    //端口号
    cin.sin_addr.s_addr = inet_addr(CLI_IP);    //ip地址

2.bind(cfd, (struct sockaddr*)&cin, sizeof(cin))

//3.1 填充服务器地址信息结构体
    struct sockaddr_in sin;
    sin.sin_family = AF_INET;          //通信域
    sin.sin_port = htons(SER_PORT);      //服务器端口号
    sin.sin_addr.s_addr = inet_addr(SER_IP);     //服务器ip地址

3.connect(cfd, (struct sockaddr*)&sin, sizeof(sin) //将套接字文件描述符连接到addr指向的地址空间中

while(1)

{

4.send(cfd, buf, strlen(buf), 0);

5.recv(cfd, buf, sizeof(buf), 0);

}

6.close();

UDP协议:

ssize_t recvfrom([套接字名], buf[起始地址], sizeof(buf), [阻塞], [对端地址信息结构体],

[对端地址信息结构体大小]);

ssize_t sendto([套接字名], buf[起始地址], sizeof(buf), [阻塞], [对端地址信息结构体],

[对端地址信息结构体大小]);

服务器端模型:

1.sfd = socket(AF_INET, SOCK_DGRAM, 0)

//2.1 填充地址信息结构体
    struct sockaddr_in sin;       
    sin.sin_family = AF_INET;       //通信域
    sin.sin_port = htons(SER_PORT);    //端口号
    sin.sin_addr.s_addr = inet_addr(SER_IP);    //ip地址

2.bind(sfd, (struct sockaddr*)&sin, sizeof(sin)

while(1)

{

3.recvfrom(sfd, buf, sizeof(buf), 0, (struct sockaddr*)&cin, &addrlen);

4.sendto(sfd, buf, strlen(buf), 0, (struct sockaddr*)&cin, sizeof(cin))
}

5.close(sfd);

客户端模型:

1.cfd = socket(AF_INET, SOCK_DGRAM, 0);

//2.1 填充地址信息结构体
    struct sockaddr_in cin;       
    cin.sin_family = AF_INET;       //通信域
    cin.sin_port = htons(CLI_PORT);    //端口号
    cin.sin_addr.s_addr = inet_addr(CLI_IP);    //ip地址

2.bind(cfd, (struct sockaddr*)&cin, sizeof(cin)

//填充服务器的地址信息结构体
    struct sockaddr_in sin;            //接受对端地址信息
    sin.sin_family = AF_INET;      //服务器的通信域
    sin.sin_port = htons(SER_PORT);    //服务器端口号
    sin.sin_addr.s_addr = inet_addr(SER_IP);     //服务器ip地址

while (1)

{
3.//将数据发送给服务器

sendto(cfd, buf, strlen(buf), 0, (struct sockaddr*)&sin, sizeof(sin));

4.//接受服务器发来的消息

recvfrom(cfd, buf, sizeof(buf), 0, NULL, NULL);

}

5.close(cfd);

TCP和UDP基础通信模型注意事项

1> 无论是TCP还是UDP通信中,服务器端必须要绑定ip地址和端口号,以便于让客户端找到该服务器。对于客户端而言,可以绑定也可以不绑定,如果绑定了,则使用自己绑定的ip和端口号。如果没有绑定,则系统会自动绑定一个当前的IP地址和一个随机的端口号供客户端使用。

2> 对于TCP通信而言,可以使用recv和send进行通信,也可以使用read、write进行通信,还可以使用sendto和recvfrom进行通信,都没有问题。

3> 对于UDP通信而言,如果当前端只是用于接收数据,不发送数据,可以使用recvfrom、recv、read进行接收;如果当前端接收数据后还要发送数据给对端,则需要使用recvfrom进行接收数据,顺便将对端地址信息结构体接收过来。

4> UDP中是否可以使用connect函数呢?

答,可以使用。当UDP服务器端使用connect会跟指定的客户端建立一个唯一的通道。其他客户端就不能再进行通信了。如果想要断开连接,需要再次使用connect函数,并且将地址信息结构体中的family设置成AF_UNSPEC.

好处:1、能够提供数据传输效率

例如:A和B同时向服务器发送消息,但是A发送的消息较大,需要较长的时间,发送过程中可能会出现时间片用完,此时B向服务器发送消息,导致,服务器处理消息时,消息混乱。这时就可以先单独跟A建立连接,等所有数据传输结束后,再跟B通信

2、传输性能高

一般的UDP通信:获取对端地址信息 --> 将信息加载到内核 ---> 数据收发 --->获取对端地址信息 --> 将信息加载到内核 ---> 数据收发 --->获取对端地址信息 --> 将信息加载到内核 ---> 数据收发 --->....

UDP建立连接后:获取对端地址信息 --> 将信息加载到内核 ---> 数据收发 --->数据收发 --->数据收发 --->数据收发 --->数据收发 --->数据收发 --->

广播:广播地址:网络号 + 255

广播的发送端流程 ---> UDP

1、socket(AF_INET, SOCK_DGRAM, 0); //创建用于通信的套接字文件描述符

2、bind(rfd, (struct sockaddr*)&rin, sizeof(rin)); //可选,可以绑定也可以不绑定

3、setsockopt(sfd, SOL_SOCKET, SO_BROADCAST, &broad, sizeof(broad);

//设置当前套接字允许广播

4、sendto(sfd, wbuf, strlen(wbuf), 0, (struct sockaddr*)&bin, sizeof(bin));

 //向广播地址发送消息

5、close(); //关闭套接字

广播的接收端流程

1、socket(); //创建用于通信的套接字文件描述符

2、bind(); //必须进行,但是,绑定的广播地址和端口号

3、sendto(); //向广播地址发送消息

4、close(); //关闭套接字

组播:使用D类地址

D类地址

224.0.0.0~239.255.255.255

组播地址

组播可以实现小范围的数据传播:将需要接收数据的接收端加入多播组,发送端向多播组中发送消息,每个组内成员都能接收到消息

需要对接收端进行设置,将接收端加入多播组

模型和广播相同,setsockopt改为:

//准备要设置的数据
    struct ip_mreqn imr;        
    imr.imr_multiaddr.s_addr = inet_addr("224.1.2.3");   //组播ip
    imr.imr_address.s_addr = inet_addr("192.168.0.143");   //主机ip
    imr.imr_ifindex = 2;                        //网卡编号


    if(setsockopt(rfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &imr, sizeof(imr)) == -1)

三向握手四次挥手

  1. 第一次握手:客户端发送SYN包(SYN=1, seq=0)给服务器,并进入SYN_SENT状态,等待服务器返回确认包。
  2. 第二次握手:服务器接收到SYN包,确认客户端的SYN,发送ACK包(ACK=1 , ack=1),同时发送一个SYN包(SYN=1, seq=0),并进入SYN_RCVD状态。
  3. 第三次握手:客户端接收到服务器的SYN包,以及ACK包,进入establish状态,同时向服务器发送ACK包(ACK=1, ack=1)。此时三次握手包发送完毕,服务器也进入establish状态

  1. 第一次挥手,主动关闭方发送一个FIN包(FIN=1, seq = u)给被动方,进入FIN_WAIT_1状态;
  2. 第二次挥手:被动方接收到FIN包,给主动方发送一个ACK包(ACK=1, ack=u+1);并进入CLOKSE_WAIT状态。主动方接受到ACK包后,进入FIN_WAIT_2状态。如果有数据没有发送完毕,则继续发送,直到发送完毕为止;
  3. 第三次挥手:被动方发送一个FIN包(FIN=1, seq=w),进入LAST_ACK状态.
  4. 第四次挥手:主动关闭方收到FIN包,回复一个ACK包(ACK=1, ack=w+1)。被动关闭方收到主动关闭方的ACK后关闭连接。

linux系统中的库

静态库:
就是将xxx.c源文件直接编译成 libxxx.a文件

静态体现在:静态库在跟主程序一起生成可执行程序时,会将静态库完整的放入可执行程序中,每个可执行程序中都独立拥有整个静态库。函数调用时,效率比较高,但是,可执行程序的体积比较大。

静态库制作方法:
编译生成静态库

gcc -c XXX.c -o XXX.o        //只编译不链接生成二进制文件
2、ar -crs libXXX.a XXX.o    //将XXX.o的二进制文件编译生成libXXX.a的静态库
一个静态库依赖多个.o文件:ar -crs libXXX.a XXX.o  AAA.o  BBB.o   //将XXX.o的二进制文件编译生成libXXX.a的静态库
其中ar :指令表示要编译生成静态库

-c:create,表示创建静态库

-r:replace,表示如果XXX.o文件已经存在,则被新的文件替换

-s:重置静态库索引

动态库:
动态库就是将一个XXX.c的源文件,编译生成一个libXXX.so的二进制文件。

动态体现在:动态库在跟主程序一起生成可执行程序时,只是将相关函数的入口地址列表编译到可执行程序中,当执行到对应的函数调用时,会根据该函数的入口地址,找到对应的动态库,执行该库中的函数。不会将整个库封装到可执行程序中。多个可执行程序可以连接同一个动态库,所以动态库也称为共享库。动态库的体积较小,但是,执行效率没有静态库高。

动态库的制作:

1、gcc -fPIC -c XXX.c -o XXX.o     //只编译不链接生成二进制文件, -fPIC表示忽略文件位置进行编译
2、gcc -shared XXX.o -o libXXX.so  //编译生成动态库
    多个程序共同生成动态库:gcc -shared XXX.o AAA.o BBB.o -o libXXX.so
 
s也可以将上面的两个操作合成一个
    gcc -fPIC -shared XXX.c -o libXXX.so
动态库的使用

gcc main.c -L 库的路径 -I 头文件路径 -l库名 -o 可执行程序

数据库sqlite3的使用

sqlite数据库创建

sqlite3 sq.db

如果sq.db存在则直接打开sq.db数据库,如果不存在则先创建再打开;

另外也可以使用.open 创建数据库

sqlite>.open test.db

系统命令,需要以 . 开头,不需要以 ; 结尾
.quit   退出数据库
.exit   退出数据库
.help   显示帮助信息,获取所有系统命令;
​
.table  查看当前数据库下的所有表格;
.schema 查看表的结构

create table 表名 (字段名 数据类型, 字段名 数据类型);

添加数据:

insert into 表单名(字段1,字段2,....,字段n) values(数据1,数据2,...,数据 n)

查看表单中数据:

select 字段1,字段2,...,字段n from 表单名

查看指定表单中的指定字段的所有数据:

select * from 表单名

查看指定表单中的所有字段的所有数据:

insert into 表单1(字段1,字段2,....,字段n) select 字段1,字段2,...,字段n from 表单2

先查询出表单2中的所有指定字段的数据,再将这些数据,对应的添加到表单1中的每一个字段中去

where子句:

只要有任何附加的查询条件的时候,在英语语法应该写条件的地方写上 where 条件

比如说,想要查看 姓名为"张三"的所有信息 select * from stu where 姓名="张三"

修改表单中指定数据:

update 表单名 set 字段名=新数据 where条件定位 例如:将姓名为"张三"的成绩,改成50分 update stu set 成绩=50 where 姓名="张三"

。。。详见sqlite的文章

使用方法:
     1、先获取当前文件描述符的属性
         int flag = fcntl(fd, F_GETFL);
     2、将当前的文件描述符的属性中,添加非阻塞属性
         flag |= O_NONBLOCK;                                                         
     3、将更改后的状态加到当前文件描述符中
          fcntl(fd, F_SETFL, flag);  

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值