自学笔记整理,如有不对,请指正;
目录
一.网络分层:
1.特点:
①.每一层实现不同的功能,对上层的数据做透明传输
②.每一层向上层提供服务,同时使用下层提供的服务
2.各层典型协议
①网络接口与物理层
②网络层
③传输层
④应用层
3.数据传输的封包与拆包
二.TCP/IP网络编程预备知识
1.socket
①socket特点
②socket的类型
2.IP地址
①IP地址是Internet中主机的标识;
②表示形式:
③特殊IP地址:
④IP地址转换函数:
3.端口号
4.字节序
①.概念
②.字节序相关函数
三.TCP编程API
1.socket()函数:创建socket
2.bind()函数:绑定端口
3.listen函数:监听,把主动套接字变被动
4.accept()函数:阻塞等待客户端连接
5.connect()函数:连接服务器
四.TCP多线程服务器代码示例
1.头文件
2.服务器端:
3.客户端:
五.UDP编程
1.发送函数
①send()
②sendto()
2.接收函数
①recv()
②recvfrom()
3.UDP循环服务器
①客户端
②服务器
一.IO模型和多路复用模型
①.阻塞IO(最常用)
②非阻塞IO(可防止进程阻塞在I/O操作上,需要轮询)
③多路复用I/O(允许同时对多个I/O进行控制)
④信号驱动I/O(一种异步通信模型)
二.网络分析测试工具、IP头、TCP头
1.网络抓包工具wireshark
2.以太网头
3.IP头/TCP头
4.三次握手,四次释放
1.三次握手
2.四次挥手
网络互联促成了TCP/IP协议的产生,TCP协议分成了两个不同的协议:
用来检测网络传输中差错的传输控制协议TCP(可靠传输)
专门负责对不同网络进行互联的互联网协议IP(不可靠传输)
下三层属于linux内核部分
网络接口和物理层:屏蔽硬件差异
网络层(IP层):端到端的传输,A机器到B机器
传输层(TCP/UDP):数据包应该交给哪个任务去处理
应用层:各种应用层协议
>MAC地址:48位全球唯一,网络设备的身份标识
ARP/RARP:
ARP:IP地址------>MAC地址
RARP:MAC地址------->IP地址
IP地址和MAC地址一一对应,通过这两个协议找到对方;
PPP协议:拨号协议(GPS/3G/4G)
>IP协议(IPV4和IPV6)
>ICMP:Internet控制管理协议,ping命令属于ICMP
>IGMP:Internet分组管理协议,广播,组播
>TCP:传输控制协议提供面向连接的,一对一的可靠数据传输协议(会有开销,占用一定资源)
可靠传输:即数据无误,无丢失,无失序,无重复到达的通信
>UDP:用户数据协议,提供不可靠的,无连接的尽力传输协议(实时性更好)
不可靠连接:不需要建立连接,效率高
>SCTP:TCP的增强版(S:stream),提供面向连接的,多对一或多对多的可靠数据传输协议
网页访问协议:HTTP/HTTPS
邮件收发协议:POP3(收)/SMTP(发)/IMAP(接收邮件的一部分)/FTP
Tnet/SSH:远程登录
嵌入式相关
NTP:网络时钟协议
SNMP:简单网络管理协议(实现对网络设备集中式管理)
RTP/RTSP:传输音视频的协议(安防监控)
app header为应用层的app协议,和data一起被打包为数据包data
Ethernet trailer(CRC):硬件产生和校验4个字节
MTU(Max Transform Unit):最大传输单元,与网络类型有关
以太网MTU=1500
MSS(真正的用户数据大小):最大段的大小,与网络类型,线路,系统的特性相关
以太网MSS=1460
拆包过程:
>socket是一个应用编程接口,代表着网络编程的一种资源;
>一种特殊的文件描述符(对它执行IO操作函数,如read,write);
>不局限于面向连接的TCP/IP,还有无连接的UDP;
>流式套接字(SOCK_STREAM):
唯一对应着TCP,提供了一个面向连接的可靠的数据传输;
>数据报套接字(SOCK_DGRAM):
唯一对应着UDP,提供无连接服务;
>原始套接字(SOCK_RAW):(对应多个协议,传输穿透传输层)
可以跨过传输层,向较低层次协议进行直接访问如IP,ICMP;
>Internet中主机想要和别的机器通信必须要有一个IP地址;
>IP地址分为IPV4和IPV6:
IPV4:采用32位的整数来表示;
IPV6:采用了128位的整数来表示;
例如:mobileIPV6:local IP(本地注册IP),roam IP(漫游IP);
>每个数据包必须携带源IP地址和目的IP地址,路由器依靠此信息为数据包选择路由;
>常用点分形式,如202.38.64.10(16个字节,结尾补0),最后转换成一个32位的无符号整数;
>32位整数形式(四个字节);
局域网IP:192.XXX.XXX.XXX , 10.XXX.XXX.XXX
广播IP:XXX.XXX.XXX.255 , 255.255.255.255(全网广播)
组播IP:224.XXX.XXX.XXX~239.XXX.XXX.XXX
#include "sys/socket.h"
#include "netinet/in.h"
#include arpa/inet.h"
in_addr_t inet_addr(const char*cp);
cp:点分形式的IP地址,结果是32位整数(内部包含了字节序的转换,默认是网络字节序的模式)
特点:
>仅适用于IPV4;
>出错时,返回-1;
>此函数不能适用于255.255.255.255的转换;(255的储存与-1的补码一样)
int inet_pton(int af,const char *src,void *dst);
特点:
>适用于IPV4和IPV6;
>能正确处理255.255.255.255的转换问题;
参数:
>af:地址协议族(AF_INET或AF_INET6);
>src:是一个指针(填写点分形式的IP地址[主要指IPV4] );
>dst:储存转换结果;
inet_ntop();
为了区分一台主机接收到的数据包应该交给哪个任务来处理,使用端口号区分,端口号是一个16位的数字(1-65535)。
>众所周知端口:1-1023(FTP:21,SSH:22,HTTP:80,HTTPS:469)
>保留端口:1024-5000(不建议使用,系统使用了,可能会发生冲突)
>可以使用端口:5000-65535
注意:TCP和UDP端口是相互独立的,比如都可以用5005;
网络里面的通信是由IP地址+(协议+端口)来决定数据包送给哪台机器的哪个任务来处理;
不同类型的CPU的主机访问内存多字节(访问单字节数据,如字符串,则不存在大小端的问题)整数序列有两种方法,称为主机字节序(HBO):
>小端序:低字节储存在起始地址,即低对低,高对高;(X89/ARM)
>大端序:高字节储存在起始地址,即低对高,高对低;(powerpc/mips,ARM作为路由器时)
网络传输时采用大端模式,为了统一有了网络字节序和本地字节序,在发送接收数据时,相互转换;
主机字节序到网络字节序:
#include arpa/inet.h"
>u_long htonl(u_long hostlong);(host to long)
>u_short htons(u_short short);(host to net work short)
网络字节序到主机字节序:
>u_long ntohl(u_long hostlong);
>u_short ntohs(u_short short);
#include "sys/socket.h"
#include "sys/types.h"
int socket(int domain,int type,int protocol);
出错返回-1,成功返回文件描述符
参数:
AF_INET/AF_INET6(IPV4/IPV6); AF_UNIX/AF_LOCAL(本地通信); AF_NETLINK AF_PACKKET(原始套接字) ..... 代码示例: #include "sys/socket.h" #include "sys/types.h" int bind(int sockfd,const struct sockddr *addr,socklen_t addrlen); 成功返回0,失败返回1 参数: >sockfd:socket返回的fd; >addr:struct sockddr的结构体地址(通用结构体,man 7 ip 可查看) 若bind的addr不是具体的一个IP地址,而是INADDR_ANY(宏,值为-1)则代表bind服务器IP地址为本机的任意网卡地址; >addrlrn:地址长度 struct sockaddr_in { /* Internet address. */ 强制转换struct socket_in为struct socket; 代码示例: 注意:如果是IPV6的编程,要使用struct sockadd_in6结构体填充(man 7 ipv6) The sockaddr_in6 structure is bigger than the generic sockaddr. Programs that assume that all address types can be stored safely in a struct sock‐addr need to be changed to use struct sockaddr_storage for that instead. #include int listen(int sockfd, int backlog); 成功返回0,失败返回1 参数: >sockfd:绑定的socket()返回的fd >backlog:同时允许几路客户端进行正在连接的过程(三次握手),一般填5,ARM最大为8; int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen); 成功返回已经建立好连接的新的newfd,与之前的fd相区别,失败返回-1; 参数 >socket()返回的fd; >addr和addrlen:用于获取客户端的IP地址和端口号; #include int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen); 成功返回0,失败返回-1; UPD传输:无连接的不可靠传输,存在丢包,实时性好,用于实时音视频传输,DNS域名解析包 #include ssize_t send(int sockfd, const void *buf, size_t len, int flags); >和write相比,send多一个flags参数,一般填0,此时和write作用一样; MSG_DONTWAIT:非阻塞操作,当缓冲区满时不阻塞等待; MSG_OOB:用于发送TCP类型的带外数据(out—of—band); ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,const struct sockaddr *dest_addr, socklen_t addrlen); #include ssize_t recv(int sockfd, void *buf, size_t len, int flags); >和read相比,send多一个flags参数,一般填0,此时和write作用一样; MSG_DONTWAIT:非阻塞操作,当缓冲区空时不阻塞等待; MSG_OOB:用于接收TCP类型的带外数据(out—of—band); MSG_PEEK:第一次从缓冲区读取的数据后,数据不会消失,下次去读还是原来的数据; ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,struct sockaddr *src_addr, socklen_t *addrlen); >相比于recv,多了两个参数,包含了发送方的IP地址、端口号、协议方式、地址长度; 在UNIX/Linux下主要有4中I/O模型: 缺省情况下,套接字建立后所处的模式就是阻塞I/O,还有读操作:read,recv,recvfrom;写操作:write,send(sendto不阻塞);其他操作:accept,connect >读阻塞:以read为例:调用read从套接字上读取数据,当套接字接收缓冲区为空时,read将发生阻塞,直到缓冲区有数据或者有信号打断阻塞,内核就会唤醒该进程,但如果阻塞过程中发生故障,那进程将永远阻塞下去; >写阻塞:缓冲区大小小于要写入数据量的大小,此时写操作不进行任何拷贝,发生阻塞,缓冲区空间足够时,内核唤醒进程,将数据从用户缓冲区拷贝到相应的缓冲区;UDP套接字上执行的写操作sendto永远不会阻塞,因为这个函数不经过内核缓冲区; 非阻塞I/O下,当I/O操作不能立刻进行时,内核不会让进程阻塞,而是返回一个错误,使用非阻塞套接字时需要有一个应用程序来循环测试是否一个文件描述符可读(叫做polling),这个过程很消耗CPU资源,不普遍使用; >非阻塞模式的实现: socket创建的套接字一般为阻塞I/O,要通过函数fcntl设一个套接字的标志位O_NONBLOCK来实现非阻塞; fcntl() #include int fcntl(int fd, int cmd, ... /* arg */ ); ioctl() #include int ioctl(int fd, unsigned long request, ...); 当应用程序处理多路输入输出流时,阻塞I/O达不到预期效果,非阻塞I/O会占用太多资源,若设置多个进程,又会产生新进程间的通信问题,使程序变得复杂;较好的解决方法是使用I/O多路复用,基本思想是: >把关心的文件描述符组成一个表fd_set,表的每一位代表一个文件描述符,这张表大小写是4字节的整数倍,具体根据存在的最大fd决定(节约资源); >然后调用函数select()/poll()函数,监控fd_set中的文件描述符,阻塞等待一个或多个文件描述符有数据,退出select(); >退出select()后依次判断哪个文件描述符有数据; >依次处理有数据的文件描述符; fd_set相关函数() void FD_ZERO(fd_set*fdset);//对集合清零 void FD_SET(int fd,fd_set*fdset);//加入集合 void FD_CLR(int fd,fd_set*fdset);//清除fd int FD_ISSET(int fd,fd_set*fdset);//判断fd是否在集合中 select() #include int select(int nfds, fd_set *readfds, fd_set *writefds,fd_set *exceptfds, struct timeval *timeout); nfds:最大的文件描述符maxfd+1; readfds/writefds:读写文件描述符集合; 超时结构体: struct timeval { 一般填充读集合,写集合为NULL,异常集合一般NULL(带外数据才填),超市检测,阻塞超出时间时退出select,select退出后,集合表示有数据的集合; 也就是说select()之前是关心的文件描述符的集合,select()之后是有数据的文件描述符; baidu直接下载就行; linux下:sudo apt-get install wireshark 以太网头中有目的MAC地址和源MAC地址各占6个字节,每个MAC地址都是全球唯一的; CRC字段用于对帧内数据进行校验,保证数据传输的正确性,通常是硬件实现的。 https://blog.csdn.net/m0_37542524/article/details/84594111 A的TCP客户进程向B发出建立连接请求报文段,其中SYN(同步位)=1,ACK(确认位)=0,seq(序号)=x。 TCP规定,当报文段的SYN=1且ACK=0时,表明这是一个请求建立连接的;SYN报文段(SYN=1的报文段)不能携带数据,但是要消耗掉一个序号。 在A发送完毕之后,A的TCP客户端进程进入SYN-SENT(同步已发送)状态。 B在收到连接请求报文段之后,如果同意建立连接,则向A发送确认报文段。其中SYN=1,ACK=1,ack(确认号)=x+1,同时设置自己的初始序号seq=y。 TCP规定,当报文段的SYN=1且ACK=1时,表明这是一个同一建立连接响应报文段;这个报文段也不能携带数据,同样需要消耗掉一个序号。 在B发送完毕之后,B的TCP服务端进程进入SYN-RCVD(同步收到)状态。 A在收到B的确认报文段之后,还需要向B给出确认报文段。其中ACK=1,seq=x+1,ack=y+1。 TCP规定,这个ACK报文段可以携带数据;如果不携带数据则不消耗序号,即A下一个数据报文段的序号仍然是seq=x+1。 在A发送完毕之后,A的TCP客户端进程进入ESTABLISHED(已建立连接)状态;B在接收到A的确认报文段之后,B的服务端进程也进入ESTABLISHED(已建立连接)状态。 原文链接:https://blog.csdn.net/guoweimelon/article/details/50878730 先由客户端向服务器端发送一个FIN,请求关闭数据传输。 当服务器接收到客户端的FIN时,向客户端发送一个ACK,其中ack的值等于FIN+SEQ 然后服务器向客户端发送一个FIN,告诉客户端应用程序关闭。 当客户端收到服务器端的FIN是,回复一个ACK给服务器端。其中ack的值等于FIN+SEQ 相关函数: #include struct hostent *sethostbyname(const char *name);//只适用于IPV4 struct hostent *gethostbyaddr(const void *addr,socklen_t len, int type); name填域名或者IP地址; void herror(const char *s); const char *hstrerror(int err); struct hostent { h_addr_list:An array of pointers to network addresses for the host (in net‐work byte order), terminated by a null pointer.(指向主机的多个网路字节序的网络地址列表) ①setsockopt #include 参数: >level为指定控制套接字的层次: 1)SOL_SOCKET:通用套接字选项(应用层) 2)IPPRORO_TCP:TCP选项(传输层) 3)IPPROTO_IP:选项(网络层) >optval:对于getsockopt(),指向返回选项值的缓冲。对于setsockopt(),指向包含新选项值的缓冲。 选项名称 说明 数据类型 示例: ②getsockopt int getsockopt(int sockfd, int level, int optname,void *optval, socklen_t *optlen); 在网络通信中,很多操作会使进程阻塞(recv,recvfrom,connect,accept),超时检测可以避免进程在没有数据时无限制的阻塞,当设定的时间到,进程从原操作返回继续运行。 简单的几种网络超时检测_youge_C的博客-CSDN博客_网络超时检测 信号处理:signal和sigaction的区别_fly_air的博客-CSDN博客 动态检测网络是否断开或者异常: ①交互双方每隔一段时间相互发送一定数据,若超出设定次数均未检测到数据,则网络有问题 ②设置套接字属性 >广播:将数据包同时发送给局域网里的所有主机 >只有UDP才能广播 >广播地址: -以192.168.1.0网段为例,最大的主机地址为192.168.1.255代表该网段的广播地址 -发送到广播地址的数据包被所有的主机接收 -255.255.255.255在所有网段中都代表广播地址 >创建UDP套接字 >缺省的套接字不能广播数据包,需要设置套接字属性(setsockopt) >接收方地址为广播地址,指定端口信息,发送数据包 >创建UDP套接字 >绑定本机IP地址和端口(绑定的端口必须和发送方指定的端口相同) >等待接受数据 组播:一个人发送,只有加入某个多播组的主机才能接收到数据 组播的IP地址:224.0.0.1-239.255.255.254(中间除掉广播地址) 组播编程都是基于UDP编程 >创建DUP套接字 >接收方地址为组播地址,指定端口信息 >发送数据包 >创建UDP套接字 >加入多播组 struct ip_mreqn{ >bind本机IP地址 >等待接受数据 socket可以在创建时使用本地协议PF_UNIX(或PF_LOCAL)用于本地通信。 >socket(AF_LOCAL,SOCK_STREAM,0) >socket(AF_LOCAL,SOCK_DG_RAM,0) bind时填充的结构体是sockaddr_un struct sockaddr_un { sun_path:unix域套接字的文件路径名,必须事先不存在,给绝对路径; bind后,根据TCP/UDP进行相应的编程; 进程间通信:int fd;
struct socket_in sin;
/*1.创建socket */
if((fd=socket(AF_INET,SOCK_STREAM,0))<0)//创建socket
{
perror("socket");
exit(-1);
}
2.bind()函数:绑定端口
sa_family_t sin_family; /* address family: AF_INET */(2字节)
in_port_t sin_port; /* port in network byte order */(2字节)
struct in_addr sin_addr; /* internet address */(4字节)
};
struct in_addr {
uint32_t s_addr; /* address in network byte order */
};/*2.绑定*/
/*2.1填充*/
bzero(&sin,sizeof(sin));//清零
sin.sin_family=AF_INET;
sin.sin_port=htons(SERV_PORT);//网络字字节序的端口
sin.sin_addr.s_addr=inet_addr(SERV_IP_ADDR);//IP地址转换
/* if(inet_pton(AF_INET,SERV_IP_ADDR,(void *)&sin.sin_addr)!=1)
{
perror("inet_pton");
exit(-1);
}
另一种方式的转换*/
/*2.2绑定*/
if(bind(fd,(*struc socket)&sin,sizeof(sin))<0)//绑定
{
exit(-1);
}
3.listen函数:监听,把主动套接字变被动
#include 4.accept()函数:阻塞等待客户端连接
#include
#include
5.connect()函数:连接服务器
#include
和服务器的bind()函数写法类似;四.TCP多线程服务器代码示例
1.头文件
#ifndef _CS_H_
#define _CS_H_
#include "stdio.h"
#include "stdlib.h"
#include "string.h"
#include "unistd.h"
#include "sys/types.h"
#include "sys/socket.h"
#include "strings.h"
#include
2.服务器端:
#include "cs.h"
void* pthread_server(void*newfd);
int main()
{
int fd=-1;
int newfd=-1;
struct sockaddr_in sin;
/*1.创建socket */
if((fd=socket(AF_INET,SOCK_STREAM,0))<0)//创建socket
{
perror("socket");
exit(-1);
}
/*2.绑定*/
/*2.1填充*/
bzero(&sin,sizeof(sin));//清零
sin.sin_family=AF_INET;
sin.sin_port=htons(SERV_PORT);//网络字字节序的端口
sin.sin_addr.s_addr=htonl(INADDR_ANY);//IP地址转换,绑定任意IP地址
/*2.2绑定*/
if((bind(fd,(struct sockaddr*)&sin,sizeof(sin)))<0)//绑定
{
exit(-1);
}
/*3.调用listen()把主动套接字变被动*/
if(listen(fd,BACKLOG)<0)
{
perror("listen");
exit(-1);
}
printf("Server staring...\n");
/*4.阻塞等待客户端的连接请求*/
struct sockaddr_in cin;
socklen_t addrlen=sizeof(cin);
pthread_t pthread;
while(1)
{
if((newfd=accept(fd,(struct sockaddr*)&cin,&addrlen))<0){//阻塞等待连接
perror("accept");
exit(-1);
}
char ipaddr[16];
if(inet_ntop(AF_INET,(void*)&cin.sin_addr,ipaddr,sizeof(cin))<0)
{
perror("inet_ntop");
exit(-1);
}
printf("Client IP is:%s\nPort is:%d\n",ipaddr,ntohs(cin.sin_port));//打印客户端信息
if(pthread_create(&pthread,NULL,pthread_server,(void*)&newfd)<0){//建线程
perror("pthread_create");
exit(-1);
}
}
close(fd);
return 0;
}
void* pthread_server(void*arg)//线程完成读写操作
{
char buf[BUFSIZE];
int newfd=*(int*)arg;
int ret=-1;
while(1)
{
bzero(buf,sizeof(buf));
do
{
ret=read(newfd,buf,BUFSIZE-1);
}while(ret<0&&EINTR==errno);//EINTR系统调用被信号中断
if(ret<0){
perror("read");
exit(1);
}
if(!ret)//对方已关闭
break;
printf("Receive data:%s\n",buf);
if(strncasecmp(buf,"quit",strlen("quit"))==0){
printf("Client(fd=%d) is exiting\n",newfd);
break;
}
}
close(newfd);
}
3.客户端:
#include "cs.h"
int main(int argc,char**argv)
{
if(argc<3){
printf("Usage....\n");
exit(-1);
}
int fd;
short PORT=atoi(argv[2]);
char*ADDR_IP=argv[1];
struct sockaddr_in sin;
/*1.创建socket*/
if((fd=socket(AF_INET,SOCK_STREAM,0))<0){
perror("socket");
exit(-1);
}
/*2.连接服务器*/
sin.sin_family=AF_INET;
sin.sin_port=htons(PORT);
sin.sin_addr.s_addr=inet_addr(ADDR_IP);
if(connect(fd,(struct sockaddr*)&sin,sizeof(sin))<0)
{
perror("connect");
exit(-1);
}
/*3.读写数据*/
char buf[BUFSIZE];
while(1)
{
bzero(buf,BUFSIZE);
if(fgets(buf,BUFSIZE,stdin)==NULL){
continue;
}
if(strncasecmp(buf,"quit",strlen("quit"))==0){
printf("CLient is exiting\n");
break;
}
write(fd,buf,strlen(buf));
}
/*4.关闭套接字*/
close(fd);
return 0;
}
五.UDP编程
1.发送函数
①send()
#include
成功返回发生的字节数,失败返回-1;②sendto()
>相比于recv,多了两个参数,包含了接收方的IP地址、端口号、协议方式、地址长度;2.接收函数
①recv()
#include
成功返回接收到的字节数,失败返回-1;②recvfrom()
3.UDP循环服务器
①客户端
#include "cs.h"
int main(int argc,char**argv)
{
int fd=-1;
if((fd=socket(AF_INET,SOCK_DGRAM,0))<0){//创建socket
perror("socket");
exit(-1);
}
char buf[BUFSIZE];
struct sockaddr_in cin;//创建填充结构体
bzero(&cin,sizeof(cin));
short port=htons(atoi(argv[2]));
cin.sin_family=AF_INET;
cin.sin_port=port;
cin.sin_addr.s_addr=inet_addr(argv[1]);
socklen_t addrlen=sizeof(cin);
while(1){//客户端发送数据
if(fgets(buf,BUFSIZE-1,stdin)==NULL){
perror("fgets");
exit(-1);
}
sendto(fd,buf,strlen(buf),0,(struct sockaddr*)&cin,addrlen);
if(strncasecmp(buf,"quit",strlen("quit"))==0)
{
printf("Client is exiting...\n");
break;
}
}
close(fd);
}
②服务器
#include "cs.h"
int main()
{
int fd=-1;
struct sockaddr_in sin;
if((fd=socket(AF_INET,SOCK_DGRAM,0))<0){//创建socket
perror("socket");
exit(-1);
}
bzero(&sin,sizeof(sin));
sin.sin_family=AF_INET;
sin.sin_port=htons(SERV_PORT);
sin.sin_addr.s_addr=INADDR_ANY;
if(bind(fd,(struct sockaddr*)&sin,sizeof(sin))<0){//绑定
perror("bind");
exit(-1);
}
char buf[BUFSIZE];
struct sockaddr_in cin;
socklen_t addrlen;
while(1){//等待接受数据,并处理
bzero(buf,BUFSIZE);
bzero(&cin,sizeof(cin));
if(recvfrom(fd,buf,BUFSIZE-1,0,(struct sockaddr*)&cin,&addrlen)<0){
perror("recvfrom");
continue;
}
char client_ip[16];
inet_ntop(AF_INET,(void*)&cin.sin_addr,client_ip,sizeof(cin));
short port=ntohs(cin.sin_port);
if(strncasecmp(buf,"quit",strlen("quit"))==0){//处理数据
printf("Client:%s port:%d is exiting...\n",client_ip,port);
}
else{
printf("Recive data from:%s port:%d:\n%s\n",client_ip,port,buf);
}
}
}
六.IO模型和多路复用模型
①.阻塞IO(最常用)
②非阻塞IO(可防止进程阻塞在I/O操作上,需要轮询)
#include
例如:int flag;
flag=fcntl(fd,F_GETFL,0);
flag|=O_NONBLOCK;
fcntl(fd,F_GETFL,flag);
int b_on=1;
ioctl(fd,FIONBIO,&b_on);
③多路复用I/O(允许同时对多个I/O进行控制)
#include
#include
参数:
long tv_sec; /* seconds */
long tv_usec; /* microseconds */
};④信号驱动I/O(一种异步通信模型)
七.网络分析测试工具、IP头、TCP头
1.网络抓包工具wireshark
2.以太网头
3.IP头/TCP头
4.三次握手,四次释放
1.三次握手
①第一次握手
②第二次握手
③第三次握手
2.四次挥手
原文链接:https://blog.csdn.net/sanwe3333/article/details/110110006八.网络知识扩展
1.网络信息检索
extern int h_errno;
域名解析,成功返回一个结构体指针,出错返回空;出错是打印错误:
char *h_name; /* official name of host */
char **h_aliases; /* alias list */
int h_addrtype; /* host address type */
int h_length; /* length of address */
char **h_addr_list; /* list of addresses */
}
#define h_addr h_addr_list[0] /* for backward compatibility *
void endhostent(void);//不用返回的信息结构体时,释放2.网络属性设置
#include
int setsockopt(int sockfd, int level, int optname,const void *optval, socklen_t optlen);
>optlen:对于getsockopt(),作为入口参数时,选项值的最大长度。作为出口参数时,选项值的实际长度。对于setsockopt(),现选项的长度。
========================================================================
SOL_SOCKET
------------------------------------------------------------------------
SO_BROADCAST 允许发送广播数据 int
SO_DEBUG 允许调试 int
SO_DONTROUTE 不查找路由 int
SO_ERROR 获得套接字错误 int
SO_KEEPALIVE 保持连接 int
SO_LINGER 延迟关闭连接 struct linger
SO_OOBINLINE 带外数据放入正常数据流 int
SO_RCVBUF 接收缓冲区大小 int
SO_SNDBUF 发送缓冲区大小 int
SO_RCVLOWAT 接收缓冲区下限 int
SO_SNDLOWAT 发送缓冲区下限 int
SO_RCVTIMEO 接收超时 struct timeval
SO_SNDTIMEO 发送超时 struct timeval
SO_REUSERADDR 允许重用本地地址和端口 int
SO_TYPE 获得套接字类型 int
SO_BSDCOMPAT 与BSD系统兼容 int
========================================================================
IPPROTO_IP
------------------------------------------------------------------------
IP_HDRINCL 在数据包中包含IP首部 int
IP_OPTINOS IP首部选项 int
IP_TOS 服务类型
IP_TTL 生存时间 int
========================================================================
IPPRO_TCP
------------------------------------------------------------------------
TCP_MAXSEG TCP最大数据段的大小 int
TCP_NODELAY 不使用Nagle算法 int
========================================================================/*允许广播*/
int b_br=1;
setsockopt(fd,SOL_SOCKET,SO_BROADCAST,&b_br,sizeof(int));
/*设置接收超时*/
struct timeval tout;
tout.tv_sec=5;
tout.tv_usec=0;
setsockopt(fd,SOL_SOCKET,SO_RCVTIMEO,&tout,sizeof(struct timeval));
3.网络超时检查
①setsocktopt函数
②select函数
③设置定时器t,sigaction()捕捉SIGALRM
/*函数定义:*/
void setKeepAlive (int sockfd, int attr_on, socklen_t idle_time, socklen_t interval, socklen_t cnt)
setsockopt (sockfd, SOL_SOCKET, SO_KEEPALIVE, (const char *) dattr_on, sizeof (attr_on)) ;
setsockopt (sockfd, SOL_TCP, TCP_KEEPIDLE, (const char *) idle_time, sizeof (idle_time))
setsockopt (sockfd, SOL_ICP, TCP_KEEPINTVL, (const char *) &interval, sizeof (interval)) :
setsockopt (sockfd, SOL_ICP, TCP_KEEPCNT, (const char *) &ent, sizeof (cnt)):
/*使用:*/
int keepAlive = 1;
//设定KeepAlive
int keepIdle = 5;
1/开始首次KeepAlive探测前的TCP空闭时间
int keepInterval = 5;
//两次KeepAlive探测间的时间间隔
int keepCount = 3;
//判定断开前的KeepAlive探测次数
setkeepAlive (newfd, keepAlive, keepIdle, keepInterval, keepCount):
4.广播
①广播的发送
②广播的接收
5.组播
①组播的发送
②组播的接收
struct in_addr imr_multiaddr; /* IP multicast group
address */
struct in_addr imr_address; /* IP address of local
interface */
};
(man 7 ip)struct ip_mreq mreq;
mreq.imr_multiaddr.s_addr=inet_addr(MUTICAST_IP);
mreq.imr_interface.s_addr=htonl(INADDR_ANY);
setsockopt(fd,IPPROTO_IP,IP_ADD_MEMBERSHIP,&mreq,sizeof(mreq));
6.UNIX域套接字
sa_family_t sun_family; /* AF_UNIX */
char sun_path[108]; /* pathname */
};
进程间的数据共享:管道、消息队列、共享内存、unix域套接宇
易用性:
消息队列>unix域套接字 ›管道,共享内存(经常要和信号量一起用)
效率:共享内存 >unix域套接字>管道>消息队列
常用:共享内存、unix域套接字
异步通信:信号
同步和互斥:(做资源保护)信号量