进程间通信
网络通信
三要素:ip地址 + 协议 + port端口
分层结构
七层、四层
协议:http, smtp, ftp, dns, telnet, tcp, udp, ip, icmp, igmp, arp, rarp
tcp和udp
SYN ACK、FIN ACK
IO模型
阻塞、非阻塞、信号驱动、多路复用
open( fileName, O_RDONLY | O_NDELAY )
open( fileName, O_RDONLY | O_NONBLOCK )
socket( AF_INET, SOCK_STREAM/SOCK_DGRAM, 0 )
int fcntl( 文件描述符,命令,参数 );
命令:
F_GETFL : 获取当前文件的打开方式,第三个参数无意义。
F_SETFL : 设置新的打开方式,第三个参数就是新的打开方式。
int flag = fcntl( fd, F_GETFL, 0 );
flag &= ~O_NONBLOCK; //取消非阻塞
flag |= O_NONBLOCK; //修改为非阻塞
fcntl( fd, F_SETFL, flag );
信号驱动:
当文件的状态发生改变,通知内核,让内核给当前进程发送信号;
当前进程收到信号,处理信号,对该文件进行IO操作。
多路复用
应用场景:一个进程中有多个阻塞IO
将所有阻塞描述符进行监听,把阻塞集中到某一个地方,
只要有文件可以进行IO操作时,阻塞被解除,直接进行IO操作。
select特点:
1. 存放描述符的表需要来回拷贝
比如:在调用select时,由userSpace拷贝到kernelSpace
在select返回时,由kernelSpace拷贝到userSpace
2. 存放描述符的表需要多次轮询
比如:在调用select时,kernelSpace轮询,查看哪个描述符可以进行IO操作
在select返回时,userSpace轮询,如果某个描述符在表中,则进行IO操作
3. 为了提高轮询的效率,先计算存放的最大描述符maxFd,轮询的范围是0~maxFd
总共maxFd+1个需要轮询。
4. 表的大小是多大?fd_set是什么类型?
/* fd_set for select and pselect. */
typedef struct
{
long fds_bits[1024 / (8*sizeof(long))];
} fd_set;
fd_set readFds;
sizeof(readFds) = (1024/(8*sizeof(long))) * sizeof(long)=(1024/8)bytes
5. select能监听的描述符的最大值是多少呢?
最大值是:1023
范围:0~1023,总共1024个
服务器模型
循环服务器
tcp循环服务器:
创建socket,绑定,监听
while(1)
{
newID = accept接受连接
while(1) //通信
{
接收消息newID
处理消息
回复消息newID
}
close(newID);
}
close(socketID);
特点:
前一个客户端如果通信没有结束,后一个客户端无法响应。
结论:tcp循环服务器很少用
UDP循环服务器
创建socket, 绑定
while(1)
{
接收消息
处理消息
回复消息
}
关闭socket
特点:
可以同时处理多个客户端
并发服务器
通过多进程或者多线程来实现并发。
tcp并发服务器
创建socket,绑定,监听
while(1)
{
newID = accept接受连接
创建进程/线程,子进程/子线程用于通信
}
close(socketID);
特点:
a. 通信的过程不会影响接受连接
b. 如果客户端比较多的情况下,会创建大量的进程/线程,比较耗资源.//线程池
多路复用服务器
用于一个进程中有多个阻塞IO的情况
epoll多路复用
//TCP通信 多路复用服务器#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h> /*memset*/
#include <unistd.h> /*close*/
#include <sys/time.h>
#include <fcntl.h>
#include <sys/epoll.h>#define IP "127.0.0.1" //本机测试地址
#define SPORT 10086
#define CPORT 10010#define SIZE 100int main()
{//1.设置变量int sockfd = 0;struct sockaddr_in addr;int addrlen = 0;int newID = 0;char buf[SIZE] = {'\0'};//设置epoll变量int epollfd = 0;struct epoll_event event;struct epoll_event arrEvents[10];int ret = 0;//2.创建socket套接字//参数1:地址族--->使用网络协议族//参数2:套接字类型---->使用流式套接字//参数3:通常设置为0sockfd = socket(PF_INET, SOCK_STREAM, 0);if(0 > sockfd){perror("socket error");return -1;}printf("socket ok\n");//3.绑定地址(自己)ip+port端口addrlen = sizeof(addr);memset(&addr,0,addrlen);addr.sin_family = PF_INET; //地址族--->使用网络协议族addr.sin_port = htons(SPORT); //htons(主机字节序转换网络字节序函数),short短整型---->设置端口addr.sin_addr.s_addr = inet_addr(IP); //inet_addr(IP地址的转换),转换成32位的网络字节序二进制值if(0 > bind(sockfd,(struct sockaddr *)&addr,addrlen)){perror("bind error");close(sockfd); //关闭套接字return -1;}printf("bind ok\n");//4.建立监听----->socket变为被动,只能用于三次握手//参数2:监听队列存放三次握手还没有结束的客户端if(0 > listen(sockfd,5)){perror("listen error");close(sockfd); //关闭套接字return -1;}printf("listen ok\n");//创建epoll,返回epollfdepollfd = epoll_create(SIZE);if(0 > epollfd){perror("epoll_create error");close(sockfd);return -1;}//调用epoll_ctl把等连接sockfd放在入epollfdmemset(&event,0,sizeof(event));event.data.fd = sockfd;event.events = EPOLLIN;if (0 > epoll_ctl( epollfd, EPOLL_CTL_ADD, sockfd, &event)){perror("epoll_ctl sockfd EPOLL_CTL_ADD error");close( sockfd );close( epollfd );return -1;} while(1){//epoll_wait阻塞等待文件可以进行IO操作//参数4:时间ret = epoll_wait( epollfd, arrEvents, sizeof(arrEvents)/sizeof(arrEvents[0]), -1); int i = 0;//遍历epoll_wait返回的数组addrlen = sizeof(addr);for(i = 0;i < ret;i++){//判断arrEvents[i].fd==等连接sockfdif(arrEvents[i].data.fd == sockfd){memset(&addr,0,addrlen);//5.接受连接------>若成功,返回已经连接的套接字newID=accept(sockfd, (struct sockaddr *)&addr,&addrlen);if(0 > newID){perror("accept error");close(sockfd); //关闭套接字return -1;}printf("accept ok %d\r\n", newID);printf("client ip=%s, port=%d\r\n", (char *)inet_ntoa(addr.sin_addr), ntohs(addr.sin_port) );//调用epoll_ctl把newID放在入epollfdmemset(&event,0,sizeof(event));event.data.fd = newID;event.events = EPOLLIN;if (0 > epoll_ctl( epollfd, EPOLL_CTL_ADD, newID, &event)){perror("epoll_ctl sockfd EPOLL_CTL_ADD error");break;} }else{//6.通信----->接收 发送 使用已经连接的套接字//参数3:通常设置为0//参数2:为什么用不了strlen测buf有效长度----->因为strlen测有效字符,buf前面已经置空为0memset(buf,0,SIZE);if(0 < recv(arrEvents[i].data.fd, buf, sizeof(buf)-1,0)){printf("服务器已收到:%s\n",buf);if(0 == strncmp(buf,"quit",4)){break; }}else{printf("nothing,客户端已经关闭\n");close(arrEvents[i].data.fd);//关闭已经连接的套接字if (0 > epoll_ctl( epollfd, EPOLL_CTL_DEL, arrEvents[i].data.fd, NULL)){perror("epoll_ctl EPOLL_CTL_DEL error");break;}}if ( 0 < send(arrEvents[i].data.fd, buf, strlen(buf),0)){printf("回答完毕\n");}}} }//8.关闭socketclose(sockfd);close(epollfd);return 0;
}
epoll可以监听的描述符总共有多少个,最大值是多少?
网络属性设置
int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen);
参数:
sockfd: socket()返回值
level: 比如:设置地址复用,那么,level是SOL_SOCKET
optname:选项名称,SO_REUSEADDR
optval: 当前选项对应传的值,当前选项是地址复用,值有两种允许和不允许, int on = 1;
optlen: 前面optval指针所指空间的大小,当前选项是地址复用,大小就是sizeof(int);
解决:当前进程结束后,再次运行时,绑定port失败.
例:
设置本地地址复用 防止前一次进程结束,下次运行进程绑定失败
int on = 1;
setsockopt( sockfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on) );
网络超时
当前的tcp的等连接套接字/已连接套接字和udp的套接字的接收都是阻塞.
a. setsockopt
struct timeval tv = {3, 0}; //阻塞3s 0ms
setsockopt( sockfd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv) );
//tcp通信的服务器
#include <stdio.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/time.h>#define IP "127.0.0.1" //本机测试地址
#define SPORT 10086
#define CPORT 10010int main()
{int sockfd = 0;int newID = 0;int addrLength = 0;char buf[100] = {0};struct sockaddr_in addr;//创建socketsockfd = socket( PF_INET, SOCK_STREAM, 0 );if ( sockfd < 0 ){perror("socket error");return -1;}printf("socket ok \r\n");int on = 1;setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));//绑定自己的地址addrLength = sizeof(addr);memset(&addr, 0, addrLength );addr.sin_family = PF_INET;addr.sin_port = htons( SPORT );addr.sin_addr.s_addr = inet_addr( IP ); if (0 > bind( sockfd, (struct sockaddr *)&addr, addrLength) ){perror("bind error");close(sockfd);return -1;}printf("bind ok \r\n");//监听 socket变成被动,只能用于三次握手if (0 > listen( sockfd, 5 )){perror("listen error");close(sockfd);return -1;}printf("listen ok \r\n");struct timeval t = {7, 0};setsockopt( sockfd, SOL_SOCKET, SO_RCVTIMEO, &t, sizeof(t) );memset(&addr, 0, addrLength); //接受连接 -- 成功时,将返回已经连接的套接字newID = accept( sockfd, (struct sockaddr *)&addr, &addrLength );if ( newID < 0 ){perror("accept error");close(sockfd);return -1;}printf("accept ok \r\n");printf("client ip=%s, port=%d\r\n", (char *)inet_ntoa(addr.sin_addr), ntohs(addr.sin_port) );//通信 接收 发送 使用已经连接的套接字if ( 0 < recv( newID, buf, sizeof(buf) - 1, 0 ))printf("服务器收到:%s\r\n", buf);else printf("nothing\r\n");//关闭已经连接的套接字close( newID );//关闭socketclose( sockfd );return 0;
}
b. select
struct timeval tv = {3, 0};
select( maxFd + 1, &readFds, NULL, NULL, &tv );
//tcp通信的多路复用服务器
#include <stdio.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/time.h>#define IP "127.0.0.1" //本机测试地址
#define SPORT 10086
#define CPORT 10010int main()
{int sockfd = 0;int newID = 0;int addrLength = 0;char buf[100] = {0};struct sockaddr_in addr;int maxFd = 0;fd_set readFds;fd_set tmpFds;//创建socketsockfd = socket( PF_INET, SOCK_STREAM, 0 );if ( sockfd < 0 ){perror("socket error");return -1;}printf("socket ok \r\n");//绑定自己的地址addrLength = sizeof(addr);memset(&addr, 0, addrLength );addr.sin_family = PF_INET;addr.sin_port = htons( SPORT );addr.sin_addr.s_addr = inet_addr( IP ); if (0 > bind( sockfd, (struct sockaddr *)&addr, addrLength) ){perror("bind error");close(sockfd);return -1;}printf("bind ok \r\n");//监听 socket变成被动,只能用于三次握手if (0 > listen( sockfd, 5 )){perror("listen error");close(sockfd);return -1;}printf("listen ok \r\n");//清空readFdsFD_ZERO(&readFds);//将等连接的套接字放入readFdsFD_SET( sockfd, &readFds );tmpFds = readFds;//maxFd是select所有监听描述符中的最大值maxFd = sockfd;while(1){ struct timeval t = {7, 0};//tmpFds保存最完整的被监听的描述符readFds = tmpFds;//调用select函数,它是一个阻塞函数int j = select( maxFd + 1, &readFds, NULL, NULL, &t );if ( j < 0 ){perror("select error");break;}else if ( 0 == j ){printf("select time out , try again\r\n");continue;}int i = 0;//select返回后,应用程序需要轮询内核返回的readFdsfor ( i = 0; i < maxFd + 1; i++ ){if ( FD_ISSET( i, &readFds ) ){ if ( i == sockfd ) //等连接socket{memset(&addr, 0, addrLength); //接受连接 -- 成功时,将返回已经连接的套接字newID = accept( sockfd, (struct sockaddr *)&addr, &addrLength );if ( newID < 0 ){perror("accept error");close(sockfd);return -1;}printf("accept ok %d\r\n", newID);printf("client ip=%s, port=%d\r\n", (char *)inet_ntoa(addr.sin_addr), ntohs(addr.sin_port) );//把newID放到tmpFds里FD_SET( newID, &tmpFds );if ( maxFd < newID ){maxFd = newID;}}else {memset(buf, 0, sizeof(buf));//通信 接收 发送 使用已经连接的套接字if ( 0 < recv( i, buf, sizeof(buf) - 1, 0 )){printf("服务器收到:%s\r\n", buf);}else {printf("nothing,客户端已经关闭\r\n");close(i);//关闭已经连接的套接字FD_CLR(i, &tmpFds);}if ( 0 < send( i, buf, strlen(buf), 0 ) ){printf("回答完毕!\r\n");}}}}}//关闭socketclose( sockfd );return 0;
}
c. SIGALRM
void handle( int sig ){...}
sigaction( SIGALRM, NULL, &act );
act.sig_handle = handle;
act.sig_flags &= ~SA_RESTART; //
sigaction( SIGALRM, &act, NULL);
alarm(5);
//udp通信 被动接收 服务端
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <signal.h>#define IP "127.0.0.1"
#define SPORT 50000
#define CPORT 50001
#define SIZE 100void handler( int sig )
{if ( SIGALRM == sig ){printf("sig---------------------\r\n");}else {printf("wrong wrong \r\n");}
}int main()
{int socketID = 0;int addrLength = 0;struct sockaddr_in addr ;char buf[SIZE] = {0};int ret = 0;//创建socketsocketID = socket( PF_INET, SOCK_DGRAM, 0);if ( socketID < 0 ){perror("socket error");return -1;}printf(" socket ok\r\n" );//绑定自己的地址addrLength = sizeof(addr);memset( &addr, 0, addrLength );addr.sin_family = PF_INET;addr.sin_port = htons( SPORT );addr.sin_addr.s_addr = INADDR_ANY;if ( 0 > bind( socketID, (struct sockaddr *)&addr, addrLength )){perror("bind error");close(socketID);return -1;}printf("bind ok\r\n");struct sigaction act;sigaction(SIGALRM, NULL, &act);//获取当前进程对SIGALRM的处理方式act.sa_handler = handler;act.sa_flags &= ~SA_RESTART; //不再重启阻塞sigaction(SIGALRM, &act, NULL);//设置当前进程对SIGALRM的处理方式alarm(5);//接收消息memset( buf, 0, SIZE );memset( &addr, 0, addrLength );ret = recvfrom( socketID, buf, SIZE - 1, 0, (struct sockaddr *)&addr, &addrLength);if ( ret > 0 ){printf("ip=%s, port=%d ", (char *)inet_ntoa(addr.sin_addr), ntohs(addr.sin_port) );printf(" said:%s\r\n", buf);}else if ( ret < 0 ){perror(" recvfrom error ");} else{printf("recvfrom return %d\r\n", ret);}//发送消息 //输入准备发送的消息memset( buf, 0, SIZE );strncpy( buf, "太好了,真棒!", SIZE - 1 );ret = sendto( socketID, buf, strlen(buf), 0, (struct sockaddr *)&addr, addrLength );if ( ret > 0 ){printf("send ok\r\n");}//关闭socketclose(socketID);return 0;
}
数据库
数据持久化-->文件(普通文件, 数据库)
相关工作
在公司比较重要的岗位/人员稳定
常见的数据库
oracle, DB2, sqlServer, mySql, sqlite
数据库的特点*****(在就业前网上查一下)
安装数据库
1. apt-get install ***
2. 下载源码, 自行编译, 拷贝库到指定位置, 调用库中的函数实现自己的功能
3. 下载源码, 将源码和自己的代码一起编译生成可执行文件即可.
操作数据库
1. 图形化界面(测试人员/终端用户)
2. shell命令(运维人员)
3. API应用程序接口(函数)(软件开发人员)
学习的重点: SQL(结构化查询语言)******
---------------
进程:
进程是程序运行的一次过程。
进程是动态的,不能保存的。
进程是操作系统分配资源的最小单位。
线程:
轻量级的进程。
线程是操作系统调度的最小单位。
线程只有独立的栈段,和主线程共享其它内存资源。