参加敏捷之旅北京2012的一些记录

作为敏捷之旅2012天津站的组织者,和老曹一起去了北京去学习一下。

参加了几个session的心得:

 

段念的《生长出来的敏捷》

敏捷提升效率:更多的活?还是组织集中精力做有意义的事情?

核心价值观-》原则-》实践

敏捷生长的土壤(自发的) vs. 规划好的敏捷架构(缺乏感情)

 

douban.com团队 120开发人员,5专职测试(实际上是技术很好的,有大量CI、自动化经验)

豆瓣团队的自发实践:

1. happy day,比赛写代码解决某个问题

2. 资源池(仅fix bug)<—> 机动队A(新开发) + 机动队B(新开发)

3. pull request of GIT. CI先跑pull request,成功后才能提交到master,中心CI只跑master。因为人工review是静态的,不能保证发现所有问题。

4. git enterprise 不好用,于是二次开发自己的code管理工具,还能发徽章,及内部问答系统。

5. 60%喜欢用trello

 

生长的阳光土壤空气:

1. 工程师文化、吐槽文化  vs. 公司文化、规划

2. 季度目标(避免提到绩效):哪三件事情最需要做?为什么要做?如何衡量?

3. 对老板的一些介入和要求:“挺好的,我先考虑一下”。直到他再而三的找你。

 

种子:

不招对代码不负责、不认可甚至阻碍改进文化的开发人员

 

 

 

伍斌:担水砍柴皆能悟道

中国式管理:曾仕强

日本一分法,集体主义,长官意志

美国二分法,制度+个人主义

中国三分法,太极,情绪好的时候讲道理

==》 先由情入理,再依法办事

==》解决冲突:给双方面子,再化解。比如:“A的想法不比B差,为了锻炼,可以让他去试试”

 

驱动敏捷:中国人的专业精神。生于忧患死于安乐

南怀瑾。世上苦难根源:贪痴(无明)嗔

项目中的无明

1. 浪费

2. 先作对(道)还是先做好(术)? 目标vs.方法

先改变理念,再改变方法。

 

Mike Long & Juven – Code Retreat

题目: Tic-Tac-Toe

四个简单设计的原则

pair programming + TDD

 

3x 45min sessions, each of those need to switch pair and delete all code

 

Retro:

even I’ve TDD experiece, still I found different ideas(tests, design etc) when pair with different people

a good female programmer  surprised me (we’ve rushed out a PHP test framework!)

need to prepared well for computer and common languages, better with WIFI in the room

手工安装 grunt.js/npm/phantomJs 一些经验

grunt.js是node.js的一个组件,本身还带有很多plug-in,用于javascript应用程序的构建管理及依赖管理,诸如lint, copy, concat, minify,  unit test等,有点象Maven。

有连接外网的机器,可以很方便的利用其本身的依赖管理NPM安装。但是企业内网就有一些限制,下面就如何在无外网连接的linux主机安装grunt.js等进行说明。

 

主机是centOS,之前已经安装了RPM包的gcc。

下载node.js源码,拷贝到目标机,然后make,这个还好。

下载grunt.js,同样拷贝到目标机。注意grunt.js应该使用(npm -g install grunt)作为全局安装。在linux系统下,结果是在/usr/lib/node_modules下。在winXP下,是在 C:\Documents and Settings&lt;username>\Application Data\npm\node_modules 下。其他grunt的plugin,如果不是全局module,一般都是安装在项目下,即<project>/node_modules。

 

我们希望grunt的这些module能被目标机的多个项目共享,毕竟没有网络,也不想拷来拷去。在https://npmjs.org/doc/link.html 提供了一种npm link的方法,不太好使。其实不用它这么麻烦。将所需的grunt module都拷到目标机的某个目录下的node_modules,然后直接在每个项目下新建个指向该目录的符号链接:

cd <project>

ln -s  <somewhere>/node_modules/      node_modules

 

ok,在项目下运行grunt吧!

(要是使用了jasmine进行单元测试,还需要安装grunt-jasmine-runner模块及PhantomJS)
1.      Grunt.js/node.js should be quite easy toonce-click install if this centOS5 server has a internet connection, but, forthis server, I have to copy the .tar manually, then copy all modules andplugins by finding the right place to put

2.      Grunt-jasmine-runner require phantomJs to rununit test. At first, I tried to run the complied version for linux, but itrequires libgc v2.9+ , which finally I realize centOS5 can’t afford such highversion (even forcely compile that lib with low version gcc compiler, possiblywill make the low version linux core crash).

3.      Then I tried to compile phantomJs 1.7 manually,which depends on 30+ libs. Those can be download fromhttp://mirror.centos.org/centos/5/os/x86_64/CentOS/then copy to server, and run with rpm command in turn. Fortunately, it works

4.      The last tricky one, the passed unit test failson that server by run ‘grunt jasmine’.

<pre>Running “jasmine” task

Testing jasmine specs via phantom

[D] [“phantomjs”,”onResourceReceived”,”GET http://127.0.0.1:8888/test/spec/CommonTest.js"]
[D] [“phantomjs”,”onError”,”ReferenceError: Can’t find variable: describe”,[{“file”:”http://127.0.0.1:8888/test/spec/CommonTest.js","line":31,"function":""}]]
ReferenceError: Can’t find variable: describe
…</pre>

At last, it’s caused by that: I createda symbolic link for “node_modules” to re-use common module, but it preventgrunt-jasmine-runner to fetch sth. (not sure yet). To resolve it, need to copythe “node_modules” files directly under project folder.

基于HTML5的EasyXDM组件实现浏览器兼容的跨域资源请求

同源策略(http://baike.baidu.com/view/1580195.htm)指浏览器客户端的javascript脚本,只被允许访问来自同一站点的资源,阻止来自其它站点可能怀有恶意的资源。这种策略在保证安全的同时,也带来了站点间交互的诸多限制。一直以来,存在多种解决方案来实现跨域资源请求,比如基于flash或基于iFrame等等,web开发者需要选择不同的方案来兼容各种浏览器。

HTML5 XDM

近年不断发展的HTML5规范对XDM-跨文档消息传送机制进行了规范,利用postMessage()(http://www.whatwg.org/specs/web-apps/current-work/multipage/web-messaging.html#dom-window-postmessage)函数可以简单稳妥的实现对其他窗口及站点的跨文档消息传送XDM(cross-document messaging)。

_postMessage(message, targetOrigin, transfer)_函数有三个参数。第一个message表示要发送的消息,第二个参数表示消息接收方所在域。第三个参数是可选的,用于渠道信息发送。

比如下面的例子简单演示了使用postMessage()向内嵌iframe发送一条消息,并指定来源自www.infoq.com,防止其中文档来源被悄悄改变。

var iframWindow = document.getElementById(“theFrame”).contentWindow;

iframWindow.postMessage(“private info”, “http://www.infoq.com“);

 

接收方www.html5.org收到XDM消息会异步触发window对象的message事件。首先验证消息来源是已知的域www.infoq.com,在处理完消息后,向发送方回复一条消息。

window.addEventListener(“message”,function(event) {

if (event.origin == “http://www.infoq.com“) {

processMessage(event.data);

event.source.postMessage(“Heard You”, “http://www.html5.org“);

}

});

**easyXDM**

easyXDM (https://github.com/oyvindkinsey/easyXDM)是一款轻量级开源javascript组件,很好的实现了各种跨域解决方案的封装,来消除同源策略带来的限制,方便应用程序通过javascript API进行跨域通讯。easyXDM借助传输栈允许两个窗口双向交换基于字符串的消息,支持双向通讯、可靠性、队列和发送方验证等。对于支持HTML5的浏览器如IE 8+, Opera 9+,Firefox 3+, Safari 4+, Chrome 2+等直接采用HTML5的postMessage(),对于其他非HTML5浏览器,则根据当前浏览器优先选择最有效的方法实现跨文档消息传送,比如加载swf文件组件,创建iFrame等。

 

easyXDM包含两个基本对象。

easyXDM.Socket对象包装了传输栈,允许在通讯的消费者(客户端)和提供者(服务端)之间通讯,并定义一系列属性用于异步事件。

服务端首先建立Socket:

var socket = new easyXDM.Socket({

onMessage**:function**(message, origin) {

//do something with message

}

});

客户端创建Socket并进行连接:

    var socket = new easyXDM.Socket({

remote**:** “http://path.to/provider/“, // the path to the provider

onMessage**:function**(message, origin) {

//do something with message

}

});

socket.postMessage(“hello world”);

 

easyXDM.Rpc对象允许创建带有代理对象进行远程方法调用。Rpc与Socket使用相同的传输栈和配置属性。

在服务端配置被调用的远程对象:

var rpc = new easyXDM.Rpc({},

{

local**:** {

helloWorld**: function**(one, two, three, successFn, errorFn){

// here we expose a simple method with three arguments

// that returns an object

return {

this_is**:** “an object”

};

}

},

remote**:** {

helloWorld**:**{

// here we tell the Rpc object to stub a method helloWorld for us

}

}

});

在客户端可以进行基于JSON的远程方法调用:

rpc.helloWorld(1,2,3, function(response){

// here we can do something with the return value from helloWorld

}, function(errorObj){

// here we can react to a possible error

};

 

跨域资源请求

在web开发中跨域资源请求相当常用。借助XDM,可以将资源内容作为消息内容来传送。

下面举例如何使用easyXDM来跨站点文件请求:站点client.com/index.html获取remote.com/list.xml 这种典型场景。首先在remote.com放置代理,注意这个代理是纯HTML文件,例如remote.com/agent.html

 

在客户端client.com/index.html引用easyxdm.js后,添加以下代码,发起远程连接并准备好回调函数来处理XML文件内容。

 

var socket = new easyXDM.Socket({

remote: “http://remote.com/agent.html,

onMessage:function(message, origin) {

var data = $.parseXML(message);

//do something with message data

}

});

 

在被请求的远程服务端remote.com/agent.html,添加以下代码:list.xml的内容将被缓存并发送

$.ajax({

type: “GET”,

url: “list.xml”,

dataType: “text”,

success: function (data) {

var socket = new easyXDM.Socket(

{

onReady: function(message, origin){ }

}

);

socket.postMessage(‘’ + data);

}

});

 

 

小结:easyXDM开源组件封装和支持HTML5的postMessage()和其他传统跨文档消息传送XDM方案,允许web开发人员可以方便安全的实现跨域资源请求,并方便的兼容不同的浏览器。

Calatrava:自由构建UI的跨平台移动框架

移动是未来计算的趋势,越来越多的人使用移动设备来访问互联网。但是目前至少三大平台:iOS、Android、移动Web。相比桌面Web,移动用户需要更好的体验、界面和设计。然而移动设备受限于电池、不可靠的网络连接和小尺寸屏幕。

几个月前,Martin Fowler写过一篇关于开发跨设备移动应用的文章。他指出,要获得最佳用户体验,最直接的办法就是针对每个移动平台开发一个本地应用,但这却将带来极大的成本。或者选用跨平台工具箱,“一次编写随处运行”?然而没有几个产品真正获得成功。Web应用程序倒是可以以较低成本运行在跨平台之上,但是用户界面又受到限制。因此,不可避免地要在用户体验和成本之间做出选择。

或者,折中的办法就是开发混合式(hybrid)应用程序:结合Web和原生应用。

ThoughtWorks 探索这条路并取得一些进展,最近发布了Calatrava开源框架。Calatrava的思路是开发者用跨平台的JavaScript编写客户端逻辑,这部分代码完全相同,运行在iOS、Android、移动Web的JavaScript解释器中。Calatrava提供本地桥接,允许逻辑来驱动原生UI。

它将移动应用程序分成两部分来考虑:UI和客户端逻辑主体(headless-body)。开发者使用JavaScript代码开发通用的控制器逻辑,而原生代码处理UI部分生成原生外观(native veneer),可移植的JavaScript代码和原生代码之间能够互操作,进而开发出混合移动应用。

Calatrava本身并不提供任何UI框架或建立UI抽象层。在iOS上,就用Objective-C和Cocoa Touch框架来构建UI;在Android上,就用Java和Android库;在移动Web上,就用HTML5、CSS3和你喜欢的JavaScript库。跨平台逻辑层与代表着应用表现层Page对象进行交互,该对象提供了从显示和交互机制中分离出的API接口。很多时候,对于移动应用来说,HTML5 UI已经足够好,所以Calatrava也允许在iOS和Android上桥接HTML5,但是应用绝不会绑定在HTML UI上。

这样,每个平台都有具有了自己独有的UI设计,避免了“恐怖谷效应”(uncanny valley effect)。应用开发可以从HTML UI开始,当你觉得某些地方不够好的时候,就用原生UI替换掉,且只对部分平台进行替换,而不影响其他部分。同时你也可以享受某些平台特有的UI特性。

Calatrava适合包含较多复杂的客户端逻辑的应用,且当应用偏重于成为现有产品的新渠道(Channel),而非产品本身。如果应用的大部分代码是UI部分(如游戏),或是已经提供非常好的用户体验的Web应用,Calatrava就不太适合了。 Calatrava编写的核心JavaScript逻辑支持使用Jasmine进行单元测试,以及使用cucumer.js进行功能测试。

想试试吗?从github的Caltrava主页可以找到说明,简单来说就是下面几步:

  1. 安装依赖:Node.js, Ruby, Xcode, Android.
  2. 安装Ruby插件gem install calatrava
  3. 使用Calatrava工具创建项目calatrava create sample
  4. 编译并运行项目
    Calatrava还处在早期开发阶段,估计还是会有很多bug,另外插件、模块和文档也需要完善。

Martin Fowler分析了用户体验和可负担能力之间的动态平衡,他认为混合式(hybrid)解决方案介于纯本地应用和纯Web应用之间,更适合增量式发布。即首次发布(release)采用纯Web UI,之后的发布(release)逐步将Web UI转为本地UI特性,或逐步地增加本地UI特性的比例。

MartinFowler认为Caltrava最有价值的地方就在于它适合增量式发布策略。比如cover-your-bases策略,即当你已经有大量用户基础,而移动应用定位为现有产品的新渠道(channel)。由于现有的用户,最重要的事情是将新渠道尽快地推到尽可能多的用户面前。很明显,平台覆盖率最重要。然而在移动平台上,体验也非常重要,所以应该用最小功能集提供简化的体验,而不是提供退化的体验。

它将移动应用程序分成两部分来考虑:UI和客户端逻辑主体(headless-body)。开发者使用JavaScript代码开发通用的控制器逻辑,而原生代码处理UI部分生成原生外观(native veneer),可移植的JavaScript代码和原生代码之间能够互操作,进而开发出混合移动应用。

Calatrava本身并不提供任何UI框架或建立UI抽象层。在iOS上,就用Objective-C和Cocoa Touch框架来构建UI;在Android上,就用Java和Android库;在移动Web上,就用HTML5、CSS3和你喜欢的JavaScript库。跨平台逻辑层与代表着应用表现层Page对象进行交互,该对象提供了从显示和交互机制中分离出的API接口。很多时候,对于移动应用来说,HTML5 UI已经足够好,所以Calatrava也允许在iOS和Android上桥接HTML5,但是应用绝不会绑定在HTML UI上。

这样,每个平台都有具有了自己独有的UI设计,避免了“恐怖谷效应”(uncanny valley effect)。应用开发可以从HTML UI开始,当你觉得某些地方不够好的时候,就用原生UI替换掉,且只对部分平台进行替换,而不影响其他部分。同时你也可以享受某些平台特有的UI特性。

Calatrava适合包含较多复杂的客户端逻辑的应用,且当应用偏重于成为现有产品的新渠道(Channel),而非产品本身。如果应用的大部分代码是UI部分(如游戏),或是已经提供非常好的用户体验的Web应用,Calatrava就不太适合了。 Calatrava编写的核心JavaScript逻辑支持使用Jasmine进行单元测试,以及使用cucumer.js进行功能测试。

想试试吗?从github的Caltrava主页可以找到说明,简单来说就是下面几步:

  1. 安装依赖:Node.js, Ruby, Xcode, Android.
  2. 安装Ruby插件gem install calatrava
  3. 使用Calatrava工具创建项目calatrava create sample
  4. 编译并运行项目
    Calatrava还处在早期开发阶段,估计还是会有很多bug,另外插件、模块和文档也需要完善。

Martin Fowler分析了用户体验和可负担能力之间的动态平衡,他认为混合式(hybrid)解决方案介于纯本地应用和纯Web应用之间,更适合增量式发布。即首次发布(release)采用纯Web UI,之后的发布(release)逐步将Web UI转为本地UI特性,或逐步地增加本地UI特性的比例。

MartinFowler认为Caltrava最有价值的地方就在于它适合增量式发布策略。比如cover-your-bases策略,即当你已经有大量用户基础,而移动应用定位为现有产品的新渠道(channel)。由于现有的用户,最重要的事情是将新渠道尽快地推到尽可能多的用户面前。很明显,平台覆盖率最重要。然而在移动平台上,体验也非常重要,所以应该用最小功能集提供简化的体验,而不是提供退化的体验。

原文:http://www.infoq.com/cn/articles/calatrava

无GUI的server上通过虚拟显示来运行RobotFramework+Selenium2 (webdriver)

RobotFramework是强大的开源自动化测试框架,与selenium2(webdriver)结合,可以自动化对网页应用进行测试,甚至A-TDD。

selenium需要打开浏览器,而浏览器需要图形化界面。如果你在liunx服务器上直接运行firefox,你会得到:

1
2
# firefox
Error: no display specified

如何在这种限制下进行Robot测试呢?

Xvfb是流行的虚拟现实库,可以使很多需要图形界面的程序虚拟运行。

pyvirtualdisplay是该库的python封装,进一步我们可以将其封装在Keywords中。那么你的Robotcase可能看起来是这样:

~ ~ ~
Run Keyword If ‘${IsVirtualDisplay}’==’NO’ Open Virtual Display
Open Browser http://someurl firefox
Some Operations…
Close All Browsers
Run Keyword If ‘${IsVirtualDisplay}’==’NO’ Close Virtual Display

在本地具有GUI的机器进行Test case调试时,可以关闭该开关,而在CI服务器配置中,可以设置开关来打开虚拟显示。

1
pybot -v IsVirtualDisplay:YES example.tsv

另外,Selenium2Library是在GitHub上的开源项目,很好将selenium2(webdriver)封装成Robot Keyword,但是往往这些还不够用。
肯定需要实现产品特性专用的Keyword Library。
如果单独写一个库,会无法享受到Selenium2Library封装好的,特别是对浏览器句柄封装好的keywords。
因此,可以考虑继承Selenium2Library,注意,该库使用了Decorator来将分布在不同类中的public方式暴露并组装成整体。

好用的Git config 缩写配置

初次安装运行Git,建议在命令行中运行以下命令,配置缩写和颜色,支持中文文件名显示和提交等。
可以有效提高效率。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
git config --global --add user.email mebusw@163.com
git config --global --add user.name JackyShen
git config --global alias.co checkout
git config --global alias.br branch
git config --global alias.ci commit
git config --global alias.st status
git config --global alias.last 'log -1 HEAD'
git config --global color.diff auto
git config --global color.status auto
git config --global color.branch auto
git config --global alias.co checkout
git config --global core.quotepath false
git config --global merge.tool kdiff3
git config --global meregtool.kdiff3.path /usr/bin/kdiff3
git config --global alias.visual !gitk

建立忽略文件 ~/.gitignore_global

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
###################
*.com
*.class
*.dll
*.exe
*.o
*.so
*.pyc

# Packages #
############
# it's better to unpack these files and commit the raw source
# git has its own built in compression methods
*.7z
*.dmg
*.gz
*.iso
*.jar
*.rar
*.tar
*.zip

# Logs and databases #
######################
*.log
*.sql
*.sqlite

# OS generated files #
######################
.DS_Store*
ehthumbs.db
Icon?
Thumbs.db
.svn
*.bak

Scrum 2011更新

  • 进行增量产出的一组开发人员组成一个开发团队(development team)。如果大家都在单打独斗的开发,那不叫团队.
  • 在冲刺计划会(sprint planning)上,团队不再承诺(commit)所有工作,而是建立一个相信能够完成任务的预期(forcast),但是这个预期在整个sprint中,随着对任务的深入了解而变化。
  • 不再必须用燃尽图(burndown chart)来监控进度。Scrum只需要每天对剩余工作进行估计,且在整个sprint中,持续维护任务完成的趋势即可。
  • 版本计划会(release planning)对使用scrum来说很重要,但不是scrum本身的要求。
  • 冲刺订单(sprint backlog)相当于从产品订单(product backlog)中选择出来的任务,加上交付任务的计划。冲刺订单任务(sprint backlog item)的概念不再需要,尽管这种技术可以做出伟大的计划。一个自我组织的开发团队总是有其自己的计划。
  • 产品订单是有序排列的,而不再要求是按优先级排列的。这样允许产品负责人(product owner)在特定环境下灵活的优化产品价值。

原文:

http://www.scrum.org/storage/Scrum%20Update%202011.pdf

Java NIO Socket实现C/S架构

一般的Server端

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
package thread.socket;  
import java.io.*;
import java.net.*;
import java.util.*;
public class Server extends ServerSocket {
private static final int SERVER_PORT = 10000;
private List<WorkerThread> workers;

public Server() throws IOException {
super(SERVER_PORT);
workers = new LinkedList<WorkerThread>();

try {
System.out.println("server is listening...");
while (true) {
Socket socket = accept();
workers.add(new WorkerThread(socket));
System.out.println(String.format("new worker created, total %d", getWorkerCount()));
}
} catch (IOException e) {
} finally {
close();
}
}
public synchronized int getWorkerCount() {
return workers.size();
}
public static void main(String[] args) throws IOException {
new Server();
}
}
// --- WorkerThread
class WorkerThread extends Thread {
private Socket client;
private BufferedReader in;
private PrintWriter out;
public WorkerThread(Socket s) throws IOException {
System.out.println(String.format("create a new thread. %s", s));
client = s;
in = new BufferedReader(new InputStreamReader(client
.getInputStream(), "GB2312"));
out = new PrintWriter(client.getOutputStream(), true);
out.println("--- Welcome ---" + client.getRemoteSocketAddress());
start();
}
public void run() {
try {
String line = in.readLine();
while (!line.equals("bye")) {
System.out.println("client " + client.getRemoteSocketAddress() + " says: " + line);
String msg = createMessage(line);
out.println(msg);
line = in.readLine();
}
out.println("bye");
System.out.println("client " + client.getRemoteSocketAddress() + " quit");
client.close();
} catch (IOException e) {
}
}
private String createMessage(String line) {
// ;
return "response to " + line;
}
}

使用NIO的Server端 (从1.5开始,Java对InputStream/OutputStream 进行了重新改写,用的就是NIO,因此,就算你不显示声明要用NIO,只要你的类继承了InputStream/OutputStream就已经在用NIO了)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
import java.io.BufferedWriter;  
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Set;
public class SelectorServer
{
private static int DEFAULT_SERVERPORT = 6018;//默认端口
private static int DEFAULT_BUFFERSIZE = 1024;//默认缓冲区大小为1024字节
private static String DEFAULT_CHARSET = "GB2312";//默认码集
private static String DEFAULT_FILENAME = "bigfile.dat";
private ServerSocketChannel channel;
private LinkedList<SocketChannel> clients;
private Selector selector;//选择器
private ByteBuffer buffer;//字节缓冲区
private int port;
private Charset charset;//字符集
private CharsetDecoder decoder;//解码器


public SelectorServer(int port) throws IOException
{
this.port = port;
this.clients = new LinkedList<SocketChannel>();
this.channel = null;
this.selector = Selector.open();//打开选择器
this.buffer = ByteBuffer.allocate(DEFAULT_BUFFERSIZE);
this.charset = Charset.forName(DEFAULT_CHARSET);
this.decoder = this.charset.newDecoder();

}

private class HandleClient
{
private String strGreeting = "welcome to VistaQQ";
public HandleClient() throws IOException
{
}
public String readBlock()
{//读块数据
return this.strGreeting;
}
public void close()
{

}
}
protected void handleKey(SelectionKey key) throws IOException
{//处理事件
if (key.isAcceptable())
{ // 接收请求
ServerSocketChannel server = (ServerSocketChannel) key.channel();//取出对应的服务器通道
SocketChannel channel = server.accept();
channel.configureBlocking(false);
channel.register(selector, SelectionKey.OP_READ);//客户socket通道注册读操作
}
else if (key.isReadable())
{ // 读信息
SocketChannel channel = (SocketChannel) key.channel();
int count = channel.read(this.buffer);
if (count > 0)
{
this.buffer.flip();
CharBuffer charBuffer = decoder.decode(this.buffer);
System.out.println("Client >>" + charBuffer.toString());
SelectionKey wKey = channel.register(selector,
SelectionKey.OP_WRITE);//为客户sockt通道注册写操作
wKey.attach(new HandleClient());
}
else
{//客户已经断开
channel.close();
}
this.buffer.clear();//清空缓冲区
}
else if (key.isWritable())
{ // 写事件
SocketChannel channel = (SocketChannel) key.channel();
HandleClient handle = (HandleClient) key.attachment();//取出处理者
ByteBuffer block = ByteBuffer.wrap(handle.readBlock().getBytes());
channel.write(block);
// channel.socket().getInputStream().(block);
// PrintWriter out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(
// channel.socket().getOutputStream())), true);
// out.write(block.toString());
}
}
public void listen() throws IOException
{ //服务器开始监听端口,提供服务
ServerSocket socket;
channel = ServerSocketChannel.open(); // 打开通道
socket = channel.socket(); //得到与通到相关的socket对象
socket.bind(new InetSocketAddress(port)); //将scoket榜定在制定的端口上
//配置通到使用非阻塞模式,在非阻塞模式下,可以编写多道程序同时避免使用复杂的多线程
channel.configureBlocking(false);
channel.register(selector, SelectionKey.OP_ACCEPT);
try
{
while(true)
{// 与通常的程序不同,这里使用channel.accpet()接受客户端连接请求,而不是在socket对象上调用accept(),这里在调用accept()方法时如果通道配置为非阻塞模式,那么accept()方法立即返回null,并不阻塞
this.selector.select();
Iterator iter = this.selector.selectedKeys().iterator();
while(iter.hasNext())
{
SelectionKey key = (SelectionKey)iter.next();
iter.remove();
this.handleKey(key);

}
}
}
catch(IOException ex)
{
ex.printStackTrace();
}
}
public static void main(String[] args) throws IOException
{
System.out.println("服务器启动");
SelectorServer server = new SelectorServer(SelectorServer.DEFAULT_SERVERPORT);
server.listen(); //服务器开始监听端口,提供服务
}
}

使用NIO的Client端

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
package thread.socket;  
import java.io.*;
import java.net.*;
public class Client {
Socket socket;
BufferedReader in;
PrintWriter out;
public Client() {
try {
socket = new Socket("127.0.0.1", 10000);
in = new BufferedReader(new InputStreamReader(socket
.getInputStream()));
out = new PrintWriter(socket.getOutputStream(), true);
BufferedReader line = new BufferedReader(new InputStreamReader(
System.in));
String cmd = "";
while (!cmd.equals("bye")) {
System.out.println("server says: " + in.readLine());
out.println(cmd = line.readLine());
}
System.out.println("socket " + socket + " stop");
line.close();
out.close();
in.close();
socket.close();
} catch (IOException e) {
}
}
public static void main(String[] args) {
new Client();
}
}

Do you like my piece of paper?

What I see every day is people creating pieces of paper to take into a room for people to look at and decide if they like that piece of paper, and if they don’t totally like it they say what they would prefer to see on that piece of paper.

This repeats until everybody is happy with a piece of paper.

capture3

Read More