linux基础:网络编程
一.基础了解
(为什么要学习这门技术,用于面试)
1:套接字实现的是两台不同主机之间的通信(也是进程间通信)
- 网络的发展史
2.1阿帕网----早期的通信协议
不能互联不同类型的计算机和不同类型的通信系统,没有纠错功能
2.2TCP/IP协议.
TCP----用来检测网络传输中差错的传输控制协议
IP------专门负责对不同网络进行互联的互联网协议
2.1体系架构
TCP/IP协议族
应用层---http(超文本协议)/ftp/Telnet等协议-----用户空间
传输层--TCP/UDP协议
网络层----IP(重点)/655TCMP/TGMP协议
网络接口(物理和数据链路)层---以太网协议 数据桢传输(ARP RARP)(二进制流转换为数据帧,并实现数据桢的发生与接收)
内核空间:传输/网络/网络接口层
各协议:
TCP--传输控制协议(数据流传输),可靠性好,实时性不高
IP----网间协议
UDP--用户数据报协议,可靠性不高,实时性好
TCMP--控制报文协议
ARP:地址解析协议----IP转为物理地址
域名解析(DNS):将域名指向对应的IP地址
2.1.1数据传输协议封装过程由内核实现
网络编程只需要用内核提供的接口
2.1.2数据打包与解包过程
---内核与路由完成
2.2IP地址
2.2.1分类
IP地址的点分十进制:xxx.xxx.xxx.xxx(.代表一个字节结束)---一个4个字节32位
IP地址的点分十进制---〉二进制网络字节序:inet_addr(“192.168.5.7”)
Inet_aton:()
A类
第一个字节以0开头(01111111--127(2^7-1))
范围(1.0.0.0-126.255.255.255)--0.0.0.0d代表任意IP与任何地址都可以匹配用户不能私用,127.0.0.0--127.255.255.255本地测试地址(不能跨主机通信,可用于本机网络测试)
B类
第一个字节以10开头(10111111==2^8-1-2^6==191)
范围:128.0.0.0-191.255.255.255
C类
第一个字节以110开头
范围:192.0.0.0--223.255.255.255
D(1110开头224.0.0-239.255.255)/E(11110开头240.0.0.0-255.255.255)类
广播与主播地址
子网掩码
为了区分网络号与主机号
1) A类地址
子网掩码:255.0.0.0
A类地址第一个字节为网络号((126-1+1)*256*256个),后三个字节为主机号(2^24-2个,124.0.0.0网关地址124.255.255.255广播地址---不能用)
2)B类地址
子网掩码:255.255.0.0
B类地址前两个字节为网络号((191-128+1(本机地址))*256*256个),后两个字节为主机号(2^16-2,首尾不用)
3)C类地址
子网掩码:255.255.255.0
C类地址前三个字节为网络号((223-192+1)*256*256),后一个字节为主机号(2^8-2,首尾不用)
某一网络子网掩码为255.255.255.248,掩码位为0的,对应IP就是主机位,可以分配,248的二进制是1111 1000,因为有三个0,所以主机所占位也是三个,2^3=8,所以子网IP共8个。
去掉首尾两个不可分配,所以最多接主机是6台。
在实际应用中,因为必须有一个IP做网关,所以实际上是5个IP可用。
全双工,简单点就是说,比如你的连接速率是100Mb的,那么全双工状态下,你的上行和下行速率同时可以达到100Mb。半双工则不行
2.2.2IP地址作用
标识主机和目标地址(IP地址头包含两个内容--源地址IP+目标地址IP)
2.2.3:IP地址转换
2.3端口号
2.3.1作用
标识进程(linux系统通过端口决定哪一个进程来服务)
2.3.2:范围
1-1023:供系统使用
程序可使用已登记端口与动态或私有端口:1024-49151 49152-65535
端口分配时是系统随机提取的没有被使用的端口
2.4字节序
数据的最小存储单位--一个字节
2.4.1主机字节序
如果是Intel的芯片。采用1386架构,字节序为小端序//arm大小端序都可以
小端序:低地址存放低字节,输出是倒叙输出
主机序转网络字节序:htons();htonl()
网络到主机:ntohs();ntohl();
例子:int a = ox12345678,存放的方式78 56 34 12,打印一个字节是78 ,打印两个字节是5678
例子:小端序应用
2.4.2网络字节序
基本都是大端序
大端序:低地址存放高字节;如:0x12345678,存放方式12 34 56 78,打印一个字节12,打印两个字节12 34
1. 套接字(socket)
2.1意义/定义
意义/作用:
是一个网络编程接口
是一种特殊文件描述符(socketfd)
并不局限于TCP/IP协议
面向连接(TCP/IP)
无连接(UDP/IPX)
定义:独立于具体协议的网络编程接口,在OSI模型中位于传输层与会话层之间
2.2为什么要使用套接字
网络协议具有多样性,网络通信在不同PC机的进程间,需要用通用的网络编程接口
2.3套接字类型
SOCK_STREAM:以数据流的方式传输,面向连接-------TCP编程
SOCK_DGRAM:以数据包方式传递,无连接,不保证数据可靠性---udp编程
SOCK_RAM:操作IP底层协议
2.4TCP/UDP
共同点:属于传输层协议
不同:TCP面向连接,保证可靠,流式套接字SOCK_STREAM,
需要三次握手---QQ等即时通信软件登陆,文件/邮件传输
UDP无连接,不可靠
高效(不需要连接,不需要三次握手)(数据报套接字SOCK_DGRAM)-----群聊天,视频浏览或直播
2.5TCP服务器搭建流程
注:头文件:<netinet/in.h>或/usr/include/netinet/in.h或man 7 ip
买手机 办卡 到 实现打电话过程
1)Socket创建套接字//返回监听套接字关闭该套接字则不能与任何客户端通信
Int sockfd = Socket(AF_INET,SOCK_STREAM,0)
2)设置服务器的地址以及端口信息
Struct sockaddr__in seraddr;
Seraddr.sa_family = AF_INET;
Seraddr.sin_port = htons(8888);//htons()将主机字节序-〉网络字节序
Seraddr.sin_addr,s_addr = inet_addr(“192.168.5.107”);//点分十进制-〉二进制
需要用到内核提供的结构体
struct sockaddr_in --提供给编程用户使用
struct sockaddr_in{
Sin_family_t sin_family;
Sin_port_t Sin_porrt;
Struct in_addr Sin_addr;
}
Struct in_addr{
Unit32_t S_addr;
}
Struct sokaddr;----通用结构体,内核使用该结构体来操作(当用户传结构体给内涵核需强转位通用结构体)
Struct sockaddr{
Sa_family_t sin_family;
Char sa_data[14];
}//内核使用该结构体操作,大小与struct sockaddr_in一样
2------绑定套接字 bind()
Bind(sockfd,(struct sockaddr *)&seraddr,sizeof(seraddr))
3-----监听套接字 listen()---------接受客户端请求/DOS
Listen(sockfd,5)//5表示可以同时监听5个客户端
4--------接受客户端请求accept()---产生通信套接字fd用于数据收发//关闭则不能与当前客户通信
Int len = sizeof(cliaddr);
Int confd = Accept(sockfd,(struct sockaddr *)&cliaddr,&len))
5----关闭套接字 close()
发送数据:send()或write()
Send(connfd,buf,sizeof(buf),0);//无数据阻塞等待
接受数据:read()或recv()
Recv(connfd,buf,sizeof(buf),0);
注:配套使用
2.6TCP客户端创建流程
1)创建套接字 socket()
Int sockfd = socket(AF_INET,SOCK_STREAM,0)
2)设置服务器的地址以及端口信息struct sokaddr_in
Struct sockaddr_in seraddr;
Seraddr.sin_family = AF_INET;
Seraddr.sin_port = htons(8888);//将主机字节序转换为网络字节序
Seraddr.sin_addr.s_addr = inet_addr(“192.168.5.107”);//将十进制转换为32位二进制
3)请求连接 connect()
int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
Connect(sockfd,(struct sockaddr*)&seraddr,sizeof(seraddr));
4)关闭套接字 close()
注:只有一端会造成管道破裂,
作业:完成服务器与客户端正常的聊天(一对一)
1〉创建两个线程:一个用于接受,一个用于发送
解释:当读写过程写在同一进城时,因为读写函数具有阻塞问题,只会执行一个任务就会阻塞另一个任务,因此需要将两个任务独立出来--可以用多线程同步运行。
代码实现:
TCP_server.c
#include <stdlio.h>
#include <string.h>
#include <pthread.h>
#include <sys/socket.h>
#include <netinet.h>
#include <pthread.h>
#define err_log(err) do{perror(err);exit(-1);}while(0)
pthread_t pid1,pid2;
void *pthread_recv(void *arg)
{
int connfd = *(int *)arg;//(int)arg需要配套使用
char buf[32]={0};
int ret = 0;
while(1){
ret = read(connfd,buf,sizeof(buf));
if(0 == ret){
printf("client closed\n");
pthread_cancel(pid2);
break;
}
printf("recv:%s\n",buf);
memset(buf,0,sizeof(buf));//这一步恒重要,记住记住
}
pthread_exit(NULL);
}
void *pthread_send(void *arg)
{
int connfd = *(int *)arg;
char buf[32] = {0};
while(1){
printf("send:");
fgets(buf,sizeof(buf),stdin);
if(write(connfd,buf,sizeof(buf)) < 0)//需要判断一下
{
err_log("write failed");
}
}
pthread_exit(NULL);
}
int main()
{
//创建套接字
int sockfd = socket(AF_INET,SOCK_STREAM,0);
if(-1 == sockfd){
err_log("socket failed");
}
//设置服务器地址及端口信息
struct sockaddr_in seraddr;
seraddr.sin_family = AF_INET;
seraddr.sin_port = htons(8888);
seraddr.sin_addr.s_addr = inet_addr("192.168.1.9");
//绑定套接字
if(bind(sockfd,(struct sockaddr*)&seraddr,sizeof(seraddr)) < 0){
err_log("bind failed");
}
printf("bind success--------\n");
//监听套接字
if(listen(sockfd,5) <0){
err_log("listen failed");
}
printf("listen success------------\n");
//接受客户端请求
int connfd = accept(sockfd,NULL,NULL);/*如果后两个参数都设置为NULL,则不获取客户端的信息*/重要
if(-1 == connfd){
err_log("accept failed");
}
//接受发送数据线程
if(pthread_create(&pid1,NULL,pthread_recv,&connfd) < 0){//(void *)connfd
err_log("pthread_create1 failed");
}
if(pthread_create(&pid2,NULL,pthread_send,&connfd) < 0){
err_log("pthread_create2 failed");
}
if(pthread_join(pid1,NULL) < 0){
err_log("pthread_join failed");
}
if(ptread_join(pid2,NULL) < 0){
err_log("pthread_join failed");
}
//关闭套接字
close(scokfd);
close(connfd);
}
TCP_cli.c
pthread_t pid,pid2;
void *pthread_send(void *arg)
{
int sockfd = *(int *)arg;
char buf[32] = {0};
while(1){
printf("send:\n");
fgets(buf,sizeof(buf),stdin);
buf[strlen(buf)-1] = '\n';
send(sockfd,buf,sizeof(buf),o);
memset(buf,0,sizeof(buf));
}
pthread_exit()NULL;
}
void *pthread_recv(void *arg)
{
int sockfd = *(int *)arg;
char buf[32] = {0};
int ret = o
while(1){
ret = recv(sockfd,buf,sizeof(buf),o);
if(0 == ret){
break;
}
memset(buf,0,sizeof(buf));
}
pthread_exit(NULL);
}
int mian()
{
int sockfd = socket(AF_INET,SOCK_STREAM,0);
if(-1 == sockfd){
err_log("socket failed");
}
struct sockaddr_in seraddr;
seraddr.sin_family = AF_INET;
seraddr.sin_port = htons(8888);
seraddr.sin_addr.s_addr = inet_addr("192.168.1.9");
if(connect(sockfd,(struct sockaddr*)&seraddr,sizeof(seraddr)) == -1){
err_log("connect failed");
}
if(pthread_create(&pid1,NULL,pthread_send,&sockfd) < 0){
err_log("pthread_create failed");
}
if(pthread_create(&pid2,NULL,pthread_recv,&sockfd) < 0){
err_log("pthread_create failed");
}
if(pthread_join(pid1,NULL) < 0){
err_log("pthread_join failed");
}
if(pthread_join(pid2,NULL) < 0){
err_log("pthread_join failed");
}
close(sockfd);
return 0;
}
作业:完成服务器与客户端的一对多聊天
即一个服务器可为多个客户端提供服务,而不会因为一个客户端的退出而退出--------循环服务器(将accept写在while(1)循环内,保证每一次有新的客户端访问都会触发客户端请求函数,建立新的连接)
2. TCP三次握手/四次挥手协议(connect()与accept()之间完成)
-----必须客户端先向服务器发送请求
Eg:客户端向服务器发送一个消息进入send,服务器受到消息并送
SYN---待确认包
ACK----发送确认信息
两次握手不会建立连接,在第二次服务器发送消息后,服务器会等待一会,没有接收到消息就不会建立连接
第一次握手:建立连接时,客户端发送SYN包((SYN=i)到服务器,并进入SYN SEND状态,等待服务器确认;
第二次握手:服务器收到SYN包,必须确认客户的SYN (ACK=i+1 ),同时自己也发送一个SYN包((SYN j)}即SYN+ACK包,此时服务器进入SYN_RECV状态;
第三次握手:客户端收到服务器的SYN十ACK包,向服务器发送确认包ACK(ACK=j+1),此包发送完毕,客户端和服务器进入ESTABLISHED状态,完成三次握手,客户端与服务器开始传送数据。
三次握手
字段 含义
URG 紧急指针是否有效。为1,表示某一位需要被优先处理
ACK 确认号是否有效,一般置为1。
PSH 提示接收端应用程序立即从TCP缓冲区把数据读走。
RST 对方要求重新建立连接,复位。
SYN 请求建立连接,并在其序列号的字段进行序列号的初始值设定。建立连接,设置为1
FIN 希望断开连接。
四次挥手
3. UDP服务器的创建
1)创建数据报套接字
Socket();SOCK_DGRAM;
2) 设置地址以及端口信息 struct sockaddr_in;
Sin_family;
Sin_port;
Sin_addr.s_addr;
3) 绑定套接字
Bind();
4)接收数据 recvfrom()接受数据的同时会接受客户端的地址信息并保存在recvfrom之后,保证接受到客户端的地址信息后再向客户端发送消息
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
struct sockaddr *src_addr, socklen_t *addrlen);
Sockfd:创建的套接字
Restrict buffer:接受数据的缓冲区首地址
Leng:接受的数据长度
Flag:默认为0(阻塞模式)
Restric address:客户端的地址信息结构体(函数内部进行设置)
Restric address_len:地址长度指针
发送数据 sento()
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
const struct sockaddr *dest_addr, socklen_t addrlen);
Sockfd:创建的套接字
Restrict buffer:接受数据的缓冲区首地址
Leng:接受的数据长度
Flag:默认为0(阻塞模式)
Restric address:客户端的地址信息结构体(函数内部进行设置)
addr_len:地址长度
4. UDP客户端的创建
1)创建数据报套接字
Socket();SOCK_DGRAM;
2)设置服务器地址以及端口信息 struct sockaddr_in;
Sin_family;
Sin_port;
Sin_addr.s_addr;
3)接收数据 recvfrom()//
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
struct sockaddr *src_addr, socklen_t *addrlen);
Sockfd:创建的套接字
Restrict buffer:接受数据的缓冲区首地址
Leng:接受的数据长度
Flag:默认为0(阻塞模式)
Restric address:服务器的地址信息结构体(函数内部进行设置)
Restric address_len:地址长度指针
发送数据 sento()//
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
const struct sockaddr *dest_addr, socklen_t addrlen);
Eg:sendto(sockfd,buf,sieof(buf),0,(strict sockaddr*)&seraddr,sizeof(seraddr))
Sockfd:创建的套接字
Restrict buffer:接受数据的缓冲区首地址
Leng:接受的数据长度
Flag:默认为0(阻塞模式)
Restric address:服务器的地址信息结构体(函数内部进行设置)
addr_len:地址长度
作业:UDP实现群聊功能/聊天室
5. TCP与UDP服务器区别
1)tcp创建一个流式套接字(SOCK_STREAM) udp创建一个数据报套接字(SOCK_DGRAM)
2)设置地址信息,struct sockaddr_inudp----一样
3)绑定套接字(Bind())udp----一样
4)tcp监听listen() udp---无
5)tcp接收客户端的请求accept() udp----无
Udp ----recvfrom()接受客户端信息,并且保存客户端地址信息
6)关闭套接字close() udp---一样
**7.I/O模型
1)阻塞i/o
-------一直等待直到有信号(一种状态)让函数执行,会让进程等待不能运行其他程序(卡进程)
Read();connect();fgets();write()具有写缓存区,当缓存区满时,会阻塞
Eg:read():当读缓存区有内容的时候就执行,当读缓存区没有内容时就是阻塞状态,直到有内容时,内核将其唤醒
Write():一般不会阻塞,只有当写入的数据大于写缓存区时才会阻塞,一旦写缓存区有空间,内核就会将其唤醒,udp没有写缓存满的情况,所以往udp套接字里面写数据不会阻塞。因为udp不用等待确认,没有实际的发送缓冲区
阻塞与非阻塞转换---非阻塞模式的实现
F_GETFL-----获取文件属性
F_SETFL-----更改文件属性
O_NONLOCK----非阻塞状态(在open函数定义中)
注:#include <strings.h>
void bzero(void *s, size_t n);---设置前n个数据为0
void rewind(FILE *stream);-----将文件指针指向文件开头
2)非阻塞i/o
------函数的执行不用等待,都是立即返回
函数不断不断轮询,极度消耗CPU资源
****3)多路复用i/o
------一个程序可以实现多个i/o的使用(可以操作多个文件描述符而不被阻塞)
3.1多路复用IO与其他IO的差异与优势
3.2创建使用步骤
头文件:<fcntl.h>
FD_SET 将fd加入fdset
FD_CLR 从setfd里面清除fd
FD_ZERO 清空文件描述符集合表
FD_ISSET 判断fd是否在setfd集合中
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);
(1)创建一张文件描述符集合表
---------一个字节为8位---用位描述文件描述符
Fd_set set_fd;
(2)清空集合表
FD_ZERO(&set_fd);
(3)加入要操作的文件描述符到集合
FD_SET(fd,*set_fd);//将fd所在的位设置为1
FD_SET(0,&set_fd);
(4)文件描述符监测
---select函数集合表轮询,检测一个后向后移一位依次判断,到最后一位后又返回到第一位.当检测到一个文件描述符准备就绪时会将其他位置0
当文件进行IO操作时,内核发送信号给select,将其唤醒,
int select(int maxfd, fd_set *readfds, fd_set *writefds fd_set *exceptfds, struct timeval *timeout);,//不能在内部代码中有循环,否则会陷入死循环,不能执行其他的IO操作
参数:
返回值:成功:集合表中文件描述符个数
失败:-1
FD_ISSET(fd,&set_fd)
-----epoll()函数监测文件描述符集合表中已经有的文件描述符,当其中一个文件描述符准备就绪,就执行对应的io操作
实例1:
打开鼠标设备(mice/mouse0/mouse1):
---鼠标有坐标概念,buf[0]-设备号 buf[1]-x buf[2]-y
实例2:
打印鼠标设备信息与从键盘获取数据可以在同一个进程中进行,不会因为阻塞io而阻塞。
注意:max_fd = biggest_fd+1;
Temp_fd = set_fd;
4)信号驱动i/o
------一种异步通信模型
5)i/0总结
**8.服务器模型
1)循环服务器
1.1特点:在同一时刻只能响应一个客户端的请求
------当一个客户端退出后,不会影响服务器,当其他客户端连接也会成功
1.2 创建步骤
Socket()
Bind()
Listen()
While(1){
Accept();
While(1){
Recv();
Process();
Send();
Close();
}
}
2)并发服务器
2.1特点
同一时刻能够处理多个客户端的请求
2.2种类
(1)***多进程并发服务器
父进程只负责监听是否有客户端连接,子进程实现数据交互,子进程会继承父进程的内容
Void signal_handler()
{
Wait(NULL);//
}
Int main{
Socket()
Bind()
Listen()
Signal(SIGCHLD,signal_handler)//父进程回收子进程资源
While(1){
Connfd = Accept();
Pid = Fork()
If(pid < 0){
Err_log();
}
Else if(0 == pid){
Close(sockfd);//子进程不需要监听但需要数据交互
While(1){
Recv();//必须判断,否则会因为客户端关闭不断向connfd写入,造成管道破裂,不能完成3次握手
Process();
Send();
Close(connfd);
Exit(0);
}
}
Else{
//不能用waitpid()--阻塞父进程导致其他客户端的访问/wait(NULL)--非阻塞会关闭connfd导致该客户端不能再次数据交互;
Close(connfd);//父进程不需要数据交互,但需要监听套接字
}
}
注意:子进程关闭sockfd,
由于父进程会频繁的监听是否有其他客户端访问来不及回收子进程的资源导致子进程变成僵尸进程(Z+僵尸态),因此需要父进程用信号处理函数(signal软中断,不会影响用户空间程序的执行)回收子进程资源
SIGCHLD---子进程改变装态时,父进程会收到这个信号-----默认操作忽略
Signal(SIGCHLD,signal_handler)//当父进程收到SIGCHLD信号就会跳转到信号处理函数,执行完信号处理函数再返回进程软中断的地方继续执行
Kill -l:查看所有内核信号
(2) ***多线程并发服务器
Void *pthread_fun(void *arg){
While(1){
Recv();
Process();
Send();
}
Close(connfd);
Pthread_exit(NULL);
}
}
Socket()
Bind()
Listen()
While(1){
Connfd = Accept();
Pthread_create();
Pthread_detach();//不能使用pthread_join()---会阻塞进程
//不能关闭scokfd,connfd因为线程地址空间内容共享
}
}
(3)IO多路复用并发服务器
特点:用一个进程来实现
缺点:当给客户端发送文件工序太长,没有时间为另一个客户端提供服务
注意:temp_fd = set_fd;
FD_SET(connfd);
Max_fd = (lagest_fd > connfd) ? (lagest+1):(connfd+1);
网络编程
网络编程 1
一.基础了解 2
二.网络体系结构 2
1. OSI网络模型 2
2******TCP/IP协议 3
2.1体系架构 3
2.1.1数据传输协议封装过程由内核实现 3
2.1.2数据打包与解包过程 4
2.2IP地址 4
2.2.1分类 4
A类 5
B类 5
C类 5
D(1110开头224.0.0-239.255.255)/E(11110开头240.0.0.0-255.255.255)类 5
子网掩码 5
1) A类地址 5
2) B类地址 5
3) C类地址 5
2.2.2IP地址作用 5
2.2.3:IP地址转换 6
2.3端口号 6
2.3.1作用 6
2.3.2:范围 6
2.4字节序 6
2.4.1主机字节序 6
2.4.2网络字节序 7
2. 套接字(socket) 7
2.1意义/定义 7
2.2为什么要使用套接字 8
2.3套接字类型 8
2.4TCP/UDP 8
2.5TCP服务器搭建流程 8
2.6TCP客户端创建流程 9
作业:完成服务器与客户端正常的聊天(一对一) 10
作业:完成服务器与客户端的一对多聊天 14
3. TCP三次握手/四次挥手协议(connect()与accept()之间完成) 14
4. UDP服务器的创建 15
5. UDP客户端的创建 16
作业:UDP实现群聊功能/聊天室 17
6. TCP与UDP服务器区别 17
7.I/O模型 18
1)阻塞i/o 18
阻塞与非阻塞转换---非阻塞模式的实现 18
2)非阻塞i/o 19
****3) 多路复用i/o 19
3.1多路复用IO与其他IO的差异与优势 19
3.2创建使用步骤 19
(1) 创建一张文件描述符集合表 19
(2) 清空集合表 20
(3) 加入要操作的文件描述符到集合 20
(4) 文件描述监测 20
4)信号驱动i/o 21
二.网络体系结构
6. OSI网络模型
---现在很少使用可通用(了解)
应用层----http(网页协议).FTP(文件协议)
表示层/会话层-------文件/数据加密,建立联系
传输层----tcp协议(对数据纠错检错,保证数据无错传输)
网络层----IP/ICMP协议
数据链路层---将ip地址转换为物理地址
物理层---以太网协议
2******TCP/IP协议
三.项目
1.百度云文件上传与下载
服务器实现list功能
- 词典翻译
四.TCP/IP协议网络编程--进阶篇
一>网络协议头分析
二>设置网络属性****
;1.Setsockopt()----设置套接口属性
int setsockopt(int sockfd, int level, int optname,const void *optval, socklen_t optlen);
Sockfd:套接字
Optval:1有效 0无效
Optlen:第四个参数的长度
例子:套接字属性---bind之前
Int val=1;//为1表示更改属性,为0表示不更改属性
Setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,&val,sizeof(val));
注意:setsockopt()的作用--当程序异常退出,系统没有回收套接字资源(ip和端口),当再次开启服务器不会提示该地址已经被使用,会正常的实现与客户端的交互。不能解决多个进程使用同一的套接字端口的问题
2.Getsockopt()---获取套接口属性
int getsockopt(int sockfd, int level, int optname,void *optval, socklen_t *optlen);
Sockfd:套接字
Optval:根据第三个参数设置,获取时系统自动为其赋值
Optlen:第三个参数长度的地址
例子:获取发送/接收缓存区的大小//结果为字节
Int recv_buf,send_buf;
Int recv_len = sizeof(recv_buf);
Int send_len = sizeof(send_buf);;
Getsockopt(sockfd,SOL_SOCKET,SOL_RCVBUF,&recv_buf,&recv_len)
Getsockopt(sockfd,SOL_SOCKET,SOL_SNDBUF,&recv_buf,&send_len)
Printf(“rcv_buf:%dKB send_buf:%dKB\n”,recv_buf,send_buf);
Recv_buf ---85kb send_buf----16KB
3.具体实例
3.1:设置超时连接
1) 使用setsockopt实现
----对accept进行超时检测,将其放在超时检测之后
Struct timeval tm;
Struct timeval{
Long tv_sec;//秒
Long tv_usec;//微秒
};
Setsockopt(sockfd,SOL_SOCKET,SO_RCVTIMEO,&tm,sizeof(tm));
EG:if(connfd == -1){
If(errno == 11){printf(“timeout---\n”);continue;}//超时提醒,不退出服务器,错误号11是超时为访问的提示
Else{perror(“accept”);}
}
2)***使用select函数来设置超时检测
Struct timeval = {5,0};
Select(max_fd,&set_fd,NULL,NULL,&tm);//当最后一个参数为NULL是select会阻塞等待。设置定时,当定时器时间到了,select会返回0
EG:int set_ret ;
Set_ret = select();
If(0 == set_ret){printf(“timeout----\n”);continue;}
Else{perror(“select”);}
3)设置定时器(timer),捕捉SIGALRM信号
sigaction
三〉****广播和组播
-----只能基于UDP协议
综合:利用广播获得一个服务器,在利用tcp建立连接进行数据交互
1. 广播
1.1定义
在网段里面,主机号全为1的地址就是广播地址,以192.168.5.0,广播地址192.168.5.255
1.2特点:
当客户端发送消息时,在本局域网内的所有主机都能收到信息
--容易引起网络风暴
1.3简介
1.4广播接收端创建
----服务器端/不能准确的找到客户端,只能在第一次连接的时候实时发送一次信息
1)创建数据报套接字
2)设置接收端广播地址与端口信息
3)绑定套接字 bind()
4)Recvfrom()接收信息
5)关闭套接字close()
1.5广播发送端创建----客户端
1)创建数据报套接字
2)设置接受端广播地址与端口信息
3)设置接受端为广播属性 setsockopt()
Int val = 1;//值为1表示设置属性,值为0表示不设置属性
Setsockopt(sockfd,SOL_SOCKET,SO_BROADCAST,&val,sizeof(val))
4)sendto()发送信息
5)关闭套接字close()
2.组播
2.1组播地址
224.0.0.1 - -239.255.255.255 即D类地址
2.2组播特点
只有加入到同组的主机才能收到客户端的消息
2.3组播接受端
1)创建数据报套接字
2)加入到组播组 struct ip_mreq----组播结构体 setsockopt()--设置为组播
struct ip_mreq
{
/* IP multicast address of group. */
struct in_addr imr_multiaddr;
/* Local IP address of interface. */
struct in_addr imr_interface;
};
例子:
Struct ip_mreq mip;
Mip.imr_multiaddr.s_addr = inet_addr(“224.10.10.1”);
Mip.imr_interface.s_addr = inet_addr(“192.168.1.7”);
If(setsockopt(sockfd,IPPROTO_IP, IP_ADD_MEMBERSHIP,&mip,sizeof(mip)))
2.struct ip_mreq mreq;
bzero(&mreq, sizeof(mreq));
mreq.imr_multiaddr.s_addr = inet_addr(“224.10.10.1”);//指定组播地址
mreq.imr_interface.s_addr = htonl(INADDR_ANY); //指定本地接口的IP INADDR_ANY(0)---》ipv4的通配地址 由内核选择一个ip地址
3)设置组播地址与端口信息
4)绑定套接字
5)Recvfrom()
6)Close();
2.4组播发送端
1) 创建数据报套接字
2) 设置组播地址与端口信息
3) Sendto
4) Close
3.UDP与广播
3.1服务器(接受端)--广播需设为广播地址
3.2客户端(发送端)--广播须在发送之前设置广播属性
4.UDP与组播
4.1服务器----在设置组播地址及端口属性之前组播设置组播结构体和设置组播属性
4.2客户端----组播要设置为组播地址
四〉UNIX域套接字/本地套接字
---(tcp/udp)只能进行本机通信----进程间通信
- 简介
- 服务器创建(tcp)
头文件<sys/un.h>
1)创建本地套接字 PF_UNIX域套接字/AF_UNIX
2)设置域套接字描述
struct sockaddr_un {
sa_family_t sun_family; /* AF_UNIX */
char sun_path[UNIX_PATH_MAX]; /* pathname */
};
(remove删除文件)
3)绑定套接字bind(),并创建文件
4)监听
5)接受请求
6)关闭套接字
- 客户端创建(tcp)
1)创建域套接字
2)设置域套接字描述
3)(绑定可选)
4)请求连接connect()
五〉应用综合项目--英语词典翻译
1.数据库
1.1基本概念
基本操作:增删改查
数据的最小单位---数据项
1.2常用数据库
1.3基于嵌入式linux的数据库----sqlite3
1) 安装本地数据库文件
Sudo dpkg -i *.deb
2) 进入并创建数据库
Sqlite3 my.db
3)基本使用命令
.database 查看数据库信息
.help 查看帮助文档
.quit 退出数据库
Drop table tea; 删除tea表
Select * from table_name; 查看table_name表内容
...> 提醒输入时忘记加’;‘
.table <table_name> 查看数据库当中拥有的数据表(如学生管理系统)
Insert into table_name values(12,’eret’,67); 向学生管理系统表插入学生信息
Create table stu(num int ,name text,gread int); 创建一张学生信息的表(text文本类型==数组,s数据类型自动匹配,可省略,要写的话放在变量后面)
增,删,改,查
{
增: insert into <tablename> values(type1, type2, type3 ...);
删: delete from <tablename> where num=12; 按照具体内容删除信息.
改: update <tablename> set num=23,grade=78 where name='yh'; 按照具体内容修改信息
查: select * from <tablename>; 查找所有内容
select * from <tablename> where num=12; 按照具体内容查找
select * from <tablename> limit 2; 查找信息限制为2条.
select * from <tablename> order by num; 查找的信息按照学号排序.
}
4)接口函数使用
{
int sqlite3_open(
const char *filename, /* Database filename (UTF-8) */
sqlite3 **db /* OUT: SQLite db handle */
);
功能:打开数据库 获得数据库的句柄
参数:
filename:数据库路径
db:数据库的句柄(C++中指针的另一种表示)
返回值:
成功:0(SQLITE_OK)
失败:-1
int sqlite3_close(sqlite3 *db);
功能:
关闭数据库
参数:
db:数据库的句柄
返回值:
成功:0(SQLITE_OK)
失败:-1
const char *sqlite3_errmsg(sqlite3 *db);
功能:
提供错误信息
参数:
db:数据库的句柄
返回值:
错误信息
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 */
);
功能:
操作数据库
参数:
db:数据库的句柄
sql:sql语句
callback:回掉函数------>满足条件调用(用于查询)
arg:传递给回掉函数的参数
errmsg :错误信息
返回值:
成功:0(SQLITE_OK)
失败:-1
typedef int (*sqlite3_callback)(void *para, int f_num, char **f_value, char **f_name);
功能:每找到一条记录自动执行一次回调函数
para:传递给回调函数的参数
f_num:记录中包含的字段数目
f_value:包含每个字段值的指针数组
f_name:包含每个字段名称的指针数组
返回值:成功返回0,失败返回-1
int sqlite3_get_table(
sqlite3 *db, /* An open database */
const char *zSql, /* SQL to be evaluated */
char ***pazResult, /* Results of the query */
int *pnRow, /* Number of result rows written here