当前位置: 星创客 > 学习资源 > 学员笔记 > Linux下网络编程学习笔记
Linux下网络编程学习笔记 时间:2017-11-09     来源:星创客
网络编程
	=======================================================================
	OSI开放系统互联模型
		应用层:
			应用程序:FTP、E-mail、Telnet
		表示层:
			数据格式定义、数据转换/加密	
		会话层:
			建立通信进程的逻辑名字与物理名字之间的联系 	
		传输层:
			差错处理/恢复,流量控制,提供可靠的数据传输	
		网络层:
			数据分组、路由选择
		数据链路层:
			数据组成可发送、接收的帧
		物理层:
			传输物理信号、接口、信号形式、速率
	=======================================================================
	7层通信
		(1)应用层:
				指定特定应用的协议
				(比如发送和接受文件的软件按钮,发送者输入“早上好”并附上收件人,
				按下发送按钮,接受者收到信息会将其存储在硬盘或者非易失存储器
				(数据不会因为断电而丢失的一种存储设备)上,这些都是在应用层上的)
				
		(2)表示层:
				设备固有数据格式和网络标准数据格式的转换
				(接受者和发送者如果使用的邮件客户端不一样,
				那么就会出现问题,如何实现用户之间的通信,那么就需要在表示层来起作用,
				使得在不同的客户端上拥有相同的网络格式)
				
		(3)会话层:
				通信管理,负责建立或者断开通信连接
				(发送者一次性发送5份邮件,那么接受者如何接受,
				是一次性接受所有的文件然后断开连接还是没接受一次就断开,然后在此进行,发送者同理)
				
		(4)传输层:
				管理两个节点(互联的网络中断)之间的数据传输。
				负责可靠传输(确保数据被可靠地传送到目标地址)
				(确保发送者和接受者之间的通信,会话层负责决定建立连接和断开连接的时机,
				而传输层进行实际的建立和断开处理)
				
		(5)网络层:
				地址管理与路由选择,作用:在网络相互连接的环境中,
				将数据从发送端主机发送到接受端主机
				
		(6)数据链路层:
				互连设备之间传送和识别数据帧
				
		(7)物理层:
				以“0”、“1”代表的电压的高低、灯光的闪灭。界定连接器和网络的规格。
	
	=======================================================================
	TCP/IP协议族:
		传输控制/网际协议(Transfer Control Protocol/Internet Protocol) 又称作网络通讯协议
		
		应用层	TFTP,HTTP,SNMP,FTP,SMTP,DNS,Telnet 
		传输层	TCP,UDP 
		网络层	IP,ICMP,RIP,OSPF,BGP,IGMP 
		网络接口与物理层	SLIP,CSLIP,PPP,ARP,RARP,MTU ISO2110,IEEE802.1,EEE802.2 
		
		TCP(Transport Control Protocol)传输控制协议
		IP(Internetworking Protocol)网间协议	
		UDP(User Datagram Protocol)用户数据报协议	
		SMTP(Simple Mail Transfer Protocol)简单邮件传输协议	
		HTTP(Hypertext Transfer Protocol) 超文本传输协议	
		FTP(File Transfer Protocol)文件传输协议	
		ARP(Address Resolution Protocol)地址解析协议

	=======================================================================
	socket的分类
			流式套接字(SOCK_STREAM)
				提供了一个面向连接、可靠的数据传输服务,数据无差错、无重复的发送且按发送顺序接收。
				内设置流量控制,避免数据流淹没慢的接收方。数据被看作是字节流,无长度限制。
				==>TCP
				
			数据报套接字(SOCK_DGRAM)
				提供无连接服务。数据包以独立数据包的形式被发送,不提供无差错保证,
				数据可能丢失或重复,顺序发送,可能乱序接收。
				==>UDP
				
			原始套接字(SOCK_RAW)
				可以对较低层次协议如IP、ICMP直接访问。
	=======================================================================
	IP地址的分类(依据ipv4的前八位来区分)
			A类	0000 0000 - 0111 1111  0.x.x.x - 127.x.x.x
			B类	1000 0000 - 1011 1111  128.x.x.x - 191.x.x.x
			C类 1100 0000 - 1101 1111  192.x.x.x - 223.x.x.x
			D类	1110 0000 - 1110 1111  224.x.x.x - 239.x.x.x  表示组播地址
			E类	1111 0000 - 1111 1111  240.x.x.x - 255.x.x.x  属于保留测试
			
			127.x.x.x 表示主机地址
			192.168.x.x 表示局域网ip地址
			
			192.168.1.x为例
				192.168.1.0 表示网段、网络地址
				192.168.1.1 表示网关
				192.168.1.255 表示广播地址
			
		子网掩码 表示主机的大连接数
			A类  255.0.0.0    2~24
			B类  255.255.0.0  2~16
			C类  255.255.255.0  2~8
	=======================================================================
	字节序
		不同类型CPU的主机中,内存存储多字节整数序列有两种方法,称为主机字节序(HBO)
			小端序(little-endian) - 低序字节存储在低地址
				将低字节存储在起始地址,称为“Little-Endian”字节序,Intel、AMD等采用的是这种方式
			大端序(big-endian)- 高序字节存储在低地址
				将高字节存储在起始地址,称为“Big-Endian”字节序,由ARM、Motorola等所采用

		网络中传输的数据必须按网络字节序,即大端字节序
	=======================================================================
	IP地址和端口的转换
		#include <arpa/inet.h>
		in_addr_t inet_addr(const char *cp);
			将点分十进制IP地址转化为网络字节序的整型数据
		char *inet_ntoa(struct in_addr in);
			将网络字节序的整型数据转化为点分十进制IP地址
		
		
		#include <arpa/inet.h>
		uint32_t htonl(uint32_t hostlong);
		uint16_t htons(uint16_t hostshort);
			将主机字节序转化为网络字节序
		
		uint32_t ntohl(uint32_t netlong);
		uint16_t ntohs(uint16_t netshort);
			将网络字节序转化为主机字节序
=======================================================================
TCP网络编程
	比起udp,tcp的服务器端多出了listen和accept的操作
	为了确保通信的可靠性,需要先确认连接上,所以需要accept接收客户端的connect请求
	因为tcp已经确定了稳定的连接,所以可以得到固定的文件描述符用来操作发送和接受信息
	udp只能每次都通过地址发送
	=======================================================================
	服务器端
		创建套接字socket
		int socketfd = socket(AF_INET, SOCK_STREAM, 0);
		
		struct sockaddr_in addr;
		addr.sin_family = AF_INET;
		addr.sin_port = htons(9999);
		addr.sin_addr.s_addr = inet_addr("192.168.1.100");
		
		绑定网络信息
		bind(socketfd, (struct sockaddr *)&addr, sizeof(addr));
		
		设置监听
		listen(&socketfd, 5);
		
		int client_socketfd;
		struct sockaddr_in client_addr;
		socklen_t client_addr_len = sizeof(client_addr);
		
		接收来自connect的请求
		client_socketfd = accept(socketfd, (struct sockaddr *)&client_addr, &client_addr_len);
	=======================================================================
	客户端
		创建套接字socket
		int socketfd = socket(AF_INET, SOCK_STREAM, 0);
		
		struct sockaddr_in serveraddr;
		serveraddr.sin_family = AF_INET;
		serveraddr.sin_port = htons(9999);
		serveraddr.sin_addr.s_addr = inet_addr("192.168.1.100");
		
		连接服务器,建立通信
		connect(socketfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr));
=======================================================================
UDP网络编程
	服务器端只是比客户端多了一个bind的操作,需要先确定一个目标,客户端才可以准确的发来数据
	=======================================================================
	服务器端
		int socketfd = socket(AF_INET, SOCK_DGRAM, 0);
		
		struct sockaddr_in addr;
		addr.sin_family = AF_INET;
		addr.sin_port = htons(9999);
		addr.sin_addr.s_addr = inet_addr("192.168.1.100");
		
		//为了一开始客户端可以有一个确定的发送对象,可以向这个ip这个port发送信息
		bind(socketfd, (struct sockaddr *)&addr, sizeof(addr));
		
		struct sockaddr_in client_addr;
		socklen_t client_addr_len = sizeof(client_addr);
		
		recvfrom(socketfd, buf, sizeof(buf), 0, (struct sockaddr *)&client_addr, &client_addr_len);
		sendto(socketfd, buf, sizeof(buf), 0, (struct sockaddr *)&client_addr, client_addr_len);
	=======================================================================
	客户端
		int socketfd = socket(AF_INET, SOCK_DGRAM, 0);
		
		struct sockaddr_in serveraddr;
		serveraddr.sin_family = AF_INET;
		serveraddr.sin_port = htons(9999);
		serveraddr.sin_addr.s_addr = inet_addr("192.168.1.100");
		
		//知道对方的ip和port之后,可以直接发送消息
		sendto(socketfd, buf, sizeof(buf), 0, (struct sockaddr *)&serveraddr, sizeof(serveraddr));
=======================================================================
网络编程方法定义
	==============================================================
	创建套接字socket()
		#include <sys/types.h>          /* See NOTES */
		#include <sys/socket.h>
		int socket(int domain, int type, int protocol);
			参数及使用方式:
				domain:通信域,协议族
					AF_UNIX 本地通信
					AF_INET ipv4网络协议
					AF_INET6 ipv6网络协议
					AF_PACKET 底层的协议
				type:类型
					SOCK_STREAM 流式套接字 tcp
					SOCK_DGRAM 数据报套接字 UDP
					SOCK_RAW 底层的通信
				protocol:一般为0
			返回值:
				成功:文件描述符serversocketfd
				失败:-1
	==============================================================
	绑定网络地址bind()
		#include <sys/types.h>          /* See NOTES */
		#include <sys/socket.h>
		int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
			参数及使用方式:
				sockfd:文件描述符,serversocket
				addrlen:addr的长度,serveraddr_len
				addr:网络信息结构体 serveraddr
					通用的(一般不用)
						struct sockaddr {
							sa_family_t sa_family;  2个字节
							char        sa_data[14];  14个字节
						}
					常用:sockaddr_in
					#include <netinet/in.h>
					struct sockaddr_in{
						sa_family_t sin_family;  地址族 AF_INET  2个字节
						in_port_t sin_port;   端口号 2个字节
						struct in_addr sin_addr;
							struct in_addr{
									in_addr_t s_addr;  ip地址  4个字节
							};
						//这个没有用,为了保证sockaddr与sockaddr_in一样大
						unsigned char sin_zero[sizeof (struct sockaddr) -
								__SOCKADDR_COMMON_SIZE -
								sizeof (in_port_t) -
								sizeof (struct in_addr)]; 
					}
				返回值:
					成功:0
					失败:-1
				例子:
					struct sockaddr_in serveraddr;
					
					serveraddr.sin_family = AF_INET;
					serveraddr.sin_port = htons(9999);
					serveraddr.sin_addr.s_addr = inet_addr("192.168.1.123");
					bind(sockfd, (struct sockaddr *)&serveraddr, sizeof(struct sockaddr_in))
	==============================================================
	设置监听listen()
		#include <sys/types.h>          /* See NOTES */
		#include <sys/socket.h>
		int listen(int sockfd, int backlog);
			参数及使用方式:
				sockfd:文件描述符,serversocket
				backlog:允许同时响应客户端请求的个数,一般为5, 10
			返回值:
				成功:0
				失败:-1
	==============================================================
	阻塞等待客户端的连接请求accept()
		#include <sys/types.h>          /* See NOTES */
		#include <sys/socket.h>
		int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
			参数及使用方式:
				sockfd:文件描述符,serversocket
				addr:网络信息结构体client_addr
				addrlen:client_addr_len
			返回值:
				成功:客户端文件描述符,client_socket
				失败:-1
	==============================================================
	客户端连接服务器connect()
		#include <sys/types.h>
		#include <sys/socket.h>
		int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
			参数及使用方式:
				sockfd:文件描述符,client_sock
				addr:服务器地址
				addrlen:addr的长度
			返回值:
				成功:0
				失败:-1
	==============================================================
	tcp发送信息send()
		#include <sys/types.h>
		#include <sys/socket.h>
		ssize_t send(int sockfd, const void *buf, size_t len, int flags);
			参数及使用方式:
				sockfd:文件描述符
					服务器:使用accept的client——sock
					客户端:自己的socket
				buf:发送的数据
				len:数据的长度
				flags:标志位
					0  阻塞
					MSG_DONTWAIT 非阻塞
			返回值:
				成功:发送的数据的长度
				失败:-1

	==============================================================
	tcp接收信息recv()
		#include <sys/types.h>
		#include <sys/socket.h>
		ssize_t recv(int sockfd, void *buf, size_t len, int flags);
			参数及使用方式:
				sockfd:文件描述符
					服务器:使用accept的client——sock
					客户端:自己的socket
				buf:用于存放接收的数据
				len:接收指定长度的直接
				flags:标志位
					0  阻塞
					MSG_DONTWAIT 非阻塞
			返回值:
				成功:发送的数据的长度
					0 发送者关闭文件描述符或者退出
				失败:-1

	===============================================================
	udp发送消息sendto()
		#include <sys/socket.h>
		ssize_t sendto(int socket, const void *message, size_t length,
			int flags, const struct sockaddr *dest_addr, socklen_t dest_len);
			参数及使用方式:
				socket:文件描述符
				message:发送的数据
				length:数据的长度
				flags:标志位,一般为0
				dest_addr:目的地址(发送给谁)
				dest_len:addr的长度
			返回值:
				成功:发送的数据的长度
				失败:-1
	===============================================================
	udp接收消息recvfrom()
		#include <sys/types.h>
		#include <sys/socket.h>
		ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
			struct sockaddr *src_addr, socklen_t *addrlen);
			参数及使用方式:
				sockfd:文件描述符
				buf:接收的数据
				len:数据的长度
				flags:标志位,一般为0
				src_addr:源的地址(自动填充)
				addrlen:addr的长度
			返回值:
				成功:接收的数据的长度
				失败:-1
================================================================
IO模型
	在UNIX/Linux下主要有4种I/O 模型:
		阻塞I/O:
			常用、简单、效率低
		非阻塞I/O:
			可防止进程阻塞在I/O操作上,需要轮询
		I/O 多路复用:
			允许同时对多个I/O进行控制
		信号驱动I/O:
			一种异步通信模型
	================================================================
	fcntl非阻塞IO
		#include <unistd.h>
		#include <fcntl.h>
		int fcntl(int fd, int cmd, ... /* arg */ );
			参数及使用方式:
				fd:要操作文件描述符
				cmd:命令
					F_GETFL 获取文件状态标志位
					F_SETFL 设置文件状态标志位
							O_NONBLOCK 非阻塞
				arg:可变参
					根据需求选择需不需要
			返回值:
				成功:
					F_GETFL 获取到的文件状态标志位
					F_SETFL 0
				失败:
					-1
			例子:
				注意:对寄存器或者位的操作,一般执行读、改、写三步
		
				第一步:获取文件状态标志位
				int flags;
				if((flags = fcntl(0, F_GETFL)) < 0)
				{
					perror("fail to fcntl");
					return -1;
				}
				
				第二步:修改标志位
				flags = flags | O_NONBLOCK;
				
				第三步:将新的标志位写回去
				if(fcntl(0, F_SETFL, flags) < 0)
				{
					perror("fail to fcntl");
					return -1;
				}
================================================================
IO多路复用
	============================================================
	select
		#include <sys/select.h>
		#include <sys/time.h>
		#include <sys/types.h>
		#include <unistd.h>

		int select(int nfds, fd_set *readfds, fd_set *writefds,
				fd_set *exceptfds, struct timeval *timeout);
			参数及使用方式:
				nfds:大的文件描述符加1
				readfds:读文件描述符集合
				writefds:写文件描述符集合
				exceptfds:其他或者异常的文件描述符集合
				timeout:超时
					NULL 阻塞
			返回值:
				成功:准备就绪的文件描述符的个数
				失败:-1

		void FD_ZERO(fd_set *set);
			清空一个集合set
		
		void FD_SET(int fd, fd_set *set);
			将文件描述符fd添加到集合set里面
		
		void FD_CLR(int fd, fd_set *set);
			将文件描述符fd从集合set里面移除
		
		int  FD_ISSET(int fd, fd_set *set);
			判断文件描述符fd是否在集合里面
	============================================================
	poll
		#include <poll.h>
		int poll(struct pollfd *fds, nfds_t nfds, int timeout);
			参数及使用方式:
				fds:结构体数组
					struct pollfd {
						int   fd;         文件描述符
						short events;  请求的事件 
							POLLIN		普通或优先级带数据可读
							POLLRDNORM	普通数据可读
							POLLRDBAND	优先级带数据可读
							POLLPRI		高优先级数据可读
							POLLOUT		普通数据可写
							POLLWRNORM	普通数据可写
							POLLWRBAND	优先级带数据可写
							POLLERR		发生错误
							POLLHUP		发生挂起
							POLLNVAL	描述字不是一个打开的文件
						short revents;    返回的事件
					};
				nfds:文件描述符的个数
				timeout:超时
					<0	永远等待
					0	立即返回,不阻塞进程
					>0	等待指定数目的毫秒数
			返回值:
				成功:准备就绪的文件描述符的个数
				失败:-1
================================================================
网络超时检测
	阻塞函数本质
		如果缓冲区或者文件没有数据写入,则会一直阻塞下去,直到有数据为止
	非阻塞本质
		轮询的查看缓冲区或者文件是否有数据,如果有则立即执行,如果没有,则立即返回,接着运行
	============================================================
	套接字接收超时检测
		#include <sys/socket.h>
		//获取套接字属性
		int getsockopt(int socket, int level, int option_name, void *restrict option_value, socklen_t *restrict option_len);
			参数及使用方式:
				socket:文件描述符
				level:协议层次
					SOL_SOCKET 套接字层次
					IPPROTO_IP IP层次
					IPPROTO_TCP tcp层次
				option_name:选项的名称(套接字层次)
					SO_BROADCAST 是否允许发送广播信息
					SO_REUSEADDR 是否允许重复使用本地地址
					SO_SNDBUF 获取发送缓冲区大小
					SO_RCVBUF 获取接收缓冲区大小
					SO_RCVTIMEO 获取接收超时时间
					SO_SNDTIMEO 获取发送超时时间
				option_value:获取到的值
				option_len:获取到的值的长度
			返回值:
				成功:0
				失败:-1
		
		//设置套接字属性
		int setsockopt(int socket, int level, int option_name,
              const void *option_value, socklen_t option_len);
			参数及使用方式:
				socket:文件描述符
				level:协议层次
					SOL_SOCKET 套接字层次
				option_name:选项的名称(套接字层次)
					SO_RCVTIMEO 设置接收超时时间
				option_value:获取到的值
					struct timeval
					{
						__time_t tv_sec;        秒
						__suseconds_t tv_usec;  微秒
					};     
				option_len:获取到的值的长度
			返回值:
				成功:0
				失败:-1
	============================================================
	select函数实现网络超时检测
		#include <sys/select.h>
		#include <sys/time.h>
		#include <sys/types.h>
		#include <unistd.h>
		
		int select(int nfds, fd_set *readfds, fd_set *writefds,
				fd_set *exceptfds, struct timeval *timeout);
			同步一个io操作,允许一个程序操作多个文件描述符,阻塞等待文件
			描述符准备就绪,如果有一个或者多个文件描述符准备就绪,函数立即
			返回,并执行相应的IO操作
			
			参数及使用方式:
				nfds:大的文件描述符加1
				readfds:读文件描述符集合
				writefds:写文件描述符集合
				exceptfds:其他或者异常的文件描述符集合
				timeout:超时
					struct timeval
					{
						__time_t tv_sec;        秒
						__suseconds_t tv_usec;  微秒
					}; 
					
					NULL 阻塞
					0    非阻塞
					>0   设置的超时时间
			返回值:
				成功:准备就绪的文件描述符的个数
				失败:-1
	============================================================
	使用alarm闹钟SIGALARM信号实现网络超时检测
		#include <signal.h>
		int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);
			修改信号行为
			
			参数及使用方式:
				signum:信号
				act:新的行为
				oldact:旧的行为
					struct sigaction {
					   void     (*sa_handler)(int); 信号处理函数
					   void     (*sa_sigaction)(int, siginfo_t *, void *); 信号处理函数
					   sigset_t   sa_mask; 掩码(关于阻塞)
					   int        sa_flags; 标志位
							SA_RESTART 自重启属性
					   void     (*sa_restorer)(void);  没有用
				   };
			返回值:
				成功:0
				失败:-1


			注意:对寄存器或者位的操作,一般执行读、改、写三步
			
				第一步:读取信号的行为
				struct sigaction act;
				if(sigaction(SIGALRM, NULL, &act) < 0)
				{ 
					perror("fail to sigaction");
					exit(1);
				}
				
				第二步:修改信号的行为
				act.sa_handler = handler;
				act.sa_flags = act.sa_flags & (~SA_RESTART);
				
				第三步:将新的行为写回去
				if(sigaction(SIGALRM, &act, NULL) < 0)
				{
					perror("fail to sigaction");
					exit(1);
				}
			注意:
				阻塞函数在闹钟时间到的时候会被打断,返回错误信息,erron被设置成EINTR,
				可以根据errno判断是不是超时,进行后续操作
============================================================
广播
	前面介绍的数据包发送方式只有一个接受方,称为单播
	如果同时发给局域网中的所有主机,称为广播
	只有用户数据报(使用UDP协议)套接字才能广播
	
	广播方式发给所有的主机。过多的广播会大量占用网络带宽,造成广播风暴,影响正常的通信。

	广播地址
		以192.168.1.0 (255.255.255.0) 网段为例,
		大的主机地址192.168.1.255代表该网段的广播地址
		发到该地址的数据包被所有的主机接收
		255.255.255.255在所有网段中都代表广播地址
	
	流程
		发送者:
			创建套接字 socket( )
			填充广播信息结构体 sockaddr_in
			设置为发送广播权限 setsockopt( )
			发送数据 sendto( )
		
		接收者:
			创建套接字 socket( )
			填充广播信息结构体 sockaddr_in
			将套接字与广播信息结构体绑定 bind( )
			接收数据 recvfrom( )
		=======================================================================
		接收端:类似于服务器端
			//创建套接字
			int socketfd = socket(AF_INET, SOCK_DGRAM, 0);
			
			//填充广播信息结构体
			struct sockaddr_in addr;
			addr.sin_family = AF_INET;
			addr.sin_port = htons(9999);
			//从广播地址接收消息
			addr.sin_addr.s_addr = inet_addr("192.168.1.255");
			
			bind(socketfd, (struct sockaddr *)&addr, sizeof(addr));
			
			struct sockaddr_in client_addr;
			socklen_t client_addr_len = sizeof(client_addr);
			//接收数据
			recvfrom(socketfd, buf, sizeof(buf), 0, (struct sockaddr *)&client_addr, &client_addr_len);
			sendto(socketfd, buf, sizeof(buf), 0, (struct sockaddr *)&client_addr, client_addr_len);
		=======================================================================
		发送端:类似于客户端
			//创建套接字
			int socketfd = socket(AF_INET, SOCK_DGRAM, 0);
			//填充网络信息结构体
			struct sockaddr_in serveraddr;
			serveraddr.sin_family = AF_INET;
			serveraddr.sin_port = htons(9999);
			//朝广播地址发送消息
			serveraddr.sin_addr.s_addr = inet_addr("192.168.1.255");
			//套接字默认不允许发送广播包,通过修改SO_BROADCAST选项使其允许发送
			int on = 1;
			setsockopt(socketfd, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on));
			//知道对方的ip和port之后,可以直接发送消息
			sendto(socketfd, buf, sizeof(buf), 0, (struct sockaddr *)&serveraddr, sizeof(serveraddr));
============================================================
组播
	单播方式只能发给一个接收方。
	广播方式发给所有的主机。过多的广播会大量占用网络带宽,造成广播风暴,影响正常的通信。
	组播(又称为多播)是一种折中的方式。只有加入某个多播组的主机才能收到数据。
	多播方式既可以发给多个主机,又能避免象广播那样带来过多的负载(每台主机要到传输层才能判断广播包是否要处理)
	========================================================
	组播地址
		D类地址(组播地址)
		不分网络地址和主机地址,第1字节的前4位固定为1110
		224.0.0.1 – 239.255.255.255


	流程
		发送者:
			创建套接字 socket( )
			填充组播信息结构体 sockaddr_in
			发送数据 sendto( )
		接收者:
				创建套接字 socket( )
				填充组播信息结构体 sockaddr_in
				将套接字与组播信息结构体绑定 bind( )
				设置为加入多播组 setsockopt( ) 
				接收数据 recvfrom( )
		=======================================================================
		接收端:类似于服务器端
			//创建套接字
			int socketfd = socket(AF_INET, SOCK_DGRAM, 0);
			
			//加入多播组
			struct ip_mrep mrep;
			bzero(&mrep, sizeof(mrep));
			mrep.imr_multiaddr.s_addr = inet_addr("192.168.1.255");
			//多个网卡可以由系统自动分配使用一个地址
			mrep.imr_interface.s_addr = htonl(INADDR_ANY);
			set(socketfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mrep, sizeof(mrep));
			
			
			//填充广播信息结构体
			struct sockaddr_in addr;
			addr.sin_family = AF_INET;
			addr.sin_port = htons(9999);
			//从组播地址接收消息
			addr.sin_addr.s_addr = inet_addr("192.168.1.255");
			
			bind(socketfd, (struct sockaddr *)&addr, sizeof(addr));
			
			struct sockaddr_in client_addr;
			socklen_t client_addr_len = sizeof(client_addr);
			//接收数据
			recvfrom(socketfd, buf, sizeof(buf), 0, (struct sockaddr *)&client_addr, &client_addr_len);
			sendto(socketfd, buf, sizeof(buf), 0, (struct sockaddr *)&client_addr, client_addr_len);
		=======================================================================
		发送端:类似于客户端
			//创建套接字
			int socketfd = socket(AF_INET, SOCK_DGRAM, 0);
			//填充网络信息结构体
			struct sockaddr_in serveraddr;
			serveraddr.sin_family = AF_INET;
			serveraddr.sin_port = htons(9999);
			//朝组播地址发送消息
			serveraddr.sin_addr.s_addr = inet_addr("224.0.0.1");
			//知道对方的ip和port之后,可以直接发送消息
			sendto(socketfd, buf, sizeof(buf), 0, (struct sockaddr *)&serveraddr, sizeof(serveraddr
============================================================================
unix域套接字
	socket同样可以用于本地通信
	创建套接字时使用本地协议PF_UNIX(或PF_LOCAL)。
	分为流式套接字和用户数据报套接字
	和其他进程间通信方式相比使用方便、效率更高
	常用于前后台进程通信
	
	本地信息结构体 sockaddr_un
		#include <sys/un.h>
		
		struct sockaddr_un
		{
			sa_family_t sun_family; 协议族 AF_UNIX
			char sun_path[108];  路径名,会生成一个套接字文件,用于通信
		};
	=======================================================================
	TCP
		=======================================================================
		服务器端
			创建套接字socket
			int socketfd = socket(AF_UNIX, SOCK_STREAM, 0);
			
			struct sockaddr_un addr;
			addr.sun_family = AF_UNIX;
			strcpy(addr.sun_path, "套接字文件名");
			
			绑定本地信息
			bind(socketfd, (struct sockaddr *)&addr, sizeof(addr));
			
			设置监听
			listen(&socketfd, 5);
			
			int client_socketfd;
			struct sockaddr_in client_addr;
			socklen_t client_addr_len = sizeof(client_addr);
			
			接收来自connect的请求
			client_socketfd = accept(socketfd, (struct sockaddr *)&client_addr, &client_addr_len);
		=======================================================================
		客户端
			创建套接字socket
			int socketfd = socket(AF_INET, SOCK_STREAM, 0);
			
			struct sockaddr_un serveraddr;
			serveraddr.sun_family = AF_UNIX;
			strcpy(addr.sun_path, "套接字文件名");
			
			连接服务器,建立通信
			connect(socketfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr));
	=======================================================================
	UDP
		=======================================================================
		服务器端
			int socketfd = socket(AF_UNIX, SOCK_DGRAM, 0);
			
			struct sockaddr_un addr;
			addr.sun_family = AF_UNIX;
			strcpy(addr.sun_path, "套接字文件名");
			
			//为了一开始客户端可以有一个确定的发送对象,可以向这个ip这个port发送信息
			bind(socketfd, (struct sockaddr *)&addr, sizeof(addr));
			
			struct sockaddr_in client_addr;
			socklen_t client_addr_len = sizeof(client_addr);
			
			recvfrom(socketfd, buf, sizeof(buf), 0, (struct sockaddr *)&client_addr, &client_addr_len);
			sendto(socketfd, buf, sizeof(buf), 0, (struct sockaddr *)&client_addr, client_addr_len);
		=======================================================================
		客户端
			int socketfd = socket(AF_UNIX, SOCK_DGRAM, 0);
			
			//服务器地址
			struct sockaddr_un serveraddr;
			serveraddr.sun_family = AF_UNIX;
			strcpy(addr.sun_path, "套接字文件名");
			
			//如果服务器想要回传数据,必须告诉服务器client使用的套接字
			struct sockaddr_un clientaddr;
			clientaddr.sun_family = AF_UNIX;
			strcpy(clientaddr.sun_path, "client_sock");
			//如果客户端想要接收服务器端的回传信息,必须绑定相应的套接字
			bind(socketfd, (struct sockaddr *)&clientaddr, sizeof(clientaddr));
			
			//知道对方的ip和port之后,可以直接发送消息
			sendto(socketfd, buf, sizeof(buf), 0,(struct sockaddr *)&serveraddr, sizeof(serveraddr));
			recvfrom(socketfd, buf, sizeof(buf), 0, NULL, NULL);
============================================================================
数据库
	=========================================================
	命令
		sqlite3  stu.db 创建数据库
		系统命令 以 "."开头
		普通命令 ,以";"结束 

		.schema  查看表的结构
		.quit    退出数据库
		.exit    退出数据库
		.help    查看帮助信息
		.databases 查看数据库
		.tables  显示数据库中所有的表的表名

	=========================================================
	API 函数接口
		int sqlite3_open(const char *filename, sqlite3 **ppDb);
			参数及使用方式:
				filename   数据库名字
				ppdb      操作数据库的指针,句柄。
			返回值:
				成功 SQLITE_OK  
				失败 error_code
		
		const char *sqlite3_errmsg(sqlite3* db);
			功能:获取错误信息描述
		
		int sqlite3_close(sqlite3* db);
			关闭一个数据库
		
		int sqlite3_exec(sqlite3* db,  const char *sql,  int (*callback)(void*,int,char**,char**), void * arg,  char **errmsg );
			参数及使用方式:
				db  数据库的句柄指针
				sql  将要被执行sql语句
				callback 回调函数, 只有在查询语句时,才给回调函数传参
				arg  为callback 传参的
				errmsg 错误信息的地址
			返回值:成功 SQLITE_OK
					出错 errcode 错误码
		
		int (*callback)(void* arg ,int  ncolumn ,char** f_value,char** f_name);
			对sqlite3_exec返回的结果集,每行都调用一次回调函数
			参数及使用方式:
				arg  为回调函数传递参数使用的,可以用来获取返回值
				ncolumn  记录中包含的字段的数目
				f_value  包含每个字段值的指针数组,存储字段值数组的地址
					char *value = "value";
					char **f_value = &value;
				f_name   包含每个字段名称的指针数组,存储字段名称数组的地址
					char *name = "name";
					char **f_name = &name;
			返回值:
				成功 0
				出错 非0
		=====================================================================
		获取查询结果集
			结果集以二维数组的形式返回,可以像一维数组一样访问数据
			int sqlite3_get_table(sqlite3 *db, const char *zSql, char ***pazResult, int *pnRow, int *pnColumn, char **pzErrmsg );
				参数及使用方式:
					db       数据库操作句柄
					sql      数据库的sql语句
					azResult 查询的结果,二维数组地址
					nRow     行数,不包含表头的数量
					nColumn  列数
					errmsg   错误消息
				返回值:
					成功 0
					出错 errcode

			void sqlite3_free_table(char **result);
				功能:释放内存

前台专线:010-82525158 企业培训洽谈专线:010-82525379 院校合作洽谈专线:010-82525379 Copyright © 2004-2018 北京华清远见科技发展有限公司 版权所有 ,京ICP备16055225号,京公海网安备11010802025203号
返回

学员笔记

星创客 - 华清远见旗下高端IT培训品牌

当前位置: 星创客 > 学习资源 > 学员笔记 >

Linux下网络编程学习笔记
来源: 星创客 作者: 星创客 时间:2017-11-09

网络编程 ======================================================================= OSI开放系统互联模型 应用层: 应用程序:FTP、E-mail、Telnet 表示层: 数据格式定义、数据转换/加密 会话层: 建立通信进程的...

网络编程
	=======================================================================
	OSI开放系统互联模型
		应用层:
			应用程序:FTP、E-mail、Telnet
		表示层:
			数据格式定义、数据转换/加密	
		会话层:
			建立通信进程的逻辑名字与物理名字之间的联系 	
		传输层:
			差错处理/恢复,流量控制,提供可靠的数据传输	
		网络层:
			数据分组、路由选择
		数据链路层:
			数据组成可发送、接收的帧
		物理层:
			传输物理信号、接口、信号形式、速率
	=======================================================================
	7层通信
		(1)应用层:
				指定特定应用的协议
				(比如发送和接受文件的软件按钮,发送者输入“早上好”并附上收件人,
				按下发送按钮,接受者收到信息会将其存储在硬盘或者非易失存储器
				(数据不会因为断电而丢失的一种存储设备)上,这些都是在应用层上的)
				
		(2)表示层:
				设备固有数据格式和网络标准数据格式的转换
				(接受者和发送者如果使用的邮件客户端不一样,
				那么就会出现问题,如何实现用户之间的通信,那么就需要在表示层来起作用,
				使得在不同的客户端上拥有相同的网络格式)
				
		(3)会话层:
				通信管理,负责建立或者断开通信连接
				(发送者一次性发送5份邮件,那么接受者如何接受,
				是一次性接受所有的文件然后断开连接还是没接受一次就断开,然后在此进行,发送者同理)
				
		(4)传输层:
				管理两个节点(互联的网络中断)之间的数据传输。
				负责可靠传输(确保数据被可靠地传送到目标地址)
				(确保发送者和接受者之间的通信,会话层负责决定建立连接和断开连接的时机,
				而传输层进行实际的建立和断开处理)
				
		(5)网络层:
				地址管理与路由选择,作用:在网络相互连接的环境中,
				将数据从发送端主机发送到接受端主机
				
		(6)数据链路层:
				互连设备之间传送和识别数据帧
				
		(7)物理层:
				以“0”、“1”代表的电压的高低、灯光的闪灭。界定连接器和网络的规格。
	
	=======================================================================
	TCP/IP协议族:
		传输控制/网际协议(Transfer Control Protocol/Internet Protocol) 又称作网络通讯协议
		
		应用层	TFTP,HTTP,SNMP,FTP,SMTP,DNS,Telnet 
		传输层	TCP,UDP 
		网络层	IP,ICMP,RIP,OSPF,BGP,IGMP 
		网络接口与物理层	SLIP,CSLIP,PPP,ARP,RARP,MTU ISO2110,IEEE802.1,EEE802.2 
		
		TCP(Transport Control Protocol)传输控制协议
		IP(Internetworking Protocol)网间协议	
		UDP(User Datagram Protocol)用户数据报协议	
		SMTP(Simple Mail Transfer Protocol)简单邮件传输协议	
		HTTP(Hypertext Transfer Protocol) 超文本传输协议	
		FTP(File Transfer Protocol)文件传输协议	
		ARP(Address Resolution Protocol)地址解析协议

	=======================================================================
	socket的分类
			流式套接字(SOCK_STREAM)
				提供了一个面向连接、可靠的数据传输服务,数据无差错、无重复的发送且按发送顺序接收。
				内设置流量控制,避免数据流淹没慢的接收方。数据被看作是字节流,无长度限制。
				==>TCP
				
			数据报套接字(SOCK_DGRAM)
				提供无连接服务。数据包以独立数据包的形式被发送,不提供无差错保证,
				数据可能丢失或重复,顺序发送,可能乱序接收。
				==>UDP
				
			原始套接字(SOCK_RAW)
				可以对较低层次协议如IP、ICMP直接访问。
	=======================================================================
	IP地址的分类(依据ipv4的前八位来区分)
			A类	0000 0000 - 0111 1111  0.x.x.x - 127.x.x.x
			B类	1000 0000 - 1011 1111  128.x.x.x - 191.x.x.x
			C类 1100 0000 - 1101 1111  192.x.x.x - 223.x.x.x
			D类	1110 0000 - 1110 1111  224.x.x.x - 239.x.x.x  表示组播地址
			E类	1111 0000 - 1111 1111  240.x.x.x - 255.x.x.x  属于保留测试
			
			127.x.x.x 表示主机地址
			192.168.x.x 表示局域网ip地址
			
			192.168.1.x为例
				192.168.1.0 表示网段、网络地址
				192.168.1.1 表示网关
				192.168.1.255 表示广播地址
			
		子网掩码 表示主机的大连接数
			A类  255.0.0.0    2~24
			B类  255.255.0.0  2~16
			C类  255.255.255.0  2~8
	=======================================================================
	字节序
		不同类型CPU的主机中,内存存储多字节整数序列有两种方法,称为主机字节序(HBO)
			小端序(little-endian) - 低序字节存储在低地址
				将低字节存储在起始地址,称为“Little-Endian”字节序,Intel、AMD等采用的是这种方式
			大端序(big-endian)- 高序字节存储在低地址
				将高字节存储在起始地址,称为“Big-Endian”字节序,由ARM、Motorola等所采用

		网络中传输的数据必须按网络字节序,即大端字节序
	=======================================================================
	IP地址和端口的转换
		#include <arpa/inet.h>
		in_addr_t inet_addr(const char *cp);
			将点分十进制IP地址转化为网络字节序的整型数据
		char *inet_ntoa(struct in_addr in);
			将网络字节序的整型数据转化为点分十进制IP地址
		
		
		#include <arpa/inet.h>
		uint32_t htonl(uint32_t hostlong);
		uint16_t htons(uint16_t hostshort);
			将主机字节序转化为网络字节序
		
		uint32_t ntohl(uint32_t netlong);
		uint16_t ntohs(uint16_t netshort);
			将网络字节序转化为主机字节序
=======================================================================
TCP网络编程
	比起udp,tcp的服务器端多出了listen和accept的操作
	为了确保通信的可靠性,需要先确认连接上,所以需要accept接收客户端的connect请求
	因为tcp已经确定了稳定的连接,所以可以得到固定的文件描述符用来操作发送和接受信息
	udp只能每次都通过地址发送
	=======================================================================
	服务器端
		创建套接字socket
		int socketfd = socket(AF_INET, SOCK_STREAM, 0);
		
		struct sockaddr_in addr;
		addr.sin_family = AF_INET;
		addr.sin_port = htons(9999);
		addr.sin_addr.s_addr = inet_addr("192.168.1.100");
		
		绑定网络信息
		bind(socketfd, (struct sockaddr *)&addr, sizeof(addr));
		
		设置监听
		listen(&socketfd, 5);
		
		int client_socketfd;
		struct sockaddr_in client_addr;
		socklen_t client_addr_len = sizeof(client_addr);
		
		接收来自connect的请求
		client_socketfd = accept(socketfd, (struct sockaddr *)&client_addr, &client_addr_len);
	=======================================================================
	客户端
		创建套接字socket
		int socketfd = socket(AF_INET, SOCK_STREAM, 0);
		
		struct sockaddr_in serveraddr;
		serveraddr.sin_family = AF_INET;
		serveraddr.sin_port = htons(9999);
		serveraddr.sin_addr.s_addr = inet_addr("192.168.1.100");
		
		连接服务器,建立通信
		connect(socketfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr));
=======================================================================
UDP网络编程
	服务器端只是比客户端多了一个bind的操作,需要先确定一个目标,客户端才可以准确的发来数据
	=======================================================================
	服务器端
		int socketfd = socket(AF_INET, SOCK_DGRAM, 0);
		
		struct sockaddr_in addr;
		addr.sin_family = AF_INET;
		addr.sin_port = htons(9999);
		addr.sin_addr.s_addr = inet_addr("192.168.1.100");
		
		//为了一开始客户端可以有一个确定的发送对象,可以向这个ip这个port发送信息
		bind(socketfd, (struct sockaddr *)&addr, sizeof(addr));
		
		struct sockaddr_in client_addr;
		socklen_t client_addr_len = sizeof(client_addr);
		
		recvfrom(socketfd, buf, sizeof(buf), 0, (struct sockaddr *)&client_addr, &client_addr_len);
		sendto(socketfd, buf, sizeof(buf), 0, (struct sockaddr *)&client_addr, client_addr_len);
	=======================================================================
	客户端
		int socketfd = socket(AF_INET, SOCK_DGRAM, 0);
		
		struct sockaddr_in serveraddr;
		serveraddr.sin_family = AF_INET;
		serveraddr.sin_port = htons(9999);
		serveraddr.sin_addr.s_addr = inet_addr("192.168.1.100");
		
		//知道对方的ip和port之后,可以直接发送消息
		sendto(socketfd, buf, sizeof(buf), 0, (struct sockaddr *)&serveraddr, sizeof(serveraddr));
=======================================================================
网络编程方法定义
	==============================================================
	创建套接字socket()
		#include <sys/types.h>          /* See NOTES */
		#include <sys/socket.h>
		int socket(int domain, int type, int protocol);
			参数及使用方式:
				domain:通信域,协议族
					AF_UNIX 本地通信
					AF_INET ipv4网络协议
					AF_INET6 ipv6网络协议
					AF_PACKET 底层的协议
				type:类型
					SOCK_STREAM 流式套接字 tcp
					SOCK_DGRAM 数据报套接字 UDP
					SOCK_RAW 底层的通信
				protocol:一般为0
			返回值:
				成功:文件描述符serversocketfd
				失败:-1
	==============================================================
	绑定网络地址bind()
		#include <sys/types.h>          /* See NOTES */
		#include <sys/socket.h>
		int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
			参数及使用方式:
				sockfd:文件描述符,serversocket
				addrlen:addr的长度,serveraddr_len
				addr:网络信息结构体 serveraddr
					通用的(一般不用)
						struct sockaddr {
							sa_family_t sa_family;  2个字节
							char        sa_data[14];  14个字节
						}
					常用:sockaddr_in
					#include <netinet/in.h>
					struct sockaddr_in{
						sa_family_t sin_family;  地址族 AF_INET  2个字节
						in_port_t sin_port;   端口号 2个字节
						struct in_addr sin_addr;
							struct in_addr{
									in_addr_t s_addr;  ip地址  4个字节
							};
						//这个没有用,为了保证sockaddr与sockaddr_in一样大
						unsigned char sin_zero[sizeof (struct sockaddr) -
								__SOCKADDR_COMMON_SIZE -
								sizeof (in_port_t) -
								sizeof (struct in_addr)]; 
					}
				返回值:
					成功:0
					失败:-1
				例子:
					struct sockaddr_in serveraddr;
					
					serveraddr.sin_family = AF_INET;
					serveraddr.sin_port = htons(9999);
					serveraddr.sin_addr.s_addr = inet_addr("192.168.1.123");
					bind(sockfd, (struct sockaddr *)&serveraddr, sizeof(struct sockaddr_in))
	==============================================================
	设置监听listen()
		#include <sys/types.h>          /* See NOTES */
		#include <sys/socket.h>
		int listen(int sockfd, int backlog);
			参数及使用方式:
				sockfd:文件描述符,serversocket
				backlog:允许同时响应客户端请求的个数,一般为5, 10
			返回值:
				成功:0
				失败:-1
	==============================================================
	阻塞等待客户端的连接请求accept()
		#include <sys/types.h>          /* See NOTES */
		#include <sys/socket.h>
		int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
			参数及使用方式:
				sockfd:文件描述符,serversocket
				addr:网络信息结构体client_addr
				addrlen:client_addr_len
			返回值:
				成功:客户端文件描述符,client_socket
				失败:-1
	==============================================================
	客户端连接服务器connect()
		#include <sys/types.h>
		#include <sys/socket.h>
		int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
			参数及使用方式:
				sockfd:文件描述符,client_sock
				addr:服务器地址
				addrlen:addr的长度
			返回值:
				成功:0
				失败:-1
	==============================================================
	tcp发送信息send()
		#include <sys/types.h>
		#include <sys/socket.h>
		ssize_t send(int sockfd, const void *buf, size_t len, int flags);
			参数及使用方式:
				sockfd:文件描述符
					服务器:使用accept的client——sock
					客户端:自己的socket
				buf:发送的数据
				len:数据的长度
				flags:标志位
					0  阻塞
					MSG_DONTWAIT 非阻塞
			返回值:
				成功:发送的数据的长度
				失败:-1

	==============================================================
	tcp接收信息recv()
		#include <sys/types.h>
		#include <sys/socket.h>
		ssize_t recv(int sockfd, void *buf, size_t len, int flags);
			参数及使用方式:
				sockfd:文件描述符
					服务器:使用accept的client——sock
					客户端:自己的socket
				buf:用于存放接收的数据
				len:接收指定长度的直接
				flags:标志位
					0  阻塞
					MSG_DONTWAIT 非阻塞
			返回值:
				成功:发送的数据的长度
					0 发送者关闭文件描述符或者退出
				失败:-1

	===============================================================
	udp发送消息sendto()
		#include <sys/socket.h>
		ssize_t sendto(int socket, const void *message, size_t length,
			int flags, const struct sockaddr *dest_addr, socklen_t dest_len);
			参数及使用方式:
				socket:文件描述符
				message:发送的数据
				length:数据的长度
				flags:标志位,一般为0
				dest_addr:目的地址(发送给谁)
				dest_len:addr的长度
			返回值:
				成功:发送的数据的长度
				失败:-1
	===============================================================
	udp接收消息recvfrom()
		#include <sys/types.h>
		#include <sys/socket.h>
		ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
			struct sockaddr *src_addr, socklen_t *addrlen);
			参数及使用方式:
				sockfd:文件描述符
				buf:接收的数据
				len:数据的长度
				flags:标志位,一般为0
				src_addr:源的地址(自动填充)
				addrlen:addr的长度
			返回值:
				成功:接收的数据的长度
				失败:-1
================================================================
IO模型
	在UNIX/Linux下主要有4种I/O 模型:
		阻塞I/O:
			常用、简单、效率低
		非阻塞I/O:
			可防止进程阻塞在I/O操作上,需要轮询
		I/O 多路复用:
			允许同时对多个I/O进行控制
		信号驱动I/O:
			一种异步通信模型
	================================================================
	fcntl非阻塞IO
		#include <unistd.h>
		#include <fcntl.h>
		int fcntl(int fd, int cmd, ... /* arg */ );
			参数及使用方式:
				fd:要操作文件描述符
				cmd:命令
					F_GETFL 获取文件状态标志位
					F_SETFL 设置文件状态标志位
							O_NONBLOCK 非阻塞
				arg:可变参
					根据需求选择需不需要
			返回值:
				成功:
					F_GETFL 获取到的文件状态标志位
					F_SETFL 0
				失败:
					-1
			例子:
				注意:对寄存器或者位的操作,一般执行读、改、写三步
		
				第一步:获取文件状态标志位
				int flags;
				if((flags = fcntl(0, F_GETFL)) < 0)
				{
					perror("fail to fcntl");
					return -1;
				}
				
				第二步:修改标志位
				flags = flags | O_NONBLOCK;
				
				第三步:将新的标志位写回去
				if(fcntl(0, F_SETFL, flags) < 0)
				{
					perror("fail to fcntl");
					return -1;
				}
================================================================
IO多路复用
	============================================================
	select
		#include <sys/select.h>
		#include <sys/time.h>
		#include <sys/types.h>
		#include <unistd.h>

		int select(int nfds, fd_set *readfds, fd_set *writefds,
				fd_set *exceptfds, struct timeval *timeout);
			参数及使用方式:
				nfds:大的文件描述符加1
				readfds:读文件描述符集合
				writefds:写文件描述符集合
				exceptfds:其他或者异常的文件描述符集合
				timeout:超时
					NULL 阻塞
			返回值:
				成功:准备就绪的文件描述符的个数
				失败:-1

		void FD_ZERO(fd_set *set);
			清空一个集合set
		
		void FD_SET(int fd, fd_set *set);
			将文件描述符fd添加到集合set里面
		
		void FD_CLR(int fd, fd_set *set);
			将文件描述符fd从集合set里面移除
		
		int  FD_ISSET(int fd, fd_set *set);
			判断文件描述符fd是否在集合里面
	============================================================
	poll
		#include <poll.h>
		int poll(struct pollfd *fds, nfds_t nfds, int timeout);
			参数及使用方式:
				fds:结构体数组
					struct pollfd {
						int   fd;         文件描述符
						short events;  请求的事件 
							POLLIN		普通或优先级带数据可读
							POLLRDNORM	普通数据可读
							POLLRDBAND	优先级带数据可读
							POLLPRI		高优先级数据可读
							POLLOUT		普通数据可写
							POLLWRNORM	普通数据可写
							POLLWRBAND	优先级带数据可写
							POLLERR		发生错误
							POLLHUP		发生挂起
							POLLNVAL	描述字不是一个打开的文件
						short revents;    返回的事件
					};
				nfds:文件描述符的个数
				timeout:超时
					<0	永远等待
					0	立即返回,不阻塞进程
					>0	等待指定数目的毫秒数
			返回值:
				成功:准备就绪的文件描述符的个数
				失败:-1
================================================================
网络超时检测
	阻塞函数本质
		如果缓冲区或者文件没有数据写入,则会一直阻塞下去,直到有数据为止
	非阻塞本质
		轮询的查看缓冲区或者文件是否有数据,如果有则立即执行,如果没有,则立即返回,接着运行
	============================================================
	套接字接收超时检测
		#include <sys/socket.h>
		//获取套接字属性
		int getsockopt(int socket, int level, int option_name, void *restrict option_value, socklen_t *restrict option_len);
			参数及使用方式:
				socket:文件描述符
				level:协议层次
					SOL_SOCKET 套接字层次
					IPPROTO_IP IP层次
					IPPROTO_TCP tcp层次
				option_name:选项的名称(套接字层次)
					SO_BROADCAST 是否允许发送广播信息
					SO_REUSEADDR 是否允许重复使用本地地址
					SO_SNDBUF 获取发送缓冲区大小
					SO_RCVBUF 获取接收缓冲区大小
					SO_RCVTIMEO 获取接收超时时间
					SO_SNDTIMEO 获取发送超时时间
				option_value:获取到的值
				option_len:获取到的值的长度
			返回值:
				成功:0
				失败:-1
		
		//设置套接字属性
		int setsockopt(int socket, int level, int option_name,
              const void *option_value, socklen_t option_len);
			参数及使用方式:
				socket:文件描述符
				level:协议层次
					SOL_SOCKET 套接字层次
				option_name:选项的名称(套接字层次)
					SO_RCVTIMEO 设置接收超时时间
				option_value:获取到的值
					struct timeval
					{
						__time_t tv_sec;        秒
						__suseconds_t tv_usec;  微秒
					};     
				option_len:获取到的值的长度
			返回值:
				成功:0
				失败:-1
	============================================================
	select函数实现网络超时检测
		#include <sys/select.h>
		#include <sys/time.h>
		#include <sys/types.h>
		#include <unistd.h>
		
		int select(int nfds, fd_set *readfds, fd_set *writefds,
				fd_set *exceptfds, struct timeval *timeout);
			同步一个io操作,允许一个程序操作多个文件描述符,阻塞等待文件
			描述符准备就绪,如果有一个或者多个文件描述符准备就绪,函数立即
			返回,并执行相应的IO操作
			
			参数及使用方式:
				nfds:大的文件描述符加1
				readfds:读文件描述符集合
				writefds:写文件描述符集合
				exceptfds:其他或者异常的文件描述符集合
				timeout:超时
					struct timeval
					{
						__time_t tv_sec;        秒
						__suseconds_t tv_usec;  微秒
					}; 
					
					NULL 阻塞
					0    非阻塞
					>0   设置的超时时间
			返回值:
				成功:准备就绪的文件描述符的个数
				失败:-1
	============================================================
	使用alarm闹钟SIGALARM信号实现网络超时检测
		#include <signal.h>
		int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);
			修改信号行为
			
			参数及使用方式:
				signum:信号
				act:新的行为
				oldact:旧的行为
					struct sigaction {
					   void     (*sa_handler)(int); 信号处理函数
					   void     (*sa_sigaction)(int, siginfo_t *, void *); 信号处理函数
					   sigset_t   sa_mask; 掩码(关于阻塞)
					   int        sa_flags; 标志位
							SA_RESTART 自重启属性
					   void     (*sa_restorer)(void);  没有用
				   };
			返回值:
				成功:0
				失败:-1


			注意:对寄存器或者位的操作,一般执行读、改、写三步
			
				第一步:读取信号的行为
				struct sigaction act;
				if(sigaction(SIGALRM, NULL, &act) < 0)
				{ 
					perror("fail to sigaction");
					exit(1);
				}
				
				第二步:修改信号的行为
				act.sa_handler = handler;
				act.sa_flags = act.sa_flags & (~SA_RESTART);
				
				第三步:将新的行为写回去
				if(sigaction(SIGALRM, &act, NULL) < 0)
				{
					perror("fail to sigaction");
					exit(1);
				}
			注意:
				阻塞函数在闹钟时间到的时候会被打断,返回错误信息,erron被设置成EINTR,
				可以根据errno判断是不是超时,进行后续操作
============================================================
广播
	前面介绍的数据包发送方式只有一个接受方,称为单播
	如果同时发给局域网中的所有主机,称为广播
	只有用户数据报(使用UDP协议)套接字才能广播
	
	广播方式发给所有的主机。过多的广播会大量占用网络带宽,造成广播风暴,影响正常的通信。

	广播地址
		以192.168.1.0 (255.255.255.0) 网段为例,
		大的主机地址192.168.1.255代表该网段的广播地址
		发到该地址的数据包被所有的主机接收
		255.255.255.255在所有网段中都代表广播地址
	
	流程
		发送者:
			创建套接字 socket( )
			填充广播信息结构体 sockaddr_in
			设置为发送广播权限 setsockopt( )
			发送数据 sendto( )
		
		接收者:
			创建套接字 socket( )
			填充广播信息结构体 sockaddr_in
			将套接字与广播信息结构体绑定 bind( )
			接收数据 recvfrom( )
		=======================================================================
		接收端:类似于服务器端
			//创建套接字
			int socketfd = socket(AF_INET, SOCK_DGRAM, 0);
			
			//填充广播信息结构体
			struct sockaddr_in addr;
			addr.sin_family = AF_INET;
			addr.sin_port = htons(9999);
			//从广播地址接收消息
			addr.sin_addr.s_addr = inet_addr("192.168.1.255");
			
			bind(socketfd, (struct sockaddr *)&addr, sizeof(addr));
			
			struct sockaddr_in client_addr;
			socklen_t client_addr_len = sizeof(client_addr);
			//接收数据
			recvfrom(socketfd, buf, sizeof(buf), 0, (struct sockaddr *)&client_addr, &client_addr_len);
			sendto(socketfd, buf, sizeof(buf), 0, (struct sockaddr *)&client_addr, client_addr_len);
		=======================================================================
		发送端:类似于客户端
			//创建套接字
			int socketfd = socket(AF_INET, SOCK_DGRAM, 0);
			//填充网络信息结构体
			struct sockaddr_in serveraddr;
			serveraddr.sin_family = AF_INET;
			serveraddr.sin_port = htons(9999);
			//朝广播地址发送消息
			serveraddr.sin_addr.s_addr = inet_addr("192.168.1.255");
			//套接字默认不允许发送广播包,通过修改SO_BROADCAST选项使其允许发送
			int on = 1;
			setsockopt(socketfd, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on));
			//知道对方的ip和port之后,可以直接发送消息
			sendto(socketfd, buf, sizeof(buf), 0, (struct sockaddr *)&serveraddr, sizeof(serveraddr));
============================================================
组播
	单播方式只能发给一个接收方。
	广播方式发给所有的主机。过多的广播会大量占用网络带宽,造成广播风暴,影响正常的通信。
	组播(又称为多播)是一种折中的方式。只有加入某个多播组的主机才能收到数据。
	多播方式既可以发给多个主机,又能避免象广播那样带来过多的负载(每台主机要到传输层才能判断广播包是否要处理)
	========================================================
	组播地址
		D类地址(组播地址)
		不分网络地址和主机地址,第1字节的前4位固定为1110
		224.0.0.1 – 239.255.255.255


	流程
		发送者:
			创建套接字 socket( )
			填充组播信息结构体 sockaddr_in
			发送数据 sendto( )
		接收者:
				创建套接字 socket( )
				填充组播信息结构体 sockaddr_in
				将套接字与组播信息结构体绑定 bind( )
				设置为加入多播组 setsockopt( ) 
				接收数据 recvfrom( )
		=======================================================================
		接收端:类似于服务器端
			//创建套接字
			int socketfd = socket(AF_INET, SOCK_DGRAM, 0);
			
			//加入多播组
			struct ip_mrep mrep;
			bzero(&mrep, sizeof(mrep));
			mrep.imr_multiaddr.s_addr = inet_addr("192.168.1.255");
			//多个网卡可以由系统自动分配使用一个地址
			mrep.imr_interface.s_addr = htonl(INADDR_ANY);
			set(socketfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mrep, sizeof(mrep));
			
			
			//填充广播信息结构体
			struct sockaddr_in addr;
			addr.sin_family = AF_INET;
			addr.sin_port = htons(9999);
			//从组播地址接收消息
			addr.sin_addr.s_addr = inet_addr("192.168.1.255");
			
			bind(socketfd, (struct sockaddr *)&addr, sizeof(addr));
			
			struct sockaddr_in client_addr;
			socklen_t client_addr_len = sizeof(client_addr);
			//接收数据
			recvfrom(socketfd, buf, sizeof(buf), 0, (struct sockaddr *)&client_addr, &client_addr_len);
			sendto(socketfd, buf, sizeof(buf), 0, (struct sockaddr *)&client_addr, client_addr_len);
		=======================================================================
		发送端:类似于客户端
			//创建套接字
			int socketfd = socket(AF_INET, SOCK_DGRAM, 0);
			//填充网络信息结构体
			struct sockaddr_in serveraddr;
			serveraddr.sin_family = AF_INET;
			serveraddr.sin_port = htons(9999);
			//朝组播地址发送消息
			serveraddr.sin_addr.s_addr = inet_addr("224.0.0.1");
			//知道对方的ip和port之后,可以直接发送消息
			sendto(socketfd, buf, sizeof(buf), 0, (struct sockaddr *)&serveraddr, sizeof(serveraddr
============================================================================
unix域套接字
	socket同样可以用于本地通信
	创建套接字时使用本地协议PF_UNIX(或PF_LOCAL)。
	分为流式套接字和用户数据报套接字
	和其他进程间通信方式相比使用方便、效率更高
	常用于前后台进程通信
	
	本地信息结构体 sockaddr_un
		#include <sys/un.h>
		
		struct sockaddr_un
		{
			sa_family_t sun_family; 协议族 AF_UNIX
			char sun_path[108];  路径名,会生成一个套接字文件,用于通信
		};
	=======================================================================
	TCP
		=======================================================================
		服务器端
			创建套接字socket
			int socketfd = socket(AF_UNIX, SOCK_STREAM, 0);
			
			struct sockaddr_un addr;
			addr.sun_family = AF_UNIX;
			strcpy(addr.sun_path, "套接字文件名");
			
			绑定本地信息
			bind(socketfd, (struct sockaddr *)&addr, sizeof(addr));
			
			设置监听
			listen(&socketfd, 5);
			
			int client_socketfd;
			struct sockaddr_in client_addr;
			socklen_t client_addr_len = sizeof(client_addr);
			
			接收来自connect的请求
			client_socketfd = accept(socketfd, (struct sockaddr *)&client_addr, &client_addr_len);
		=======================================================================
		客户端
			创建套接字socket
			int socketfd = socket(AF_INET, SOCK_STREAM, 0);
			
			struct sockaddr_un serveraddr;
			serveraddr.sun_family = AF_UNIX;
			strcpy(addr.sun_path, "套接字文件名");
			
			连接服务器,建立通信
			connect(socketfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr));
	=======================================================================
	UDP
		=======================================================================
		服务器端
			int socketfd = socket(AF_UNIX, SOCK_DGRAM, 0);
			
			struct sockaddr_un addr;
			addr.sun_family = AF_UNIX;
			strcpy(addr.sun_path, "套接字文件名");
			
			//为了一开始客户端可以有一个确定的发送对象,可以向这个ip这个port发送信息
			bind(socketfd, (struct sockaddr *)&addr, sizeof(addr));
			
			struct sockaddr_in client_addr;
			socklen_t client_addr_len = sizeof(client_addr);
			
			recvfrom(socketfd, buf, sizeof(buf), 0, (struct sockaddr *)&client_addr, &client_addr_len);
			sendto(socketfd, buf, sizeof(buf), 0, (struct sockaddr *)&client_addr, client_addr_len);
		=======================================================================
		客户端
			int socketfd = socket(AF_UNIX, SOCK_DGRAM, 0);
			
			//服务器地址
			struct sockaddr_un serveraddr;
			serveraddr.sun_family = AF_UNIX;
			strcpy(addr.sun_path, "套接字文件名");
			
			//如果服务器想要回传数据,必须告诉服务器client使用的套接字
			struct sockaddr_un clientaddr;
			clientaddr.sun_family = AF_UNIX;
			strcpy(clientaddr.sun_path, "client_sock");
			//如果客户端想要接收服务器端的回传信息,必须绑定相应的套接字
			bind(socketfd, (struct sockaddr *)&clientaddr, sizeof(clientaddr));
			
			//知道对方的ip和port之后,可以直接发送消息
			sendto(socketfd, buf, sizeof(buf), 0,(struct sockaddr *)&serveraddr, sizeof(serveraddr));
			recvfrom(socketfd, buf, sizeof(buf), 0, NULL, NULL);
============================================================================
数据库
	=========================================================
	命令
		sqlite3  stu.db 创建数据库
		系统命令 以 "."开头
		普通命令 ,以";"结束 

		.schema  查看表的结构
		.quit    退出数据库
		.exit    退出数据库
		.help    查看帮助信息
		.databases 查看数据库
		.tables  显示数据库中所有的表的表名

	=========================================================
	API 函数接口
		int sqlite3_open(const char *filename, sqlite3 **ppDb);
			参数及使用方式:
				filename   数据库名字
				ppdb      操作数据库的指针,句柄。
			返回值:
				成功 SQLITE_OK  
				失败 error_code
		
		const char *sqlite3_errmsg(sqlite3* db);
			功能:获取错误信息描述
		
		int sqlite3_close(sqlite3* db);
			关闭一个数据库
		
		int sqlite3_exec(sqlite3* db,  const char *sql,  int (*callback)(void*,int,char**,char**), void * arg,  char **errmsg );
			参数及使用方式:
				db  数据库的句柄指针
				sql  将要被执行sql语句
				callback 回调函数, 只有在查询语句时,才给回调函数传参
				arg  为callback 传参的
				errmsg 错误信息的地址
			返回值:成功 SQLITE_OK
					出错 errcode 错误码
		
		int (*callback)(void* arg ,int  ncolumn ,char** f_value,char** f_name);
			对sqlite3_exec返回的结果集,每行都调用一次回调函数
			参数及使用方式:
				arg  为回调函数传递参数使用的,可以用来获取返回值
				ncolumn  记录中包含的字段的数目
				f_value  包含每个字段值的指针数组,存储字段值数组的地址
					char *value = "value";
					char **f_value = &value;
				f_name   包含每个字段名称的指针数组,存储字段名称数组的地址
					char *name = "name";
					char **f_name = &name;
			返回值:
				成功 0
				出错 非0
		=====================================================================
		获取查询结果集
			结果集以二维数组的形式返回,可以像一维数组一样访问数据
			int sqlite3_get_table(sqlite3 *db, const char *zSql, char ***pazResult, int *pnRow, int *pnColumn, char **pzErrmsg );
				参数及使用方式:
					db       数据库操作句柄
					sql      数据库的sql语句
					azResult 查询的结果,二维数组地址
					nRow     行数,不包含表头的数量
					nColumn  列数
					errmsg   错误消息
				返回值:
					成功 0
					出错 errcode

			void sqlite3_free_table(char **result);
				功能:释放内存

相关推荐

全国咨询热线:400-611-6270

?2004-2018华清远见教育科技集团 版权所有 京ICP备16055225号 京公海网安备11010802025203号