//
net_echo.cpp
// 写一个程序,支持同时打开10w个文件句柄,申请1G共享内存,是一个tcp echo的server,采用select或epoll管理多连接
#include < sys / socket.h >
#include < sys / resource.h >
#include < stdio.h >
#include < sys / epoll.h >
#include < arpa / inet.h >
#include < strings.h >
#include < unistd.h >
#include < fcntl.h >
#include < errno.h >
#include < sys / shm.h >
#include < string .h >
#include < time.h >
#define SHM_MAX 1000000000UL // 共享内存大小
#define SHM_KEY 7896 // 共享内存申请时的key
#define SERV_PORT 4466 // 服务端口号
#define MAX_RLIMIT 100000 // 最大访问量
#define LISTENQ 5 // 监听队列长度
#define MAX_LINE 128 // 缓存长度
const char * local_addr = " 127.0.0.1 " ; // 绑定服务地址
struct access_info{ // 记录客户访问信息
time_t a_time; // 客户访问时间
in_addr_t a_ip; // 客户ip
int a_errno; // 是否访问成功,成功为0,否则为其错误号
};
bool setnonblocking( int fd); // 设置fd为非阻塞模式
bool set_fd_limit(unsigned int max); // 设置系统允许的进程所能打开的文件描述符的最大值
int main( int argc, char ** argv)
{
int listenfd = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP); // 建立serv socket
if (listenfd < 0 ){
perror( " create socket failed! " );
return - 1 ;
}
struct sockaddr_in servaddr,clientaddr;
bzero( & servaddr, sizeof (servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(SERV_PORT);
inet_aton(local_addr, & (servaddr.sin_addr));
if (bind(listenfd,(sockaddr * ) & servaddr, sizeof (servaddr)) < 0 ) // 绑定本机地址
{
perror( " bind error! " );
return - 1 ;
}
if ( ! set_fd_limit(MAX_RLIMIT)){
perror( " setrlimit failed! " );
return - 1 ;
}
struct epoll_event ev,events[ 20 ];
int epfd = epoll_create(MAX_RLIMIT); // epoll_create
if ( ! setnonblocking(listenfd)) return - 1 ;
ev.data.fd = listenfd;
ev.events = EPOLLIN | EPOLLET;
epoll_ctl(epfd,EPOLL_CTL_ADD,listenfd, & ev); // 把listenfd加入到epoll监听队列
int shm_id = shmget(SHM_KEY,SHM_MAX,IPC_CREAT | 0600 ); // 申请共享内存
if (shm_id ==- 1 ){
perror( " shmget " );
return - 2 ;
}
struct access_info client_info, * pos; // 客户信息
int * head;
head = ( int * )shmat(shm_id, 0 , 0 );
if ( int (head) ==- 1 ){
perror( " shmat " );
return - 1 ;
}
* head = 1 ; // 服务器在运行状态,若该值变为0,则关闭服务器
if ( * (head + 1 ) != 1 ){ // head+1服务器是否第一次运行,head+2共享内存存储的信息数量
* (head + 1 ) = 1 ; // ___________
* (head + 2 ) = 0 ; // head-->|___ 0/1___| 服务器的运行状态
} // |___ 0/1___| 共享内存是否使用过,是为1,否则为0
// |___ n____| 共享内存存储信息数量 0~SHM_MAX/(3*4*8)-1
pos = ( struct access_info * )(head) + 1 +* (head + 2 ); // 记录信息的开始位置
listen(listenfd,LISTENQ); // 监听客户端请求
int nfds,i,connfd,sockfd,n;
socklen_t len;
char line[MAX_LINE];
while ( * head)
{
nfds = epoll_wait(epfd,events, 20 , 500 ); // 检测活跃连接
for (i = 0 ;i < nfds;i ++ )
{
if (events[i].data.fd == listenfd) // 有新连接到来
{
len = sizeof (clientaddr);
connfd = accept(listenfd,(sockaddr * ) & clientaddr, & len);
client_info.a_time = time(NULL); // 注册客户信息
client_info.a_ip = clientaddr.sin_addr.s_addr;
client_info.a_errno = 0 ;
if (connfd < 0 ){
perror( " connfd<0! " );
client_info.a_errno = errno;
continue ;
}
memcpy(pos, & client_info, sizeof (client_info));
pos ++ ; // 共享内存指针后移,并把信息数量加1
if (( * (head + 2 )) ++> 4 * SHM_MAX / ( 3 * 8 * 4 * 5 )) // 共享内存剩余不足1/5时发出警告信息
fprintf(stderr, " Warning:share memory is being not enough/n Left:%d/n " ,SHM_MAX -* (head + 2 ) * 3 * 4 * 8 );
if ( ! setnonblocking(connfd)) continue ;
ev.data.fd = connfd;
ev.events = EPOLLIN | EPOLLET;
epoll_ctl(epfd,EPOLL_CTL_ADD,connfd, & ev); // 新连接加入epoll_wait
}
else if (events[i].events & EPOLLIN) // 连接可读
{
if ((sockfd = events[i].data.fd) < 0 ) continue ;
while ((n = read(sockfd,line,MAX_LINE)) == MAX_LINE)
write(sockfd,line,n);
if (n < 0 )
{
if (errno == ECONNRESET) {
events[i].data.fd = - 1 ;
epoll_ctl(epfd,EPOLL_CTL_DEL,sockfd,NULL);
close(sockfd);
continue ;
}
else continue ; // 恰好读完MAX_LINE后无数据
}
else if (n == 0 ){ // 客户端关闭连接
epoll_ctl(epfd,EPOLL_CTL_DEL,sockfd,NULL);
close(sockfd);
events[i].data.fd = - 1 ;
continue ;
}
else write(sockfd,line,n);
}
/* else if(events[i].events&EPOLLOUT)
{
sockfd=events[i].data.fd;
if(sockfd<0)continue;
write(sockfd,line,n);
ev.data.fd=sockfd;
ev.events=EPOLLIN|EPOLLET;
epoll_ctl(epfd,EPOLL_CTL_MOD,sockfd,&ev);
}
*/ }
}
shmdt(head); // 卸载共享内存
close(epfd);
close(listenfd);
return 0 ;
}
bool setnonblocking( int sock)
{
int opts;
opts = fcntl(sock,F_GETFL);
if (opts < 0 )
{
perror( " fcntl(sock,GETFL) " );
return false ;
}
opts = opts | O_NONBLOCK;
if (fcntl(sock,F_SETFL,opts) < 0 )
{
perror( " fcntl(sock,SETFL,opts) " );
return false ;
}
return true ;
}
bool set_fd_limit(unsigned int max)
{
struct rlimit rlim,rlim_new;
if (getrlimit(RLIMIT_NOFILE, & rlim) != 0 )
return false ;
if (rlim.rlim_cur >= max) return true ;
if (rlim.rlim_max == RLIM_INFINITY || rlim.rlim_max >= max){
rlim_new.rlim_max = rlim.rlim_max;
rlim_new.rlim_cur = max;
}
else {
if (geteuid() != 0 ){errno = 1 ; return false ; }
rlim_new.rlim_max = rlim_new.rlim_cur = max;
}
if (setrlimit(RLIMIT_NOFILE, & rlim_new) != 0 ) { /* failed. try raising just to the old max */
int err = errno;
setrlimit(RLIMIT_NOFILE, & rlim);
errno = err;
return false ;
}
return true ;
}
// 写一个程序,支持同时打开10w个文件句柄,申请1G共享内存,是一个tcp echo的server,采用select或epoll管理多连接
#include < sys / socket.h >
#include < sys / resource.h >
#include < stdio.h >
#include < sys / epoll.h >
#include < arpa / inet.h >
#include < strings.h >
#include < unistd.h >
#include < fcntl.h >
#include < errno.h >
#include < sys / shm.h >
#include < string .h >
#include < time.h >
#define SHM_MAX 1000000000UL // 共享内存大小
#define SHM_KEY 7896 // 共享内存申请时的key
#define SERV_PORT 4466 // 服务端口号
#define MAX_RLIMIT 100000 // 最大访问量
#define LISTENQ 5 // 监听队列长度
#define MAX_LINE 128 // 缓存长度
const char * local_addr = " 127.0.0.1 " ; // 绑定服务地址
struct access_info{ // 记录客户访问信息
time_t a_time; // 客户访问时间
in_addr_t a_ip; // 客户ip
int a_errno; // 是否访问成功,成功为0,否则为其错误号
};
bool setnonblocking( int fd); // 设置fd为非阻塞模式
bool set_fd_limit(unsigned int max); // 设置系统允许的进程所能打开的文件描述符的最大值
int main( int argc, char ** argv)
{
int listenfd = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP); // 建立serv socket
if (listenfd < 0 ){
perror( " create socket failed! " );
return - 1 ;
}
struct sockaddr_in servaddr,clientaddr;
bzero( & servaddr, sizeof (servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(SERV_PORT);
inet_aton(local_addr, & (servaddr.sin_addr));
if (bind(listenfd,(sockaddr * ) & servaddr, sizeof (servaddr)) < 0 ) // 绑定本机地址
{
perror( " bind error! " );
return - 1 ;
}
if ( ! set_fd_limit(MAX_RLIMIT)){
perror( " setrlimit failed! " );
return - 1 ;
}
struct epoll_event ev,events[ 20 ];
int epfd = epoll_create(MAX_RLIMIT); // epoll_create
if ( ! setnonblocking(listenfd)) return - 1 ;
ev.data.fd = listenfd;
ev.events = EPOLLIN | EPOLLET;
epoll_ctl(epfd,EPOLL_CTL_ADD,listenfd, & ev); // 把listenfd加入到epoll监听队列
int shm_id = shmget(SHM_KEY,SHM_MAX,IPC_CREAT | 0600 ); // 申请共享内存
if (shm_id ==- 1 ){
perror( " shmget " );
return - 2 ;
}
struct access_info client_info, * pos; // 客户信息
int * head;
head = ( int * )shmat(shm_id, 0 , 0 );
if ( int (head) ==- 1 ){
perror( " shmat " );
return - 1 ;
}
* head = 1 ; // 服务器在运行状态,若该值变为0,则关闭服务器
if ( * (head + 1 ) != 1 ){ // head+1服务器是否第一次运行,head+2共享内存存储的信息数量
* (head + 1 ) = 1 ; // ___________
* (head + 2 ) = 0 ; // head-->|___ 0/1___| 服务器的运行状态
} // |___ 0/1___| 共享内存是否使用过,是为1,否则为0
// |___ n____| 共享内存存储信息数量 0~SHM_MAX/(3*4*8)-1
pos = ( struct access_info * )(head) + 1 +* (head + 2 ); // 记录信息的开始位置
listen(listenfd,LISTENQ); // 监听客户端请求
int nfds,i,connfd,sockfd,n;
socklen_t len;
char line[MAX_LINE];
while ( * head)
{
nfds = epoll_wait(epfd,events, 20 , 500 ); // 检测活跃连接
for (i = 0 ;i < nfds;i ++ )
{
if (events[i].data.fd == listenfd) // 有新连接到来
{
len = sizeof (clientaddr);
connfd = accept(listenfd,(sockaddr * ) & clientaddr, & len);
client_info.a_time = time(NULL); // 注册客户信息
client_info.a_ip = clientaddr.sin_addr.s_addr;
client_info.a_errno = 0 ;
if (connfd < 0 ){
perror( " connfd<0! " );
client_info.a_errno = errno;
continue ;
}
memcpy(pos, & client_info, sizeof (client_info));
pos ++ ; // 共享内存指针后移,并把信息数量加1
if (( * (head + 2 )) ++> 4 * SHM_MAX / ( 3 * 8 * 4 * 5 )) // 共享内存剩余不足1/5时发出警告信息
fprintf(stderr, " Warning:share memory is being not enough/n Left:%d/n " ,SHM_MAX -* (head + 2 ) * 3 * 4 * 8 );
if ( ! setnonblocking(connfd)) continue ;
ev.data.fd = connfd;
ev.events = EPOLLIN | EPOLLET;
epoll_ctl(epfd,EPOLL_CTL_ADD,connfd, & ev); // 新连接加入epoll_wait
}
else if (events[i].events & EPOLLIN) // 连接可读
{
if ((sockfd = events[i].data.fd) < 0 ) continue ;
while ((n = read(sockfd,line,MAX_LINE)) == MAX_LINE)
write(sockfd,line,n);
if (n < 0 )
{
if (errno == ECONNRESET) {
events[i].data.fd = - 1 ;
epoll_ctl(epfd,EPOLL_CTL_DEL,sockfd,NULL);
close(sockfd);
continue ;
}
else continue ; // 恰好读完MAX_LINE后无数据
}
else if (n == 0 ){ // 客户端关闭连接
epoll_ctl(epfd,EPOLL_CTL_DEL,sockfd,NULL);
close(sockfd);
events[i].data.fd = - 1 ;
continue ;
}
else write(sockfd,line,n);
}
/* else if(events[i].events&EPOLLOUT)
{
sockfd=events[i].data.fd;
if(sockfd<0)continue;
write(sockfd,line,n);
ev.data.fd=sockfd;
ev.events=EPOLLIN|EPOLLET;
epoll_ctl(epfd,EPOLL_CTL_MOD,sockfd,&ev);
}
*/ }
}
shmdt(head); // 卸载共享内存
close(epfd);
close(listenfd);
return 0 ;
}
bool setnonblocking( int sock)
{
int opts;
opts = fcntl(sock,F_GETFL);
if (opts < 0 )
{
perror( " fcntl(sock,GETFL) " );
return false ;
}
opts = opts | O_NONBLOCK;
if (fcntl(sock,F_SETFL,opts) < 0 )
{
perror( " fcntl(sock,SETFL,opts) " );
return false ;
}
return true ;
}
bool set_fd_limit(unsigned int max)
{
struct rlimit rlim,rlim_new;
if (getrlimit(RLIMIT_NOFILE, & rlim) != 0 )
return false ;
if (rlim.rlim_cur >= max) return true ;
if (rlim.rlim_max == RLIM_INFINITY || rlim.rlim_max >= max){
rlim_new.rlim_max = rlim.rlim_max;
rlim_new.rlim_cur = max;
}
else {
if (geteuid() != 0 ){errno = 1 ; return false ; }
rlim_new.rlim_max = rlim_new.rlim_cur = max;
}
if (setrlimit(RLIMIT_NOFILE, & rlim_new) != 0 ) { /* failed. try raising just to the old max */
int err = errno;
setrlimit(RLIMIT_NOFILE, & rlim);
errno = err;
return false ;
}
return true ;
}
/*----------------------------------------------------------------*/
//
net_echo_shutdown.cpp
// 启动该进程时,关闭net_echo服务进程
#include < sys / shm.h >
#include < stdio.h >
#include < unistd.h >
#include < errno.h >
#define SHM_MAX 1000000000UL // 共享内存大小
#define SHM_KEY 7896 // 共享内存申请时的key
#ifndef IPC_ALLOC
#define IPC_ALLOC IPC_CREAT
#endif
int main( int argc, char ** argv)
{
if (geteuid() != 0 ){
errno = 1 ;
perror( " net_echo_shutdown: " );
return - 1 ;
}
int shmid;
if ((shmid = shmget(SHM_KEY,SHM_MAX,IPC_ALLOC | 0600 )) ==- 1 )
{
perror( " shmget() " );
return - 1 ;
}
int * head = ( int * )shmat(shmid, 0 , 0 );
if ( int (head) ==- 1 ){
perror( " shmat() " );
return - 1 ;
}
if ( * head != 1 ){ // 服务器并未运行
fprintf(stderr, " Net_echo server is not running/n " );
return - 1 ;
}
* head = 0 ; // 设置关闭标志
printf( " Shutdown the echo server/n " );
sleep( 2 );
shmdt(head);
return 0 ;
}
// 启动该进程时,关闭net_echo服务进程
#include < sys / shm.h >
#include < stdio.h >
#include < unistd.h >
#include < errno.h >
#define SHM_MAX 1000000000UL // 共享内存大小
#define SHM_KEY 7896 // 共享内存申请时的key
#ifndef IPC_ALLOC
#define IPC_ALLOC IPC_CREAT
#endif
int main( int argc, char ** argv)
{
if (geteuid() != 0 ){
errno = 1 ;
perror( " net_echo_shutdown: " );
return - 1 ;
}
int shmid;
if ((shmid = shmget(SHM_KEY,SHM_MAX,IPC_ALLOC | 0600 )) ==- 1 )
{
perror( " shmget() " );
return - 1 ;
}
int * head = ( int * )shmat(shmid, 0 , 0 );
if ( int (head) ==- 1 ){
perror( " shmat() " );
return - 1 ;
}
if ( * head != 1 ){ // 服务器并未运行
fprintf(stderr, " Net_echo server is not running/n " );
return - 1 ;
}
* head = 0 ; // 设置关闭标志
printf( " Shutdown the echo server/n " );
sleep( 2 );
shmdt(head);
return 0 ;
}
/******************************************************************/
//
print_shm.cpp
// 读取并打印共享内存信息
#include < stdio.h >
#include < sys / shm.h >
#include < unistd.h >
#include < errno.h >
#include < time.h >
#include < sys / socket.h >
#include < arpa / inet.h >
#include < string .h >
#define SHM_KEY 7896
#define SHM_MAX 1000000000UL
#ifndef IPC_ALLOC
#define IPC_ALLOC IPC_CREAT
#endif
struct access_info{ // 记录客户访问信息
time_t a_time; // 客户访问时间
in_addr_t a_ip; // 客户ip
int a_errno; // 是否访问成功,成功为0,否则为其错误号
};
int main( int argc, char ** argv)
{
if (geteuid() != 0 ){
errno = 1 ;
perror( " print_shm: " );
return - 1 ;
}
int shmid = shmget(SHM_KEY,SHM_MAX,IPC_ALLOC | 0600 );
if (shmid ==- 1 )
{
perror( " shmget() " );
return - 1 ;
}
int * head = ( int * )shmat(shmid, 0 , 0 );
if ( int (head) ==- 1 ){
perror( " shmat() " );
return - 1 ;
}
if ( * (head + 1 ) != 1 ){
fprintf(stderr, " SHM have not be used!/n " );
return - 1 ;
}
struct access_info * pos = (access_info * )(head) + 1 ;
for ( int i = 0 ;i <* (head + 2 );i ++ ,pos ++ )
printf( " %-15s%-10s%20s%s " ,inet_ntoa( * (in_addr * ) & (pos -> a_ip)),pos -> a_errno == 0 ? " Success! " : " Failed: " ,
pos -> a_errno == 0 ? "" :strerror(pos -> a_errno),ctime( & pos -> a_time));
shmdt(head);
return 0 ;
}
// 读取并打印共享内存信息
#include < stdio.h >
#include < sys / shm.h >
#include < unistd.h >
#include < errno.h >
#include < time.h >
#include < sys / socket.h >
#include < arpa / inet.h >
#include < string .h >
#define SHM_KEY 7896
#define SHM_MAX 1000000000UL
#ifndef IPC_ALLOC
#define IPC_ALLOC IPC_CREAT
#endif
struct access_info{ // 记录客户访问信息
time_t a_time; // 客户访问时间
in_addr_t a_ip; // 客户ip
int a_errno; // 是否访问成功,成功为0,否则为其错误号
};
int main( int argc, char ** argv)
{
if (geteuid() != 0 ){
errno = 1 ;
perror( " print_shm: " );
return - 1 ;
}
int shmid = shmget(SHM_KEY,SHM_MAX,IPC_ALLOC | 0600 );
if (shmid ==- 1 )
{
perror( " shmget() " );
return - 1 ;
}
int * head = ( int * )shmat(shmid, 0 , 0 );
if ( int (head) ==- 1 ){
perror( " shmat() " );
return - 1 ;
}
if ( * (head + 1 ) != 1 ){
fprintf(stderr, " SHM have not be used!/n " );
return - 1 ;
}
struct access_info * pos = (access_info * )(head) + 1 ;
for ( int i = 0 ;i <* (head + 2 );i ++ ,pos ++ )
printf( " %-15s%-10s%20s%s " ,inet_ntoa( * (in_addr * ) & (pos -> a_ip)),pos -> a_errno == 0 ? " Success! " : " Failed: " ,
pos -> a_errno == 0 ? "" :strerror(pos -> a_errno),ctime( & pos -> a_time));
shmdt(head);
return 0 ;
}