进程间通信
网络通信
三要素: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 100
int 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:通常设置为0
sockfd = 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,返回epollfd
epollfd = epoll_create(SIZE);
if(0 > epollfd)
{
perror("epoll_create error");
close(sockfd);
return -1;
}
//调用epoll_ctl把等连接sockfd放在入epollfd
memset(&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==等连接sockfd
if(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放在入epollfd
memset(&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前面已经置空为0
memset(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.关闭socket
close(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 10010
int main()
{
int sockfd = 0;
int newID = 0;
int addrLength = 0;
char buf[100] = {0};
struct sockaddr_in addr;
//创建socket
sockfd = 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 );
//关闭socket
close( 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 10010
int 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;
//创建socket
sockfd = 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");
//清空readFds
FD_ZERO(&readFds);
//将等连接的套接字放入readFds
FD_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返回后,应用程序需要轮询内核返回的readFds
for ( 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");
}
}
}
}
}
//关闭socket
close( 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 100
void 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;
//创建socket
socketID = 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");
}
//关闭socket
close(socketID);
return 0;
}
数据库
数据持久化-->文件(普通文件, 数据库)
相关工作
在公司比较重要的岗位/人员稳定
常见的数据库
oracle, DB2, sqlServer, mySql, sqlite
数据库的特点*****(在就业前网上查一下)
安装数据库
1. apt-get install ***
2. 下载源码, 自行编译, 拷贝库到指定位置, 调用库中的函数实现自己的功能
3. 下载源码, 将源码和自己的代码一起编译生成可执行文件即可.
操作数据库
1. 图形化界面(测试人员/终端用户)
2. shell命令(运维人员)
3. API应用程序接口(函数)(软件开发人员)
学习的重点: SQL(结构化查询语言)******
---------------
进程:
进程是程序运行的一次过程。
进程是动态的,不能保存的。
进程是操作系统分配资源的最小单位。
线程:
轻量级的进程。
线程是操作系统调度的最小单位。
线程只有独立的栈段,和主线程共享其它内存资源。