1. 程式人生 > >Mina、Netty、Twisted一起學(一):實現簡單的TCP伺服器

Mina、Netty、Twisted一起學(一):實現簡單的TCP伺服器

MINA、Netty、Twisted為什麼放在一起學習?首先,不妨先分別看一下它們官方網站對其的介紹:

MINA:

Apache MINA is a network application framework which helps users develop high performance and high scalability network applications easily. It provides an abstract event-driven asynchronous API over various transports such as TCP/IP and UDP/IP via Java NIO.


Netty:

Netty is an asynchronous event-driven network application framework for rapid development of maintainable high performance protocol servers & clients.

Twisted:

Twisted is an event-driven networking engine written in Python and licensed under the open source MIT license.

(Twisted官網的文案不專業啊,居然不寫asynchronous)

從上面簡短的介紹中,就可以發現它們的共同特點:event-driven以及asynchronous。它們都是事件驅動、非同步的網路程式設計框架。由此可見,它們之間的共同點還是很明顯的。所以我這裡將這三個框架放在一起,實現相同的功能,不但可以用少量的精力學三樣東西,而且還可以對它們之間進行各方面的對比。

其中MINA和Netty是基於Java語言的,而Twisted是Python語言的。不過語言不是重點,重點的是理念。

使用傳統的BIO(Blocking IO/阻塞IO)進行網路程式設計時,進行網路IO讀寫時都會阻塞當前執行緒,如果實現一個TCP伺服器,都需要對每個客戶端連線開啟一個執行緒,而很多執行緒可能都在傻傻的阻塞住等待讀寫資料,系統資源消耗大。

而NIO(Non-Blocking IO/非阻塞IO)或AIO(Asynchronous IO/非同步IO)則是通過IO多路複用技術實現,不需要為每個連線建立一個執行緒,其底層實現是通過作業系統的一些特性如select、poll、epoll、iocp等。這三個網路框架都是基於此實現。

下面分別用這三個框架實現一個最簡單的TCP伺服器。當接收到客戶端發過來的字串後,向客戶端回寫一個字串作為響應。

Mina:

public class TcpServer {

	public static void main(String[] args) throws IOException {
		IoAcceptor acceptor = new NioSocketAcceptor();
		acceptor.setHandler(new TcpServerHandle());
		acceptor.bind(new InetSocketAddress(8080));
	}

}

class TcpServerHandle extends IoHandlerAdapter {
	
	@Override
	public void exceptionCaught(IoSession session, Throwable cause) throws Exception {
		cause.printStackTrace();
	}

	// 接收到新的資料
	@Override
	public void messageReceived(IoSession session, Object message) throws Exception {
		
		// 接收客戶端的資料
		IoBuffer ioBuffer = (IoBuffer) message;
		byte[] byteArray = new byte[ioBuffer.limit()];
		ioBuffer.get(byteArray, 0, ioBuffer.limit());
		System.out.println("messageReceived:" + new String(byteArray, "UTF-8"));
		
		// 傳送到客戶端
		byte[] responseByteArray = "你好".getBytes("UTF-8");
		IoBuffer responseIoBuffer = IoBuffer.allocate(responseByteArray.length);
		responseIoBuffer.put(responseByteArray);
		responseIoBuffer.flip();
		session.write(responseIoBuffer);
	}

	@Override
	public void sessionCreated(IoSession session) throws Exception {
		System.out.println("sessionCreated");
	}
	
	@Override
	public void sessionClosed(IoSession session) throws Exception {
		System.out.println("sessionClosed");
	}
}

Netty:
public class TcpServer {

	public static void main(String[] args) throws InterruptedException {
		EventLoopGroup bossGroup = new NioEventLoopGroup();
		EventLoopGroup workerGroup = new NioEventLoopGroup();
		try {
			ServerBootstrap b = new ServerBootstrap();
			b.group(bossGroup, workerGroup)
					.channel(NioServerSocketChannel.class)
					.childHandler(new ChannelInitializer<SocketChannel>() {
						@Override
						public void initChannel(SocketChannel ch)
								throws Exception {
							ch.pipeline().addLast(new TcpServerHandler());
						}
					});
			ChannelFuture f = b.bind(8080).sync();
			f.channel().closeFuture().sync();
		} finally {
			workerGroup.shutdownGracefully();
			bossGroup.shutdownGracefully();
		}
	}

}

class TcpServerHandler extends ChannelInboundHandlerAdapter {

	// 接收到新的資料
	@Override
	public void channelRead(ChannelHandlerContext ctx, Object msg) throws UnsupportedEncodingException {
		try {
			// 接收客戶端的資料
			ByteBuf in = (ByteBuf) msg;
			System.out.println("channelRead:" + in.toString(CharsetUtil.UTF_8));
			
			// 傳送到客戶端
			byte[] responseByteArray = "你好".getBytes("UTF-8");
			ByteBuf out = ctx.alloc().buffer(responseByteArray.length);
			out.writeBytes(responseByteArray);
			ctx.writeAndFlush(out);
			
		} finally {
			ReferenceCountUtil.release(msg);
		}
	}
	
	@Override
	public void channelActive(ChannelHandlerContext ctx) {
		System.out.println("channelActive");
	}
	
	@Override
	public void channelInactive(ChannelHandlerContext ctx){
		System.out.println("channelInactive");
	}

	@Override
	public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
		cause.printStackTrace();
		ctx.close();
	}
}

Twisted:
# -*- coding:utf-8 –*-

from twisted.internet.protocol import Protocol
from twisted.internet.protocol import Factory
from twisted.internet import reactor

class TcpServerHandle(Protocol):
    
    # 新的連線建立
    def connectionMade(self):
        print 'connectionMade'
        
    # 連線斷開
    def connectionLost(self, reason):
        print 'connectionLost'
    
    # 接收到新資料
    def dataReceived(self, data):
        print 'dataReceived', data
        self.transport.write('你好')

factory = Factory()
factory.protocol = TcpServerHandle
reactor.listenTCP(8080, factory)
reactor.run()

上面的程式碼可以看出,這三個框架實現的TCP伺服器,在連線建立、接收到客戶端傳來的資料、連線關閉時,都會觸發某個事件。例如接收到客戶端傳來的資料時,MINA會觸發事件呼叫messageReceived,Netty會呼叫channelRead,Twisted會呼叫dataReceived。編寫程式碼時,只需要繼承一個類並重寫響應的方法即可。這就是event-driven事件驅動。

下面是Java寫的一個TCP客戶端用作測試,客戶端沒有使用這三個框架,也沒有使用NIO,只是一個普通的BIO的TCP客戶端。

TCP在建立連線到關閉連線的過程中,可以多次進行傳送和接收資料。下面的客戶端傳送了兩個字串到伺服器並兩次獲取伺服器迴應的資料,之間通過Thread.sleep(5000)間隔5秒。

public class TcpClient {
	
	public static void main(String[] args) throws IOException, InterruptedException {
		
		
		Socket socket = null;
		OutputStream out = null;
		InputStream in = null;
		
		try{
			
			socket = new Socket("localhost", 8080);      
			out = socket.getOutputStream();
			in = socket.getInputStream();
			
		    // 請求伺服器
		    out.write("第一次請求".getBytes("UTF-8"));
		    out.flush();
		            
		    // 獲取伺服器響應,輸出
		    byte[] byteArray = new byte[1024];
		    int length = in.read(byteArray);
		    System.out.println(new String(byteArray, 0, length, "UTF-8"));
		    
		    Thread.sleep(5000);
		    
		    // 再次請求伺服器
		    out.write("第二次請求".getBytes("UTF-8"));
		    out.flush();
		    
		    // 再次獲取伺服器響應,輸出
		    byteArray = new byte[1024];
		    length = in.read(byteArray);
		    System.out.println(new String(byteArray, 0, length, "UTF-8"));
		    
		    
		} finally {
			// 關閉連線
			in.close();
			out.close();
			socket.close();
		}
		
	}

}

用客戶端分別測試上面三個TCP伺服器:

MINA伺服器輸出結果:

sessionCreated
messageReceived:第一次請求
messageReceived:第二次請求
sessionClosed

Netty伺服器輸出結果:

channelActive
channelRead:第一次請求
channelRead:第二次請求
channelInactive

Twisted伺服器輸出結果:

connectionMade
dataReceived: 第一次請求
dataReceived: 第二次請求
connectionLost

MINA、Netty、Twisted一起學系列

原始碼


相關推薦

MinaNettyTwisted一起實現簡單TCP伺服器

MINA、Netty、Twisted為什麼放在一起學習?首先,不妨先分別看一下它們官方網站對其的介紹:MINA:Apache MINA is a network application framework which helps users develop high perf

MinaNettyTwisted一起整合protobuf

ear ive obj con line 谷歌 encode etc 創建 protobuf是谷歌的Protocol Buffers的簡稱,用於結構化數據和字節碼之間互相轉換(序列化、反序列化),一般應用於網絡傳輸,可支持多種編程語言。protobuf怎樣使用這裏不再介紹

MinaNettyTwisted一起HTTP伺服器

HTTP協議應該是目前使用最多的應用層協議了,用瀏覽器開啟一個網站就是使用HTTP協議進行資料傳輸。HTTP協議也是基於TCP協議,所以也有伺服器和客戶端。HTTP客戶端一般是瀏覽器,當然還有可能是其他東西。HTTP伺服器,也就是Web伺服器,目前已經有很多成熟的產品,例如A

MinaNettyTwisted一起執行緒模型

要想開發一個高效能的TCP伺服器,熟悉所使用框架的執行緒模型非常重要。MINA、Netty、Twisted本身都是高效能的網路框架,如果再搭配上高效率的程式碼,才能實現一個高大上的伺服器。但是如果不瞭解它們的執行緒模型,就很難寫出高效能的程式碼。框架本身效率再高,程式寫的太差

關於wxpy的小實驗實現登入微信訊息接收處理回覆和人臉檢測處理反饋

概述:本文主要是博主想分享一下最近在學習python和opencv時做的一些小實驗和作為自己程式設計之路剛開始的一個小筆記。在剛接觸python時發現了有一個叫wxpy的東西,他可以實現讓微信自動接收、

IO流的應用實現檔案的複製

package com.bjpowernode.demo03; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; /** 使用FileReader/FileW

Python爬蟲編寫簡單爬蟲之新手入門

最近學習了一下python的基礎知識,大家一般對“爬蟲”這個詞,一聽就比較熟悉,都知道是爬一些網站上的資料,然後做一些操作整理,得到人們想要的資料,但是怎麼寫一個爬蟲程式程式碼呢?相信很多人是不會的,今天寫一個針對新手入門想要學習爬蟲的文章,希望對想要學習的你能有所幫助~~廢話不多說,進入正文!

安卓專案實戰之強大的網路請求框架okGo使用詳解實現get,post基本網路請求,下載上傳進度監聽以及對Callback自定義的深入理解

1.新增依賴 //必須使用 compile 'com.lzy.net:okgo:3.0.4' //以下三個選擇新增,okrx和okrx2不能同時使用,一般選擇新增最新的rx2支援即可 compile 'com.lzy.net:okrx:1.0.2' compile 'com.lzy

PKI學習之路------------------------java實現簡單的socket通訊

專案地址:https://github.com/gongxianshengjiadexiaohuihui/PKI 關於什麼是socket通訊,我會寫一篇部落格去敘述 這次我們主要實現client和server通過socket進行通訊 server程式碼 package com.ggp

Linux網路程式設計一個簡單的socket程式

伺服器: /* *tcp_server.c */ #include <stdio.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include

Linux網絡編程一個簡單的socket程序

服務 htonl 網絡 pre fin efi from ret span 服務器: /* *tcp_server.c */ #include <stdio.h> #include <sys/socket.h> #include <ne

.net core 2.0學習筆記A11實戰簡單檔案伺服器

前言:本案例適合入門來看。 首先,新建一個ASP.NET Core專案,專案名稱:StaticFilesServer 選擇空的模板,如下圖: 使用NUGet命令新增Microsoft.AspNetCore.StaticFiles引用: Ins

Android二維碼掃描開發實現思路與原理

【 回覆“ 1024 ”,送你一個特別推送 】 現在二維碼已經非常普及了,那麼二維碼的掃描與處理也成為了Android開發中的一個必要技能。網上有很多關於Android中二維碼處理的帖子,大都是在講開源框架zxing用法,然後貼貼程式碼就完了,並沒有一個系統的分析和

Android5.x:RecycleView實現ListView + GridView + StaggeredGridLayou效果

1 RecycleView實現ListView的功能 需要新增依賴: compile 'com.android.support:recyclerview-v7:24.2.0' 相關方法: RecyclerView的方法:

SignalR 2.x入門SignalR簡單例子

本系列教程使用工具 開發工具:VS2015 .NET版本:4.5 SignalR 版本:2.x系列 建立空Asp.Net Web專案,在程式包管理器控制檯中輸入如下命令,安裝SignalR: install-package Microsoft.AspNet.Signa

分散式服務架構學習實現自己的RPC框架採用Java Socket

RPC實現原理圖: 1、Service API對應服務介面。 HelloService.java程式碼如下: package com.shan.rpc.service; public interface HelloService { public String

ASP.NET Core Web API下事件驅動型架構的實現一個簡單實現

很長一段時間以來,我都在思考如何在ASP.NET Core的框架下,實現一套完整的事件驅動型架構

ASP .Net Core 中介軟體的使用搭建靜態檔案伺服器/訪問指定檔案

前言 隨著Asp .Net Core的升級迭代,很多開發者都逐漸傾向於.net core開發。 .net core是一個跨平臺的應用程式,可以在windows、Linux、macOS系統上進行開發和部署,是一個體繫結構更精簡模組化框架。 生成WebUi和WebApi的統一使用場景; 整合新的客戶端框架和開發

MINANettyTwisted一起十二HTTPS

由於HTTPS協議是由HTTP協議加上SSL/TLS協議組合而成,在閱讀本文前可以先閱讀一下HTTP伺服器和SSL/TLS兩篇博文,本文中的程式碼也是由這兩篇博文中的程式碼組合而成。 HTTPS介紹 上一篇博文中介紹了SSL/TLS協議,我們平時接觸最多的S

MINANettyTwisted一起SSL/TLS

什麼是SSL/TLS 不使用SSL/TLS的網路通訊,一般都是明文傳輸,網路傳輸內容在傳輸過程中很容易被竊聽甚至篡改,非常不安全。SSL/TLS協議就是為了解決這些安全問題而設計的。SSL/TLS協議位於TCP/IP協議之上,各個應用層協議之下,使網路傳輸的內