Linux下使用bind,epoll对网络编程封装
body, table{font-family: 微软雅黑; font-size: 13.5pt}
table{border-collapse: collapse; border: solid gray; border-width: 2px 0 2px 0;}
th{border: 1px solid gray; padding: 4px; background-color: #DDD;}
td{border: 1px solid gray; padding: 4px;}
tr:nth-child(2n){background-color: #f8f8f8;}
| InetAddress.h | InetAddress.cpp |
|
#ifndef __INETADDRESS_H__
#define __INETADDRESS_H__
#include<iostream>
#include<netinet/in.h>
using namespace std;
namespace meihao
{
class InetAddress
{
public:
InetAddress(unsigned short port);
InetAddress(const string& ip,unsigned short port);
InetAddress(struct sockaddr_in addr);
const struct sockaddr_in* getInetAddressPtr();
string ip()const;
unsigned short port()const;
private:
struct sockaddr_in _addr;
};
};
#endif
|
#include"InetAddress.h"
#include<iostream>
#include<strings.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
using namespace std;
namespace meihao
{
InetAddress::InetAddress(unsigned short port)
{
bzero(&_addr,sizeof(_addr));
_addr.sin_family = AF_INET;
_addr.sin_port = htons(port);
_addr.sin_addr.s_addr = INADDR_ANY;
}
InetAddress::InetAddress(const string& ip,unsigned short port)
{
bzero(&_addr,sizeof(_addr));
_addr.sin_family = AF_INET;
_addr.sin_port = htons(port);
_addr.sin_addr.s_addr = inet_addr(ip.c_str());
}
InetAddress::InetAddress(struct sockaddr_in addr)
{
_addr = addr;
}
const struct sockaddr_in* InetAddress::getInetAddressPtr()
{
return &_addr;
}
string InetAddress::ip()const
{
return string( inet_ntoa(_addr.sin_addr) );
}
unsigned short InetAddress::port()const
{
return ntohs(_addr.sin_port);
}
};
|
| Socket.h | Socket.cpp |
|
#ifndef __SOCKET_H__
#define __SOCKET_H__
#include<iostream>
#include"InetAddress.h"
using namespace std;
namespace meihao
{
class Socket
{
public:
Socket();
Socket(int fd);
void ready(const InetAddress& addr);
int accept();
void shutdownWrite();
int fd();
static InetAddress getLocalAddress(int fd);
static InetAddress getPeerAddress(int fd);
private:
void setReuseAddr(bool on);
void setReusePort(bool on);
void bind(const InetAddress& addr);
void listen();
private:
int _fd;
};
};
#endif
|
#include"Socket.h"
#include<iostream>
#include<sys/types.h>
#include<sys/socket.h>
#include<errno.h>
#include<stdio.h>
#include<stdlib.h>
#include<strings.h>
using namespace std;
#define handle_error(msg) \
do{\
perror(msg);\
exit(-1);\
}while(0);
namespace meihao
{
int getSocketfd()
{
int sfd = socket(AF_INET,SOCK_STREAM,0);
if(-1==sfd)
{
handle_error("socket");
}
}
Socket::Socket():_fd(getSocketfd())
{
}
Socket::Socket(int fd):_fd(fd)
{
}
void Socket::setReuseAddr(bool on)
{
int val = on?1:0;
int ret = setsockopt(_fd,SOL_SOCKET,SO_REUSEADDR,(const void*)&val,sizeof(int));
if(-1==ret)
{
handle_error("setsockopt");
}
}
void Socket::setReusePort(bool on)
{
int val = on?1:0;
int ret = setsockopt(_fd,SOL_SOCKET,SO_REUSEPORT,(const void *)&val,sizeof(int));
if(-1==ret)
{
handle_error("setsockopt");
}
}
void Socket::bind(const InetAddress& addr)
{
int ret = ::bind(_fd,(const struct sockaddr*)&addr,(socklen_t)sizeof(addr));
if(-1==ret)
{
handle_error("::bind");
}
}
void Socket::listen()
{
int ret = ::listen(_fd,10);
if(-1==ret)
{
handle_error("::listen");
}
}
void Socket::ready(const InetAddress& addr)
{
setReuseAddr(true);
setReusePort(true);
bind(addr);
listen();
}
int Socket::accept()
{
int new_fd = ::accept(_fd,NULL,NULL);
if(-1==new_fd)
{
handle_error("::accept");
}
}
void Socket::shutdownWrite()
{
int ret = ::shutdown(_fd,SHUT_WR);
if(-1==ret)
{
handle_error("::shutdown");
}
}
int Socket::fd()
{
return _fd;
}
InetAddress Socket::getLocalAddress(int fd)
{
struct sockaddr_in addr;
bzero(&addr,sizeof(addr));
int addrlen = sizeof(addr);
int ret = ::getsockname(fd,(struct sockaddr*)&addr,(socklen_t*)&addrlen);
if(-1==ret)
{
handle_error("::getsockname");
}
return InetAddress(addr);
}
InetAddress Socket::getPeerAddress(int fd)
{
struct sockaddr_in addr;
bzero(&addr,sizeof(addr));
int addrlen = sizeof(addr);
int ret = ::getpeername(fd,(struct sockaddr*)&addr,(socklen_t*)&addrlen);
if(-1==ret)
{
handle_error("::getpeername");
}
return InetAddress(addr);
}
};
|
| SocketIO.h | SocketIO.cpp |
|
#ifndef __SOCKETIO_H__
#define __SOCKETIO_H__
#include<iostream>
using namespace std;
namespace meihao
{
class SocketIO
{
public:
SocketIO(int fd);
int readn(char* buf,int count);
int writen(const char* buf,int count);
int readline(char* buf,int maxlen); // 成功返回读取字节数,失败返回-1
private:
int recvPeek(char* buf,int count); // 预读取一行
private:
int _fd;
};
};
#endif
|
#include"SocketIO.h"
#include<iostream>
#include<sys/types.h>
#include<sys/socket.h>
#include<errno.h>
#include<stdio.h>
#include<stdlib.h>
using namespace std;
namespace meihao
{
SocketIO::SocketIO(int fd):_fd(fd)
{
}
int SocketIO::readn(char* buf,int count)
{
int left = count;
char* ptmp = buf;
while(left>0)
{
int nread = ::recv(_fd,ptmp,left,0);
if(-1==nread)
{
if(errno==EINTR)
continue;
exit(EXIT_FAILURE);
}
else if(0==nread)
break;
left -= nread;
ptmp += nread;
}
return count-left; // 返回发送的数据个数
}
int SocketIO::writen(const char* buf,int count)
{
int left = count;
const char* ptmp = buf;
while(left>0)
{
int nread = ::send(_fd,ptmp,left,0);
if(-1==nread)
{
if(errno==EINTR)
continue;
exit(EXIT_FAILURE);
}
else if(0==nread)
break;
left -= nread;
ptmp += nread;
}
return count-left;
}
int SocketIO::recvPeek(char* buf,int count)
{
int nread = ::recv(_fd,buf,count,MSG_PEEK);
if(-1==nread)
{
perror("recvPeek");
}
}
int SocketIO::readline(char* buf,int maxlen) // 失败返回-1
{
int left = maxlen;
char* ptmp = buf;
int nread = 0;
while(left>0)
{
nread = recvPeek(ptmp,left);
if(-1==nread)
{
if(errno==EINTR)
continue;
return -1;
}
else if(0==nread)
return -1;
for(int idx=0;idx<nread;idx++)
{
if(ptmp[idx]=='\n')
{
if(readn(ptmp,nread)!=nread)
return -1;
left -= nread;
return maxlen-left;
}
}
// 预读取一行没有读到换行
if(readn(ptmp,nread)!=nread)
return -1;
ptmp += nread;
left -= nread;
}
return maxlen-left;
}
};
|
| TcpConnection.h | TcpConnection.cpp |
|
#ifndef __TCPCONNECTION_H__
#define __TCPCONNECTION_H__
#include<iostream>
#include"SocketIO.h"
#include"Socket.h"
#include"InetAddress.h"
#include<memory>
using namespace std;
namespace meihao
{
class TcpConnection;
typedef std::shared_ptr<TcpConnection> TcpConnectionPtr;
//利用智能共享指针托管TcpConnection类型指针,这样不用考虑最后的资源释放
typedef function<void(const TcpConnectionPtr& conn)> TcpConnectionCallback; // 这里要为public,不然后面的Epoll类访问不到,最开始我放到类里面了,放到外面访问更方便
//function函数对象绑定回调函数
class TcpConnection:public std::enable_shared_from_this<TcpConnection>
{
//继承enable_shared_from_this,使用里面的shared_from_this()方法传递指向函数
//本身的指针,防止出现shared_ptr误用造成多次释放或者拷贝
public:
TcpConnection(int confd); // 连接到的fd
~TcpConnection();
string receive();
void send(const string& msg);
void shutdown();
string toString(); // 输出服务器端信息和连接上的客户端的信息
void setConnectionCallback(TcpConnectionCallback cb);
void setMessageCallback(TcpConnectionCallback cb);
void setCloseCallback(TcpConnectionCallback cb);
void handleConnectionCallback(); // 客户端连接上后做出的操作
void handleMessageCallback(); // 服务器端-客户端之间发送消息
void handleCloseCallback(); // 服务器端关闭连接做出的行为
private:
Socket _sock;
SocketIO _sockIO;
InetAddress _localAddress;
InetAddress _peerAddress;
bool _isShutdownWrite;
TcpConnectionCallback _onConnectionCb; // 请求连接到服务器做出的行为
TcpConnectionCallback _onMessageCb; // 双方发送消息
TcpConnectionCallback _onCloseCb; // 关闭链接行为
};
};
#endif
|
#include"TcpConnection.h"
#include<iostream>
#include<strings.h>
#include<stdlib.h>
#include<sstream>
#include<errno.h>
#define handle_error(msg) \
do{\
perror(msg);\
exit(-1);\
}while(0);
using namespace std;
namespace meihao
{
TcpConnection::TcpConnection(int confd):_sock(confd)
,_sockIO(confd)
,_localAddress(meihao::Socket::getLocalAddress(confd))
,_peerAddress(meihao::Socket::getPeerAddress(confd))
,_isShutdownWrite(false)
{
//剩下的回调函数使用成员函数来设置,方便使用过程中修改
}
TcpConnection::~TcpConnection()
{
}
string TcpConnection::receive()
{
char buf[1024];
bzero(buf,sizeof(buf));
int ret = _sockIO.readline(buf,sizeof(buf));
if(-1==ret)
{
handle_error("recvive");
}
return string(buf);
}
void TcpConnection::send(const string& msg)
{
_sockIO.writen(msg.c_str(),msg.size());
}
void TcpConnection::shutdown()
{
if(!_isShutdownWrite) // 没有关闭
{
_isShutdownWrite = true;
_sock.shutdownWrite();
}
}
string TcpConnection::toString()
{
ostringstream oss;
oss<<_localAddress.ip()<<" "<<_localAddress.port()<<"--->"
<<_peerAddress.ip()<<" "<<_peerAddress.port()<<endl;
return oss.str();
}
// 开始初始化类里面的回调函数的值
void TcpConnection::setConnectionCallback(TcpConnectionCallback cb)
{
_onConnectionCb = cb;
}
void TcpConnection::setMessageCallback(TcpConnectionCallback cb)
{
_onMessageCb = cb;
}
void TcpConnection::setCloseCallback(TcpConnectionCallback cb)
{
_onCloseCb = cb;
}
void TcpConnection::handleConnectionCallback()
{
if(_onConnectionCb)
{
_onConnectionCb(shared_from_this()); // 传调用该函数的指针本身
// 等价于 shared_ptr<TcpConnection>(this);
}
}
void TcpConnection::handleMessageCallback()
{
if(_onMessageCb)
{
_onMessageCb(shared_from_this());
}
}
void TcpConnection::handleCloseCallback()
{
if(_onCloseCb)
{
_onCloseCb(shared_from_this());
}
}
};
|
| Epoll.h | Epoll.cpp |
|
#ifndef __EPOLL_H__
#define __EPOLL_H__
#include<iostream>
#include"TcpConnection.h"
#include<vector>
#include<sys/epoll.h>
#include<map>
using namespace std;
namespace meihao
{
class Epoll
{
public:
Epoll(int sfd);
void loop();
void unloop();
void waitEpollfd();
//在epoll类中用函数设置TcpConnection类中的私有变量
void epollSetConnectionCallback(TcpConnectionCallback cb);
void epollSetMessageCallback(TcpConnectionCallback cb);
void epollSetCloseCallback(TcpConnectionCallback cb);
private:
void handleConnection(); //epoll处理新连接的客户端
void handleMessage(int connfd); //处理消息
bool isConnected(int connfd); //查看描述符是否断开
private:
int _efd; // epoll_create得到的fd
int _sfd; // sockfd,用来监听客户端请求
bool _isLooping; // 是否还在监听客户端请求
vector<struct epoll_event> _events; //存放epoll_wait返回的监听描述符事件数组
map<int,TcpConnectionPtr> _mapConnections; // 存放所有客户端请求的描述符和对应的智能指针
TcpConnectionCallback _onConnectionCb; // 为监听到的客户端设置连接的回调函数
TcpConnectionCallback _onMessageCb;
TcpConnectionCallback _onCloseCb;
};
};
#endif
|
#include"Epoll.h"
#include<iostream>
#include<errno.h>
#include<stdio.h>
#include<stdlib.h>
#include<sys/epoll.h>
using namespace std;
#define handle_error(msg)\
do{\
perror(msg);\
exit(EXIT_FAILURE);\
}while(0);
namespace meihao
{
int createEpollfd()
{
int efd = ::epoll_create(1); //参数不为0即可
if(-1==efd)
{
handle_error("epoll_create");
}
return efd;
}
void addEpollfd(int efd,int fd)
{
struct epoll_event event;
event.events = EPOLLIN;
event.data.fd = fd;
int ret = epoll_ctl(efd,EPOLL_CTL_ADD,fd,&event);
if(-1==ret)
{
handle_error("epoll_ctl");
}
}
void delEpollfd(int efd,int fd)
{
struct epoll_event event;
event.events = EPOLLIN;
event.data.fd = fd;
int ret = epoll_ctl(efd,EPOLL_CTL_DEL,fd,&event);
if(-1==ret)
{
handle_error("epoll_ctl_del");
}
}
Epoll::Epoll(int sfd):_efd(createEpollfd())
,_sfd(sfd)
,_isLooping(false)
,_events(1024) // 容器提前开辟空间
{
addEpollfd(_efd,_sfd);
}
void Epoll::loop() // 开启监听客户端连接请求
{
_isLooping = true;
while(_isLooping)
{
waitEpollfd();
}
}
void Epoll::unloop() //关闭epoll监听
{
if(_isLooping)
_isLooping = false;
}
void Epoll::waitEpollfd()
{
int ret;
do
{
ret = ::epoll_wait(_efd,&(*_events.begin()),_events.size(),5000);
// 事件满足的fd会放到vector里面
}while(-1==ret&&errno==EINTR); // 被中断打断
if(-1==ret)
{
handle_error("epoll_wait");
}
else if(0==ret) // 5S后超时返回
{
cout<<"Epoll wait timeout!"<<endl;
}
for(int idx=0;idx!=ret;++idx)
{
if(_events[idx].data.fd ==_sfd &&_events[idx].events == EPOLLIN)
{//处理新连接
handleConnection();
}
else if(_events[idx].events == EPOLLIN)
{//处理已经连接上的客户端的输入请求
handleMessage(_events[idx].data.fd);
}
}
}
void Epoll::handleConnection()
{
int connfd = ::accept(_sfd,NULL,NULL);
addEpollfd(_efd,connfd);
//处理新的TCP链接
TcpConnectionPtr con(new TcpConnection(connfd));
cout<<"TcpConnectionPtr"<<con<<endl;
//设置TcpConnectionPtr的回调函数
con->setConnectionCallback(_onConnectionCb);
con->setMessageCallback(_onMessageCb);
con->setCloseCallback(_onCloseCb);
_mapConnections.insert(::make_pair(connfd,con)); //map中插入新连接的请求
con->handleConnectionCallback();
}
void Epoll::handleMessage(int connfd)
{
auto it = _mapConnections.find(connfd); //找到map中存放的对应描述符的关键字
if(it!=_mapConnections.end()) //如果描述符确实存在
{
bool flag = isConnected(connfd);
if(flag)
{//链接没有断开
it->second->handleMessageCallback(); //tcp连接处理消息,调用的是TcpConnection类里的函数
}
else
{//断开
delEpollfd(_efd,connfd);
it->second->handleCloseCallback(); //打印显示断开信息
_mapConnections.erase(it); //map中删除对应的pair
}
}
}
bool Epoll::isConnected(int connfd)
{
int ret;
char buf[512];
do
{
ret = recv(connfd,buf,sizeof(buf),MSG_PEEK); //从内核缓冲区预读取
}while(-1==ret&&errno==EINTR);
return ret>0; //>0表示有数据可读,链接没有断开
}
void Epoll::epollSetConnectionCallback(TcpConnectionCallback cb)
{
_onConnectionCb = cb;
}
void Epoll::epollSetMessageCallback(TcpConnectionCallback cb)
{
_onMessageCb = cb;
}
void Epoll::epollSetCloseCallback(TcpConnectionCallback cb)
{
_onCloseCb = cb;
}
};
|
| TcpServer.h | TcpServer.cpp |
|
#ifndef __TCPSERVER_H__
#define __TCPSERVER_H__
#include<iostream>
#include"InetAddress.h"
#include"Socket.h"
#include"Epoll.h"
#include"TcpConnection.h"
using namespace std;
namespace meihao
{
class TcpServer
{
public:
TcpServer(unsigned short port);
TcpServer(const string& ip,unsigned short port);
TcpServer(const InetAddress& addr);
void start();
void stop();
void tcpServerSetConnectionCallback(TcpConnectionCallback cb);
void tcpServerSetMessageCallback(TcpConnectionCallback cb);
void tcpServerSetCloseCallback(TcpConnectionCallback cb);
private:
InetAddress _addr;
Socket _serverSock;
Epoll _epoll;
TcpConnectionCallback _onConnectionCb;
TcpConnectionCallback _onMessageCb;
TcpConnectionCallback _onCloseCb;
};
};
#endif
|
#include"TcpServer.h"
#include<iostream>
using namespace std;
namespace meihao
{
TcpServer::TcpServer(unsigned short port):_addr(port)
,_serverSock()
,_epoll(_serverSock.fd())
{
}
TcpServer::TcpServer(const string& ip,unsigned short port):_addr(ip,port)
,_serverSock()
,_epoll(_serverSock.fd())
{
}
TcpServer::TcpServer(const InetAddress& addr):_addr(addr)
,_serverSock()
,_epoll(_serverSock.fd())
{
}
void TcpServer::start()
{
_serverSock.ready(_addr); // 开启服务器,并监听
//epoll类设置请求的tcp连接的回调函数
_epoll.epollSetConnectionCallback(_onConnectionCb);
_epoll.epollSetMessageCallback(_onMessageCb);
_epoll.epollSetCloseCallback(_onCloseCb);
_epoll.loop(); // epoll_wait客户端连接请求
}
void TcpServer::stop()
{
_epoll.unloop();
_serverSock.shutdownWrite();
}
void TcpServer::tcpServerSetConnectionCallback(TcpConnectionCallback cb)
{
_onConnectionCb = cb;
}
void TcpServer::tcpServerSetMessageCallback(TcpConnectionCallback cb)
{
_onMessageCb = cb;
}
void TcpServer::tcpServerSetCloseCallback(TcpConnectionCallback cb)
{
_onCloseCb = cb;
}
};
|
| server.cpp | Makefile |
|
#include<iostream>
#include"TcpServer.h"
#include"TcpConnection.h"
using namespace std;
void onConnection(const meihao::TcpConnectionPtr& conn)
{
cout<<conn->toString()<<"has connected!"<<endl;
conn->send("connect success!\n");
}
void onMessage(const meihao::TcpConnectionPtr& conn)
{
string msg = conn->receive();
cout<<"recv:"<<msg;
conn->send(msg);
}
void onClosed(const meihao::TcpConnectionPtr& conn)
{
cout<<conn->toString()<<"has closed!"<<endl;
}
int main()
{
meihao::TcpServer tcpServer(8848);
tcpServer.tcpServerSetConnectionCallback(onConnection);
tcpServer.tcpServerSetMessageCallback(onMessage);
tcpServer.tcpServerSetCloseCallback(onClosed);
tcpServer.start();
}
|
OBJS:=*.cpp
ELF:=server
$(ELF):$(OBJS)
g++ $(OBJS) -o $(ELF) -std=c++11
.PHONY:rebuild clean
rebuild:
make clean $(ELF)
clean:
rm -rf $(ELF)
|
| client.cpp |
#include<iostream>
#include"InetAddress.h"
#include"SocketIO.h"
#include<strings.h>
#include<unistd.h>
using namespace std;
int main()
{
meihao::InetAddress inetAddr(8848);
int sfd = socket(AF_INET,SOCK_STREAM,0);
if(-1==sfd)
{
perror("socket");
exit(EXIT_FAILURE);
}
int ret = connect(sfd,(const struct sockaddr*)inetAddr.getInetAddressPtr(),sizeof(struct sockaddr));
if(-1==ret)
{
perror("connect");
exit(EXIT_FAILURE);
}
meihao::SocketIO socketIO(sfd);
char buf[1024] = "";
socketIO.readline(buf,sizeof(buf));
cout<<buf;
while(1)
{
bzero(buf,sizeof(buf));
read(0,buf,sizeof(buf));
socketIO.writen(buf,sizeof(buf));
bzero(buf,sizeof(buf));
socketIO.readline(buf,sizeof(buf));
cout<<"recv from server:"<<buf;
}
}
|
Linux下使用bind,epoll对网络编程封装的更多相关文章
- Linux下C语言的socket网络编程
关于详细的服务器建立的步骤以及相关的socket套接字的知识我已经在python socket编程的文章中提到过了,大家可以参看那一篇博客来历接socket套接字编程的内容,由于要是用C相关的API所 ...
- 使用c语言实现在linux下的openssl客户端和服务器端编程
使用c语言实现在linux下的openssl客户端和服务器端编程 摘自:https://www.cnblogs.com/etangyushan/p/3679457.html 前几天组长让我实现一个使用 ...
- linux下select/poll/epoll机制的比较
select.poll.epoll简介 epoll跟select都能提供多路I/O复用的解决方案.在现在的Linux内核里有都能够支持,其中epoll是Linux所特有,而select则应该是POSI ...
- linux下ACE使用epoll
select和epoll的比较就不用多说了.ACE在linux下默认使用select来实现Reactor的.如何在linux下让ACE使用epoll. 1.加一个编译宏,告诉ACE不要使用默认的sel ...
- linux下测试web访问及网络相关的命令
curl命令 curl是linux系统命令行下用来简单测试web访问的工具. curl -xip:port www.baidu.com -x可以指定ip和端口,省略写hosts,方便实用 -I ...
- linux下select,poll,epoll的使用与重点分析
好久没用I/O复用了,感觉差点儿相同都快忘完了.记得当初刚学I/O复用的时候花了好多时间.可是因为那会不太爱写博客,导致花非常多时间搞明确的东西,依旧非常easy忘记.俗话说眼过千遍不如手过一遍,的确 ...
- Linux下C与Mysql的混合编程(转)
1 概述 MySQL 是一个关系型数据库管理系统.由瑞典MySQL AB公司开发,眼下属于Oracle公司.MySQL是最流行的关系型数据库管理系统. 支持AIX.FreeBSD.HP-UX.Linu ...
- Linux下管道重定向使用以及Shell编程(操作系统)
实验名称:Linux的基本操作 实验目的: 1.了解管道和重定向 2.熟悉基本的Linux脚本的编写 实验环境:Ubuntu 12.4(32位,简体中文) 实验内容: 1.将当前用户目录下的文件清单输 ...
- Linux下C与Mysql的混合编程
1 概述 MySQL 是一个关系型数据库管理系统.由瑞典MySQL AB公司开发,眼下属于Oracle公司.MySQL是最流行的关系型数据库管理系统. 支持AIX.FreeBSD.HP-UX.Linu ...
随机推荐
- 取代iframe框架
一.frameset1. 属性①border设置框架的边框粗细.②bordercolor设置框架的边框颜色.③frameborder设置是否显示框架边框.设定值只有0.1:0 表示不要边框,1 表示要 ...
- pytorch 中的 split
Pytorch中的split问题: 1.使用torch.nn.Conv2d中有个参数是groups会将输入的feature map分组,此处需要注意的一点是分组之后各组的feature map的cha ...
- 小程序授权demo
<button wx:if="{{canIUse}}" open-type="getUserInfo" bindgetuserinfo=" ...
- JavaScript高级程序设计笔记(一)
---恢复内容开始--- 前三章为基础知识,为了方便以后查看,所以比较啰嗦.这里对函数的基本操作没有记录. 1.JavaScript的实现 虽然 JavaScript 和 ECMAScript 通常都 ...
- 【模板/经典题型】FWT
FWT在三种位运算下都满足FWT(a×b)=FWT(a)*FWT(b) 其中or卷积和and卷积还可以通过FMT实现(本质上就是个高维前缀和) #include<bits/stdc++.h> ...
- AVL平衡二叉树的各种问题(Balanced Binary Tree)
AVL树或者是一棵空树,或者是具有以下性质的非空二叉搜索树: 1. 任一结点的左.右子树均为AVL树: 2.根结点左.右子树高度差的绝对值不超过1. 1.声明 #include<iostream ...
- Linux中常用压缩打包工具
Linux中常用压缩打包工具 压缩打包是常用的功能,在linux中目前常用的压缩工具有gzip,bzip2以及后起之秀xz.本文将介绍如下的工具常见压缩.解压缩工具以及打包工具tar. gzip2 直 ...
- Permutations CodeForces - 736D (矩阵逆)
对于删除每个对(x,y), 可以发现他对答案的贡献为代数余子式$A_{xy}$ 复习了一下线代后发现代数余子式可以通过伴随矩阵求出, 即$A_{xy}=A^*[y][x]$, 伴随矩阵$A^*=|A| ...
- TP5+jquery即点既改
//表单 {volist name="date" id="v"}<tr id="{$v.id}"> <td>< ...
- Analytic Functions in Oracle
Contents Overview and IntroductionHow Analytic Functions WorkThe SyntaxExamplesCalculate a running T ...