考虑到代码量,除了select_server.h给出实现代码,其他的封装类只给出原型定义
将Linux上的网络编程的系统函数封装,做错误处理
netapi.h
#ifndef NETAPI_H
#define NETAPI_H
class NetApi{
public:
int Socket(int domain, int type, int protocol);
int Bind(int sockfd, const struct sockaddr * addr, socklen_t addrlen);
int Listen(int sockfd, int backlog);
int Accept(int sockfd, struct sockaddr * addr, socklen_t * addrlen);
int Connect(int sockfd, const struct sockaddr * addr, socklen_t addrlen);
ssize_t Read(int fd, void * ptr, size_t nbytes);
ssize_t Write(int fd, const void * ptr, size_t nbytes);
int Close(int fd);
void Perror(const char * str);
};
#endif
服务器的抽象基类
base_server.h
#ifndef BASE_SERVER_H
#define BASE_SERVER_H
#include "netapi.h"
class BaseServer{
protected:
int listen_fd;
struct sockaddr_in server_addr;
NetApi net_api;
public:
BaseServer();
~BaseServer();
void init_socket(const int port);
int get_listenfd();
bool is_listenfd(const int fd);
virtual void process_run() = 0;
virtual void wait_requests() = 0;
virtual void accept_clients() = 0;
virtual void close_client(const int client_fd) = 0;
virtual void deal_clients_messages() = 0;
};
#endif
多路IO转接select模型
select_server.h
#ifndef __SELECT_SERVER_H__
#define __SELECT_SERVER_H__
#include <map>
#include "base_server.h"
class SelectServer : public BaseServer{
protected:
bool init_flag;
socklen_t socket_len;
sockaddr_in * client_addrs;
int client_max_num;
int request_nums;
std::map<int, int> map_coord;
int max_fd;
int max_coord;
public:
SelectServer();
~SelectServer();
void set_max_num(const int & num);
void display_socket_addr(const int & coord);
void update_coord(const int & client_fd, const int & coord);
virtual void init_data();
virtual void process_run();
virtual void wait_requests();
virtual void accept_clients();
virtual void close_client(const int & client_fd);
virtual void deal_clients_messages();
private:
bool is_select;
int * clients;
fd_set all_sets;
fd_set read_sets;
};
#endif
select模型的高并发服务器的实现
select_server.cpp
#include <sys/select.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#include <ctype.h>
#include <string>
#include <stdlib.h>
#include <stdio.h>
#include <arpa/inet.h>
#include <iostream>
#include "select_server.h"
using std::cout;
using std::endl;
using std::string;
using std::map;
SelectServer::SelectServer(){
is_select = false;
client_max_num = 0;
init_flag = false;
}
SelectServer::~SelectServer(){
if(client_max_num && is_select){
for(int i = 1; i <= max_coord; ++i){
if(clients[i] != -1){
net_api.Close(clients[i]);
}
}
delete [] clients;
delete [] client_addrs;
}
}
void SelectServer::set_max_num(const int & num){
client_max_num = num;
}
void SelectServer::init_data(){
if(!client_max_num)return;
client_addrs = new sockaddr_in[client_max_num];
clients = new int [client_max_num];
FD_ZERO(&all_sets);
max_coord = 0;
max_fd = listen_fd;
clients[max_coord] = max_fd;
map_coord[listen_fd] = 0;
for(int i = max_coord+1; i < client_max_num; ++i){
clients[i] = -1;
map_coord[i] = -1;
}
FD_SET(max_fd, &all_sets);
init_flag = true;
is_select = true;
}
void SelectServer::display_socket_addr(const int & coord){
char buf[BUFSIZ];
const char * ip = inet_ntop(AF_INET, &client_addrs[coord].sin_addr, buf, sizeof(buf));
const int port = ntohs(client_addrs[coord].sin_port);
cout << "client ip is : " << ip;
cout << "\tclient port is: " << port;
cout << endl;
}
void SelectServer::process_run(){
if(!init_flag)return;
while(true){
wait_requests();
accept_clients();
deal_clients_messages();
}
}
void SelectServer::wait_requests(){
read_sets = all_sets;
request_nums = select(max_fd+1, &read_sets, NULL, NULL, NULL);
}
void SelectServer::accept_clients(){
if(FD_ISSET(listen_fd, &read_sets)){
int coord = -1;
for(int i = 1; i < client_max_num; ++i){
if(clients[i] == -1){
coord = i;
break;
}
}
if(coord == -1){
cout << "client number out of bound!" << endl;
return;
}
socket_len = sizeof(client_addrs[coord]);
int client_fd = net_api.Accept(listen_fd, (struct sockaddr *) &client_addrs[coord], &socket_len);
clients[coord] = client_fd;
FD_SET(client_fd, &all_sets);
update_coord(client_fd, coord);
--request_nums;
cout << "A client connected" << endl;
display_socket_addr(coord);
cout << "................................" << endl;
}
}
void SelectServer::update_coord(const int & client_fd, const int & coord ){
map_coord[client_fd] = coord;
max_coord = max_coord > coord ? max_coord : coord;
max_fd = max_fd > client_fd ? max_fd : client_fd;
}
void SelectServer::close_client(const int & client_fd){
net_api.Close(client_fd);
int coord = map_coord[client_fd];
map_coord[client_fd] = -1;
clients[coord] = -1;
FD_CLR(client_fd, &all_sets);
}
void SelectServer::deal_clients_messages(){
if( !request_nums )return;
char buf[BUFSIZ];
const string tmp = "\nServer respone: You are very handsome!\n";
for(int i = 1; i <= max_coord; ++i){
if(FD_ISSET(clients[i], &read_sets)){
int client_fd = clients[i];
int n = net_api.Read(client_fd, buf, sizeof(buf));
if( n <= 0){
close_client(client_fd);
cout << "A client disconnected" << endl;
display_socket_addr(i);
cout << "................................." << endl;
}else{
for(int j = 0; j < n; ++j){
buf[j] = toupper(buf[j]);
if(buf[j] == '\0'){
n = j;
break;
}
}
int len = tmp.length();
for(int j = 0; j < len; ++j){
buf[j+n] = tmp[j];
}
buf[n+len] = '\0';
net_api.Write(client_fd, buf, n+len);
}
}
}
}
Linux上的多路IO转接高并发服务器消除了多进程高并发和多线程高并发的系统资源消耗大,可连接客户端数较少的缺点,以上实现的是多路I/O转接高并发服务器的select模型的基本原型.
Comments