最近在学习boost库里的一些常用的库,目前已经看到了boost库的bind适配器这块,个人感觉bind这玩意太好用了,整体来讲bind要比STL里的bind1st和bind2nd要好用很多,下面就从一些基本的用法来说起吧。
bind组件在boost中包含了很多的重载函数,其中这些函数主要是以参数的个数以及被绑定的对象类型来划分,而编译器会根据具体的参数类型以及参数个数来自动地调用相关的形式,在bind中,接收的一个参数必定是一个可调用的对象,这个对象可以是普通函数,函数指针以及类的成员函数,而之后,bind最多可以接收9个参数,这些参数的个数必定和传入的可调用的对象的参数数量相等。在绑定完成之后,bind会返回一个函数对象,并且在内部保留了可调用对象的拷贝,具有operator(),返回类型会被自动推导为可调用对象的返回类型,其他的就不说了,具体来看代码吧,代码如下:
1.普通函数和函数指针
#include <boost/bind.hpp>
#include <boost/function.hpp>
#include <iostream>
#include <stdio.h>
using namespace boost;
using namespace std;
void worker()
{
printf("this is empty argument\n");
}
void worker(int a,int b)
{
int sum = a + b;
printf("this is two argument,sum=%d\n",sum);
}
template<class T>
void worker1(T a,T b)
{
printf("this is a tempate function,a=%f,b=%f\n",a,b);
}
typedef void (*pFunc1)();
typedef void (*pFunc2)(int,int);
int main()
{
int x = 2,y=3;
double z = 2.1,w = 2.4;
boost::bind(worker,x,y)();
boost::bind(worker,x,x)();
boost::bind(worker,y,y)();
boost::bind(worker,_1,_2)(x,y);
boost::bind(worker,_1,_1)(x,y);
boost::bind(worker,_2,_2)(x,y);
pFunc1 func1;
pFunc2 func2;
func1 = worker;
func2 = worker;
boost::bind(func1)();
boost::bind(func2,x,y)();
boost::bind(func2,_1,_2)(x,y);
//boost::bind<double>(worker1<double>,z,w)();
//boost::bind<double>(worker1<double>,_1,_2)(z,w);
//boost::bind<double>(worker1<double>,_2,_1)(z,w);
return 0;
}
2.成员函数和函数对象
struct demo
{
demo()
{
x = y = 0;
}
demo(int x,int y):x(x),y(y)
{
}
void display()
{
cout<<"x="<<x<<" "<<"y="<<y<<endl;
}
void operator()(int a,int b)
{
x = a;
y = b;
display();
}
int x,y;
};
int main()
{
demo demo1(1,2);
demo& demo2 = demo1;
demo* demo3 = &demo2;
boost::bind(&demo::display,demo1)();
boost::bind(&demo::display,demo2)();
boost::bind(&demo::display,demo3)();
vector<demo> demolist(10);
for_each(demolist.begin(),demolist.end(),bind(&demo::display,_1));
boost::bind<void>(demo(),_1,_2)(100,200);
boost::bind<void>(demo(),200,400)();
return 0;
}
好了,bind的一些基本的操作差不多介绍完了,下面我们想借助boost::bind来完成一个稍微复杂点的小功能,这个功能也是参考了陈硕同学的blog有感而发,下面就用bind和function来实现一个简单点的服务器框架吧,代码如下:
1)网络库方面
#ifndef __CONNECTION__H
#define __CONNECTION__H
#include <iostream>
#include <stdio.h>
#include <sys/socket.h>
#include <sys/epoll.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
using namespace std;
class Connection
{
public:
Connection(string ip = string(),int port = 0):peerIp(ip),peerPort(port)
{
sock = -1;
}
~Connection(){}
Connection(const Connection& conn)
{
sock = conn.sock;
peerIp = conn.peerIp;
peerPort = conn.peerPort;
}
Connection& operator = (const Connection& conn)
{
if(this == &conn) return *this;
sock = conn.sock;
peerIp = conn.peerIp;
peerPort = conn.peerPort;
}
int recv(void* buffer,int size)
{
return ::recv(sock,buffer,size,0);
}
int send(const void* buffer,int size)
{
return ::send(sock,buffer,size,0);
}
void displayConnInfo()
{
printf("peerIp:%s,peerPort:%d\n",peerIp.c_str(),peerPort);
}
void setSock(int sockfd)
{
sock = sockfd;
}
void setPeerIp(char* ip)
{
peerIp = ip;
}
void setPeerPort(int port)
{
peerPort = port;
}
int getSock()const
{
return sock;
}
string getPeerIp()const
{
return peerIp;
}
int getPeerPort() const
{
return peerPort;
}
private:
int sock;
string peerIp;
int peerPort;
};
#endif
#ifndef __NETSERVER__H
#define __NETSERVER__H
#include <boost/function.hpp>
#include <boost/bind.hpp>
#include <vector>
#include "Connection.h"
using namespace std;
#define MAX_EVENTS 255
class NetServer
{
public:
typedef boost::function<void(Connection*)> ConnectionCallback;
typedef boost::function<void(Connection*,const void*,int)> SendMessageCallback;
typedef boost::function<void(Connection*,void*,int&)> RecvMessageCallback;
NetServer(int port):port(port)
{
sock = -1;
epollfd = epoll_create(MAX_EVENTS);
connVec.clear();
}
~NetServer(){}
bool initNetServer()
{
sock = ::socket(AF_INET,SOCK_STREAM,0);
if(-1 == sock)
{
perror("sock init failed\n");
return false;
}
setNonBlock(sock);
struct sockaddr_in serverAddr;
bzero(&serverAddr,sizeof(serverAddr));
serverAddr.sin_family = AF_INET;
serverAddr.sin_port = htons(port);
serverAddr.sin_addr.s_addr = htons(INADDR_ANY);
if(bind(sock,(struct sockaddr*)&serverAddr,sizeof(serverAddr))<0)
{
perror("bind sock failed\n");
return false;
}
if(listen(sock,10)<0)
{
perror("listen sock failed\n");
return false;
}
addEvent(epollfd,EPOLLIN,sock);
return true;
}
void setNonBlock(int sockfd)
{
int ops;
ops = fcntl(sock,F_GETFL);
if(ops<0)
{
perror("fcntl get sock failed\n");
exit(1);
}
ops = ops | O_NONBLOCK;
if(fcntl(sock,F_SETFL,ops)<0)
{
perror("fcntl set sock failed\n");
exit(1);
}
}
bool addEvent(int epollfd,int events,int sock)
{
struct epoll_event ev;
bzero(&ev,sizeof(ev));
ev.data.fd = sock;
ev.events = events;
if(epoll_ctl(epollfd,EPOLL_CTL_ADD,sock,&ev)<0)
{
perror("add epoll failed\n");
return false;
}
return true;
}
bool delEvent(int epollfd,int events,int sock)
{
struct epoll_event ev;
bzero(&ev,sizeof(ev));
ev.data.fd = sock;
ev.events = events;
if(epoll_ctl(epollfd,EPOLL_CTL_DEL,sock,&ev)<0)
{
perror("del epoll failed\n");
return false;
}
return true;
}
Connection getNetServerConnection(int sockfd)
{
Connection temp;
for(connVecIter iter = connVec.begin();iter != connVec.end();++iter)
{
if(iter->getSock() == sockfd)
return *iter;
}
return temp;
}
void boardcast(Connection* conn)
{
char buffer[1024];
int recvlen = 0;
bzero(buffer,sizeof(buffer));
//recvMsgCallback(conn,buffer,recvlen);
recvlen = conn->recv(buffer,sizeof(buffer));
printf("MSG:%s\n",buffer);
for(int i=0;i<connVec.size();i++)
{
if(connVec[i].getSock() == conn->getSock()) continue;
sendMsgCallback(&connVec[i],buffer,recvlen);
}
}
void loop()
{
int nfds,connfd;
socklen_t clientlen;
struct sockaddr_in clientaddr;
struct epoll_event events[MAX_EVENTS];
bzero(events,sizeof(events));
for(;;)
{
nfds = epoll_wait(epollfd,events,MAX_EVENTS,500);
for(int i=0;i<nfds;i++)
{
if(events[i].data.fd == sock)
{
Connection conn;
bzero(&clientaddr,sizeof(clientaddr));
connfd = ::accept(sock,(struct sockaddr*)&clientaddr,&clientlen);
if(connfd < 0)
{
perror("accept failed\n");
continue;
}
setNonBlock(connfd);
addEvent(epollfd,EPOLLIN | EPOLLET,connfd);
conn.setSock(connfd);
conn.setPeerIp(inet_ntoa(clientaddr.sin_addr));
conn.setPeerPort(ntohs(clientaddr.sin_port));
connCallback(&conn);
connVec.push_back(conn);
printf("connVec.size():%d\n",connVec.size());
}
else if(events[i].events & EPOLLIN)
{
if(events[i].data.fd < 0) continue;
Connection conn = getNetServerConnection(events[i].data.fd);
if(conn.getSock()!=-1)
boardcast(&conn);
}
}
}
}
void registerConnectionCallback(const ConnectionCallback& f)
{
connCallback = f;
}
void registerSendMsgCallback(const SendMessageCallback& f)
{
sendMsgCallback = f;
}
void registerRecvMsgCallback(const RecvMessageCallback& f)
{
recvMsgCallback = f;
}
void sendMsg(Connection* conn,const void* buff,int len)
{
assert(conn);
conn->send(buff,len);
}
int recvMsg(Connection* conn,void* buff,int& len)
{
assert(conn);
return conn->recv(buff,len);
}
private:
int sock;
int port;
int epollfd;
ConnectionCallback connCallback;
SendMessageCallback sendMsgCallback;
RecvMessageCallback recvMsgCallback;
std::vector<Connection> connVec;
typedef std::vector<Connection>::iterator connVecIter;
};
#endif
2)实际使用的服务器代码
#ifndef __ECHO_SERVER__H
#define __ECHO_SERVER__H
#include<boost/function.hpp>
#include<boost/bind.hpp>
#include<iostream>
#include<stdio.h>
#include "Connection.h"
class EchoServer
{
public:
typedef boost::function<void(Connection*,const void*,int)> SendMessageCallback;
typedef boost::function<void(Connection*,void*,int&)> RecvMessageCallback;
EchoServer(const SendMessageCallback& sendMsgCb,const RecvMessageCallback& recvMsgCb):sendMessageCb_(sendM
{
}
void onSendMessage(Connection* conn,const void* buff,int size)
{
conn->displayConnInfo();
sendMessageCb_(conn,buff,size);
}
void onRecvMessage(Connection* conn,void* buff,int& size)
{
conn->displayConnInfo();
recvMessageCb_(conn,buff,size);
}
void onConnection(Connection* conn)
{
conn->displayConnInfo();
}
private:
SendMessageCallback sendMessageCb_;
RecvMessageCallback recvMessageCb_;
};
#endif
服务器端测试程序:
#include "NetServer.h"
#include "EchoServer.h"
int main()
{
NetServer netServer(19999);
EchoServer echoServer(bind(&NetServer::sendMsg,&netServer,_1,_2,_3),bind(&NetServer::recvMsg,&netServer,_1,_2,
netServer.registerSendMsgCallback(bind(&EchoServer::onSendMessage,&echoServer,_1,_2,_3));
netServer.registerRecvMsgCallback(bind(&EchoServer::onRecvMessage,&echoServer,_1,_2,_3));
netServer.registerConnectionCallback(bind(&EchoServer::onConnection,&echoServer,_1));
netServer.initNetServer();
netServer.loop();
return 0;
}
客户端测试程序:
#ifndef __LIBEVENT_SERVER__H
#define __LIBEVENT_SERVER__H
#include <iostream>
#include <string>
#include <string.h>
#include <stdlib.h>
#include <vector>
#include <event.h>
#include <boost/smart_ptr.hpp>
#include <pthread.h>
#include <sys/socket.h>
#include <sys/epoll.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <errno.h>
#include <fstream>
#include <iterator>
using namespace std;
using namespace boost;
#define MAX_CONN 10
#define MAX_BUFFERSIZE 1024
#define EVENT_INIT {event_init();}
namespace zmyer
{
struct Info
{
Info()
{
fd= -1;
clientname.clear();
}
Info(const Info& in)
{
clientname = in.clientname;
fd = in.fd;
}
Info(const Info* in)
{
clientname = in->clientname;
fd = in->fd;
}
~Info(){}
string clientname;
int fd;
};
struct Msg
{
Msg()
{
clientname.clear();
msg.clear();
}
Msg(const Msg& ms)
{
clientname = ms.clientname;
msg = ms.msg;
}
Msg& operator = (const Msg& ms)
{
clientname = ms.clientname;
msg = ms.msg;
}
~Msg(){}
void setClientName(string clientname)
{
this->clientname = clientname;
}
void setMsg(string msg)
{
this->msg = msg;
}
string serialize()
{
string tag = "#";
return (clientname + tag + msg);
}
void unserialize(string message)
{
string tag = "#";
string::size_type pos = message.find(tag);
if(pos == string::npos)
{
clientname ="";
msg = "";
}
else
{
clientname.assign(message,0,pos);
msg.assign(message,pos+1,(message.length() - pos));
}
}
string clientname;
string msg;
};
static void* send(void* arg)
{
shared_ptr<Info> p(new Info((Info*)arg));
string line,buffer;
for(;;)
{
line.clear();
buffer.clear();
getline(cin,line);
Msg msg;
msg.clientname = p->clientname;
msg.msg = line;
buffer = msg.serialize();
::send(p->fd,buffer.c_str(),strlen(buffer.c_str()),0);
}
return NULL;
}
static void* recv(void* arg)
{
shared_ptr<Info> p(new Info((Info*)arg));
char buffer[MAX_BUFFERSIZE];
for(;;)
{
bzero(buffer,sizeof(buffer));
int readlen = ::recv(p->fd,buffer,sizeof(buffer),0);
if(readlen <=0)
continue;
Msg msg;
msg.unserialize(buffer);
if(msg.clientname !="")
{
printf("%s : %s\n",msg.clientname.c_str(),msg.msg.c_str());
}
}
return NULL;
}
class libeventClient
{
public:
libeventClient(string ip = string(),string clientname = string(),const int port = 0):ip(ip),clientname
{
sock = -1;
}
~libeventClient(){}
bool init()
{
sock = ::socket(AF_INET,SOCK_STREAM,0);
if(-1 == sock)
{
printf("sock init failed\n");
return false;
}
struct sockaddr_in client_addr;
bzero(&client_addr,sizeof(client_addr));
client_addr.sin_family = AF_INET;
client_addr.sin_port = htons(0);
client_addr.sin_addr.s_addr = htons(INADDR_ANY);
if(bind(sock,(struct sockaddr*)&client_addr,sizeof(client_addr))<0)
{
printf("bind the address failed\n");
return false;
}
struct sockaddr_in server_addr;
bzero(&server_addr,sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(port);
inet_aton(ip.c_str(),&(server_addr.sin_addr));
if(::connect(sock,(struct sockaddr*)&server_addr,sizeof(server_addr))<0)
{
printf("connect to libeventserver failed\n");
return false;
}
return true;
}
void startSendThread()
{
Info in;
in.fd = sock;
in.clientname = clientname;
pthread_create(&sendThread,NULL,&send,&in);
}
void startRecvThread()
{
Info in;
in.fd = sock;
in.clientname = clientname;
pthread_create(&recvThread,NULL,&recv,&in);
}
void run()
{
startSendThread();
startRecvThread();
}
void stop()
{
pthread_join(sendThread,NULL);
pthread_join(recvThread,NULL);
}
private:
string ip;
string clientname;
int port;
int sock;
pthread_t recvThread,sendThread;
};
}
#endif
#include "libeventServer.h"
using namespace zmyer;
int main(int argc,char** argv)
{
if(argc < 2)
{
printf("usage:%s <clientname>\n",argv[0]);
return 0;
}
libeventClient libClient("127.0.0.1",argv[1],19999);
if(!libClient.init())
{
printf("libClient init failed\n");
return 0;
}
libClient.run();
sleep(60);
libClient.stop();
return 0;
}
测试结果:
服务器:
peerIp:0.0.0.0,peerPort:0
connVec.size():1
peerIp:127.0.0.1,peerPort:34423
connVec.size():2
MSG:B#hello,everyone
peerIp:0.0.0.0,peerPort:0
MSG:B#today is a good day!!!!!
peerIp:0.0.0.0,peerPort:0
MSG:A#en,what is your plan today????
peerIp:127.0.0.1,peerPort:34423
客户端A:
B : hello,everyone
B : today is a good day!!!!!
en,what is your plan today????
客户端B:
hello,everyone
today is a good day!!!!!
A : en,what is your plan today????
总结
这篇博文主要是学习了下boost::bind的一些常见用法,并且配合bind::function写了一个简单的服务器框架,在此基础上又重新改写了聊天室程序,通过使用echoServer这个类来扩展框架中的一些比较核心功能,在NetServer中主要是负责底层的事件监听,消息的发送,而上层EchoServer类主要是针对这些事件进行一些上层的预处理,本案例只是一个简单的测试用例,没有过多地扩展Connection和NetServer的功能,等有时间的话,在扩展一些功能,好了,本篇博文到此结束,其中有些东西也是参考了别人的案例加上自己的瞎想写的,不乏有错误,请见谅。
作者:zmyer