Please indicate the source: http://blog.csdn.net/gaoxiangnumber1
Welcome to my github: https://github.com/gaoxiangnumber1
其中的难点
非阻塞网络编程中,为什么要使用应用层发送缓冲区?
非阻塞网络编程中,为什么要使用应用层接收缓冲区?
非阻塞网络编程中,如何设计并使用缓冲区?
//examples/simple/echo/echo.h
#include <muduo/net/TcpServer.h>
class EchoServer
{
public:
EchoServer(muduo::net::EventLoop *loop,
const muduo::net::InetAddress &listen_address);
void Start(); // call server_.start()
private:
void OnConnection(const muduo::net::TcpConnectionPtr &connection);
void OnMessage(const muduo::net::TcpConnectionPtr &connection,
muduo::net::Buffer *buffer,
muduo::Timestamp time);
muduo::net::TcpServer server_;
};
//examples/simple/echo/echo.cc
EchoServer::EchoServer(EventLoop *loop,
const InetAddress &listen_address)
: server_(loop, listen_address, "EchoServer")
{
server_.setConnectionCallback(bind(&EchoServer::OnConnection, this, _1));
server_.setMessageCallback(bind(&EchoServer::OnMessage, this, _1, _2, _3));
}
//examples/simple/echo/echo.cc
void EchoServer::OnConnection(const TcpConnectionPtr &connection)
{
LOG_INFO << "EchoServer - " << connection->peerAddress().toIpPort() << " -> "
<< connection->localAddress().toIpPort() << " is "
<< (connection->connected() ? "UP" : "DOWN");
}
void EchoServer::OnMessage(const TcpConnectionPtr &connection,
Buffer* buffer,
Timestamp time)
{
string message(buffer->retrieveAllAsString()); // L37
LOG_INFO << connection->name() << " echo " << message.size() << " bytes, "
<< "data received at " << time.toString();
connection->send(message); // L40
}
send(msg)
是否完整地发送数据,因为网络库会帮我们管理发送缓冲区。TcpConnection::connected()
返回bool,表明目前连接是建立还是断开;TcpConnection的peerAddress()和localAddress()成员函数分别返回对方和本地的地址(以InetAddress对象表示的IP和port)。//examples/simple/echo/main.cc
#include "echo.h"
#include <muduo/base/Logging.h>
#include <muduo/net/EventLoop.h>
using muduo::net::EventLoop;
using muduo::net::InetAddress;
int main()
{
LOG_INFO << "pid = " << getpid();
EventLoop loop;
InetAddress listen_address(7188);
EchoServer server(&loop, listen_address);
server.start();
loop.loop();
}
协议
请求:[id:]<81digits>\r\n
响应:[id:]<81digits>\r\n
或者:[id:]NoSolution\r\n
例1请求:000000010400000000020000000000050407008000300001090000300400200050100000000806000\r\n
响应:693784512487512936125963874932651487568247391741398625319475268856129743274836159\r\n
例2请求:a:000000010400000000020000000000050407008000300001090000300400200050100000000806000\r\n
响应:a:693784512487512936125963874932651487568247391741398625319475268856129743274836159\r\n
例3请求:b:000000010400000000020000000000050407008000300001090000300400200050100000000806005\r\n
响应:
b:NoSolution\r\n
string solveSudoku(const string &puzzle);
//examples/sudoku/server_basic.cc
const int kCells = 81; // Cells' number.
void OnMessage(const TcpConnectionPtr &connection, Buffer* buffer, Timestamp)
{
LOG_DEBUG << connection->name();
size_t len = buffer->readableBytes();
while(len >= kCells + 2) // Read request data repeatedly. 2 stands for CRLF(\r\n)
{
const char* crlf = buffer->findCRLF();
if(crlf) // If found a complete request.
{
string request(buffer->peek(), crlf); // Copy request data.
buffer->retrieveUntil(crlf + 2); // Retrieve data that has been read.
len = buffer->readableBytes(); // Update buffer's length
if(ProcessRequest(connection, request) == false) // Illegal request, close the connection.
{
connection->send("Bad Request!\r\n");
connection->shutdown();
break;
}
}
else // Incomplete request.
{
break;
}
}
}
bool ProcessRequest(const TcpConnectionPtr &connection, const string &request)
{
string id;
string puzzle;
bool good_request = true;
string::const_iterator colon = find(request.begin(), request.end(), ':');
if(colon != request.end()) // If found the `id` part.
{
id.assign(request.begin(), colon);
puzzle.assign(colon+1, request.end());
}
else
{
puzzle = request;
}
if(puzzle.size() == const_cast<size_t>(kCells)) // Request's length is legal.
{
LOG_DEBUG << connection->name();
string result = SolveSudoku(puzzle); // Get answer at here.
if(id.empty())
{
connection->send(result+"\r\n");
}
else
{
connection->send(id+":"+result+"\r\n");
}
}
else
{
good_request = false;
}
return good_request;
}
方案0:accept+read/write
recipes/python/echo-iterative.py
1 #!/usr/bin/python
2
3 import socket
4
5 def handle(client_socket, client_address):
6 while True:
7 data = client_socket.recv(4096)
8 if data:
9 sent = client_socket.send(data)
10 else:
11 print "disconnect", client_address
12 client_socket.close()
13 break
14
15 if __name__ == "__main__":
16 listen_address = ("0.0.0.0", 2007)
17 server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
18 server_socket.bind(listen_address)
19 server_socket.listen(5)
20
21 while True:
22 (client_socket, client_address) = server_socket.accept()
23 print "got connection from", client_address
24 handle(client_socket, client_address)
方案1:accept+fork
recipes/python/echo-fork.py
1 #!/usr/bin/python
2
3 from SocketServer import BaseRequestHandler, TCPServer
4 from SocketServer import ForkingTCPServer, ThreadingTCPServer
5
6 class EchoHandler(BaseRequestHandler):
7 def handle(self):
8 print "got connection from", self.client_address
9 while True:
10 data = self.request.recv(4096)
11 if data:
12 sent = self.request.send(data)
13 else:
14 print "disconnect", self.client_address
15 self.request.close()
16 break
17
18 if __name__ == "__main__":
19 listen_address = ("0.0.0.0", 2007)
20 server = ForkingTCPServer(listen_address, EchoHandler)
21 server.serve_forever()
self.request
代替前面的client_socket。ForkingTCPServer会对每个客户连接新建一个子进程,在子进程中调用EchoHandler.handle(),从而同时服务多个客户端。方案2:accept+thread
$ diff -U2 echo-fork.py echo-thread.py
if __name__ == "__main__":
listen_address = ("0.0.0.0", 2007)
- server = ForkingTCPServer(listen_address, EchoHandler)
+ server = ThreadingTCPServer(listen_address, EchoHandler)
server.serve_forever()
$ diff -U2 echo-fork.py echo-single.py
if __name__ == "__main__":
listen_address = ("0.0.0.0", 2007)
- server = ForkingTCPServer(listen_address, EchoHandler)
+ server = TCPServer(listen_address, EchoHandler)
server.serve_forever()
方案3:prefork
方案4:pre threaded
recipes/python/echo-poll.py
6 server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
7 server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
8 server_socket.bind(('', 2007))
9 server_socket.listen(5)
10 # server_socket.setblocking(0)
11 poll = select.poll() # epoll() should work the same
12 poll.register(server_socket.fileno(), select.POLLIN)
13
14 connections = {}
15 while True:
16 events = poll.poll(10000) # 10 seconds
17 for fileno, event in events:
18 if fileno == server_socket.fileno():
19 (client_socket, client_address) = server_socket.accept()
20 print "got connection from", client_address
21 # client_socket.setblocking(0)
22 poll.register(client_socket.fileno(), select.POLLIN)
23 connections[client_socket.fileno()] = client_socket
24 elif event & select.POLLIN:
25 client_socket = connections[fileno]
26 data = client_socket.recv(4096)
27 if data:
28 client_socket.send(data) # sendall() partial?
29 else:
30 poll.unregister(fileno)
31 client_socket.close()
32 del connections[fileno]
$ diff echo-poll.py chat-poll.py -U4
--- echo-poll.py 2015-04-02 15:36:58.000000000 +0800
+++ chat-poll.py 2015-04-02 15:36:58.000000000 +0800
@@ -24,9 +24,11 @@
23 elif event & select.POLLIN:
24 client_socket = connections[fileno]
25 data = client_socket.recv(4096)
26 if data:
27 - client_socket.send(data) # sendall() partial?
28 + for (fd, other_socket) in connections.iteritems():
29 + if other_socket != client_socket:
30 + other_socket.send(data) # sendall() partial?
31 else:
32 poll.unregister(fileno)
33 client_socket.close()
34 del connections[fileno]
方案5:poll(reactor)
recipes/python/echo-reactor.py
6 server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
7 server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
8 server_socket.bind(('', 2007))
9 server_socket.listen(5)
10 # serversocket.setblocking(0)
11
12 poll = select.poll() # epoll() should work the same
13 connections = {}
14 handlers = {}
15
16 def handle_input(socket, data):
17 socket.send(data) # sendall() partial?
18
19 def handle_request(fileno, event):
20 if event & select.POLLIN:
21 client_socket = connections[fileno]
22 data = client_socket.recv(4096)
23 if data:
24 handle_input(client_socket, data)
25 else:
26 poll.unregister(fileno)
27 client_socket.close()
28 del connections[fileno]
29 del handlers[fileno]
30
31 def handle_accept(fileno, event):
32 (client_socket, client_address) = server_socket.accept()
33 print "got connection from", client_address
34 # client_socket.setblocking(0)
35 poll.register(client_socket.fileno(), select.POLLIN)
36 connections[client_socket.fileno()] = client_socket
37 handlers[client_socket.fileno()] = handle_request
38
39 poll.register(server_socket.fileno(), select.POLLIN)
40 handlers[server_socket.fileno()] = handle_accept
41
42 while True:
43 events = poll.poll(10000) # 10 seconds
44 for fileno, event in events:
45 handler = handlers[fileno]
46 handler(fileno, event)
$ diff -U1 echo-reactor.py chat-reactor.py
def handle_input(socket, data):
- socket.send(data) # sendall() partial?
+ for (fd, other_socket) in connections.iteritems():
+ if other_socket != socket:
+ other_socket.send(data) # sendall() partial?
方案6:reactor + thread-per-task
方案7:reactor + worker thread
方案8:reactor + thread pool
bool ProcessRequest(const TcpConnectionPtr &connection, const string &request)
{
string id;
string puzzle;
bool goodRequest = true;
string::const_iterator colon = find(request.begin(), request.end(), ':');
if (colon != request.end())
{
id.assign(request.begin(), colon);
puzzle.assign(colon+1, request.end());
}
else
{
puzzle = request;
}
if (puzzle.size() == static_cast<size_t>(kCells))
{
thread_pool_.run(bind(&solve, connection, puzzle, id));
}
else
{
goodRequest = false;
}
return goodRequest;
}
static void solve(const TcpConnectionPtr &connection,
const string &puzzle,
const string &id)
{
LOG_DEBUG << connection->name();
string result = SolveSudoku(puzzle);
if (id.empty())
{
connection->send(result+"\r\n");
}
else
{
connection->send(id+":"+result+"\r\n");
}
}
方案9:reactors in threads
server_.setThreadNum(numThreads);
$ diff -u server_basic.cc server_multiloop.cc
--- server_basic.cc 2016-09-28 01:02:54.000000000 +0800
+++ server_multiloop.cc 2016-09-28 01:02:54.000000000 +0800
@@ -20,18 +20,21 @@
class SudokuServer
{
public:
- SudokuServer(EventLoop* loop, const InetAddress& listenAddr)
+ SudokuServer(EventLoop* loop, const InetAddress& listenAddr, int numThreads)
: server_(loop, listenAddr, "SudokuServer"),
+ numThreads_(numThreads),
startTime_(Timestamp::now())
{
server_.setConnectionCallback(
boost::bind(&SudokuServer::onConnection, this, _1));
server_.setMessageCallback(
boost::bind(&SudokuServer::onMessage, this, _1, _2, _3));
+ server_.setThreadNum(numThreads);
}
void start()
{
+ LOG_INFO << "starting " << numThreads_ << " threads.";
server_.start();
}
@@ -113,15 +116,21 @@
}
TcpServer server_;
+ int numThreads_;
Timestamp startTime_;
};
int main(int argc, char* argv[])
{
LOG_INFO << "pid = " << getpid() << ", tid = " << CurrentThread::tid();
+ int numThreads = 0;
+ if (argc > 1)
+ {
+ numThreads = atoi(argv[1]);
+ }
EventLoop loop;
InetAddress listenAddr(9981);
- SudokuServer server(&loop, listenAddr);
+ SudokuServer server(&loop, listenAddr, numThreads);
server.start();
方案10:reactors in processes
方案11
server_.setThreadNum(numThreads);
就成为该方案的代码。结语
Please indicate the source: http://blog.csdn.net/gaoxiangnumber1
Welcome to my github: https://github.com/gaoxiangnumber1