| Hello-_-World님의 프로필撕裂空间的技术会所블로그리스트 | 도움말 |
|
6월 16일 [转]谈谈Java语言的垃圾收集器
6월 5일 做了个点击率刷新工具 今天上百合系版,惊见30w人气的大作。整个世界震惊了!左右一想,不对,以人为的力量这得多少人多大的耐心呐。嗯,是了,肯定是借助了某种共具。
受了启发,我已迅雷不及掩耳盗铃之势,打开可爱的eclipse。四处搜刮了一些资料,真是顺呐,这年头想在茫茫85899345920字节海的电脑里找到我想要的东东,居然就是信手拈来。。。
立马开工,很快,即两个多小时后,页面刷新工具新鲜出炉。为了提高实用性,又增加了一些功能及参数,可谓能够在各大高低速不等的BBS间游刃有余得骗取点击率了。
说了这么多废话,总得把简单的原理记下,要不怎对得起这空间?我是利用了URL封装类,直接创建连接,但是不必从连接中获取详细数据,每创建一个连接相当于页面刷新一次。同时为了防止界面失去响应,按钮事件里加入线程处理刷新页面事件;为了多个连接并行工作,为每个URL连接请求创建一个线程。另外,防止连接超时及刷新效率以及对网站增加负载的可控性,分“刷新块”(我创的名词,凑活用吧)来处理,比如一个块50次刷新,下一个块必须等待这个块全部刷新结束,即50个连接线程终止,包括异常在内。
本次设计基于的URL类还是太高级了,可控制能力弱。考虑用并行连接方式发送HTTP报头来创建连接,估计效率还会提高。
今天原想早点睡的,看看时间都2点快40了,明天还得早起,看样子考试期间的睡眠百分比要低于25%了。。。 5월 21일 JavaSoundAudioClip播放音频剪辑Java中有许多可以制作音频播放的包和类,包括java.applet.AudioClip、com.sun.media.sound.JavaSoundAudioClip和javax.sound.midi.spi&javaw.sound.sampled.spi等,其中java.applet.AudioClip是从JDK1.0就存在的一个接口,其余的多个类是对这个接口的实现及丰富其功能。
构造AudioPlay类--音频播放wav,注意异常处理
5월 20일 [整理]利用“同时开放TCP连接”建立基于TCP的P2PSimultaneous TCP open 同时开放TCP连接
这里有一种方法能够在某种情况下建立一个穿透NAT的端对端TCP直连。我们知道,绝大多数的TCP会话的建立,都是通过一端先发送一个SYN包开始,另一方则回发一个SYN-ACK包的过程。然而,这里确实存在另外一种情况,就是P2P的双方各自同时地发出一个SYN包到对方的公网地址上,然后各自都单独地返回一个ACK响应来建立一个TCP会话,这个过程被称之为:“Simultaneous open”(“同时开放连接”)。
如果一个NAT接收到一个来自私有网络外面的 TCP SYN 包,这个包想发起一个“引入”的 TCP 连接,一般来说,NAT会拒绝这个连接请求并扔掉这个SYN 包,或者回送一个TCP RST(connection reset,重建连接)包给请求方。但是,有一种情况,当这个接收到的 SYN 包 中的源IP地址和端口、目标IP地址和端口都与NAT登记的一个已经激活的TCP会话中的地址信息相符时,NAT将会放行这个SYN 包,让它进入NAT内部。特别要指出,如果NAT恰好看到一个刚刚发送出去的一个SYN包也和上面接收到的SYN包中的地址信息相符合的话,那么NAT将会认为这个TCP连接已经被激活,并将允许这个方向的SYN包进入NAT内部。
如果 Client A 和 Client B 能够彼此正确的预知对方的NAT将会给下一个TCP连接分配的公网TCP端口,并且两个客户端能够同时地发起一个“外出”的TCP连接,并在对方的 SYN 包到达之前,自己刚发送出去的SYN包都能顺利的穿过自己的NAT的话,一条端对端的TCP连接就成功地建立了。
不幸的是,这个诡计比3.4节所讲的UDP端口预言更容易被粉碎,并且对时间的敏感性的依赖更多!首先,除非双方的NAT都是Simple firewalls 或者都像cone NAT那样处理TCP通信,否则两个客户端还是无法建立这个TCP直连,因为它们无法预知对方的NAT会分配给新的会话的端口号是多少。 另外,如果双方的 SYN 到达对方的NAT 速度太快(举例来说,就是SYN A的还未穿过NAT A时,SYN B已经提早到达了NAT A),对方的NAT会将这个SYN扔掉,并返回一个 RST 包,这样就使得这个发快了的一方NAT关闭原来的会话,又重新建立一个新的会话,再利用这个新的会话重发一个SYN,这时端口预言就失效了,因为再次分配到相同的端口地址的几率太小了。
最后,尽管在“TCP规范”中说明了“Simultaneous open”是一种支持的标准技术,但是在一些公共操作系统中,对这种技术的支持并不好。基于这个原因,我们也在这里郑重申明,并不推荐使用这个技术。如果您的应用程序想要穿透NAT并进行高效率高性能的P2P的通信,应该使用UDP技术!
过程详细描述: Client A发送一个TCP SYN 包给 Client B,我们把这个SYN包叫做 SYN A,包含的信息如下: SrcAddress:10.0.0.1 Tcp port :1234 DestAddress:138.76.29.7 Tcp port:310000
同时,Client B发送一个TCP SYN包给Client A,我们把这个包叫做 SYN B,包含的信息如下: SrcAddress:10.1.1.3 Tcp port :1234 DestAddress:155.99.25.11 Tcp port:620000
SYN A首先通过NAT A(必须在SYN B到达NAT A之前),NAT A看到这个包并将其地址信息进行转换为: SrcAddress:155.99.25.11 Tcp port :620000 DestAddress:138.76.29.7 Tcp port:310000 我们把这个经过 NAT A转换的包叫做 SYN A’
同样,SYN B首先通过NAT B(也必须在SYN A到达NAT B之前),NAT B看到这个包并进行地址转换为: SrcAddress:138.76.29.7 Tcp port:310000 DestAddress:155.99.25.11 Tcp port :620000 我们把这个经过NAT B转换的包叫做 SYN B’
这时,NAT A和NAT B都在自己的TCP连接表中存储了含有上面两个公网IP地址和端口信息,因此,只要看到包含这两个信息的SYN包,都会让其通过。
就在这个瞬间,SYN A’到达了NAT B,NAT B检查了一下SYN A’,发现它的地址信息和自己TCP连接表中的信息相符,便让SYN A’通过了,并将SYN A’的地址信息转换为:我们称这个包为 SYN A’’ SrcAddress:155.99.25.11 Tcp port :620000 DestAddress:10.1.1.3 Tcp port:1234 以使这个包能够到达内部网中Client B上
也就在这个瞬间,SYN B’到达了NAT A,NAT A检查了一下SYN B’,发现它的地址信息和自己TCP连接表中的信息相符,便让SYN B’通过了,并将SYN B’的地址信息转换为:我们称这个包为 SYN B’’ SrcAddress:138.76.29.7 Tcp port :310000 DestAddress:10.0.0.1 Tcp port:1234 以使这个包能够到达内部网中Client A上
这时,Client A收到了SYN B’’,Client B收到了SYN A’’,并返回给对方ACK,经过三次握手,这个P2P的TCP连接就建立了。 [整理][续]基于UDP的P2P问题拾遗UDP协议包经NAPT透明传输的说明: NAPT为每一个Session分配一个NAPT自己的端口号,依据此端口号来判断将收到的公网IP主机返回的TCP/IP数据包转发给那台内网IP地址的计算机。在这里Session是虚拟的,UDP通讯并不需要建立连接,但是对于NAPT而言,的确要有一个Session的概念存在。NAPT对于UDP协议包的透明传输面临的一个重要的问题就是如何处理这个虚拟的Session。我们都知道TCP连接的Session以SYN包开始,以FIN包结束,NAPT可以很容易的获取到TCP Session的生命周期,并进行处理。但是对于UDP而言,就麻烦了,NAPT并不知道转发出去的UDP协议包是否到达了目的主机,也没有办法知道。而且鉴于UDP协议的特点,可靠很差,因此NAPT必须强制维持Session的存在,以便等待将外部送回来的数据并转发给曾经发起请求的内网IP地址的计算机。NAPT具体如何处理UDP Session的超时呢?不同的厂商提供的设备对于NAPT的实现不近相同,也许几分钟,也许几个小时,些NAPT的实现还会根据设备的忙碌状态进行智能计算超时时间的长短。 现在来看一下NAPT是依据什么策略来判断是否要为一个请求发出的UDP数据包建立Session的主要有一下几个策略: A. 源地址(内网IP地址)不同,忽略其它因素, 在NAPT上肯定对应不同的Session D的情况正式我们关心和要讨论的问题。依据目的地址(公网IP地址)对于Session的建立的决定方式我们将NAPT设备划分为两大类: Symmetric NAPT: Cone NAPT: 客户端都处于相同的NAT之后 我们假设,Client A 和 Client B 要使用上一节我们所描述的 “UDP打洞技术”,并通过服务器S这个“媒人”来认识,这样Client A 和Client B首先从服务端S得到了彼此的公网IP地址和端口,然后就往对方的公网IP地址和端口上发送消息。在这种情况下,如果NAT 仅仅允许在 内部网主机与其他内部网主机(处于同一个NAT之后的网络主机)之间打开UDP会话通信通道,而内部网主机与其他外部网主机就不允许的话,那么Client A 和Client B就可以通话了。我们把这种情形叫做“loopback translation”(“回环转换”),因为数据包首先从局域网的私有IP发送到NAT转换,然后“绕一圈”,再回到局域网中来,但是这样总比这些数据通过公网传送好。举例来说,当 Client A发送了一个UDP数据包到 Client B的公网IP地址,这个数据包的报头中就会有一个源地址10.0.0.1:124和一个目标地址155.99.25.11:62001。NAT接收到这个包以后,就会(进行地址转换)解析出这个包中有一个公网地址源地址155.99.25.11:62000和一个目标地址10.1.1.3:1234,然后再发送给B,虽说NAT支持“loopback translation”,我们也发现,在这种情形下,这个解析和发送的过程有些多余,并且这个Client A 和Client B 之间的对话可能潜在性地给NAT增加了负担。 UDP打洞技术有很多实用的地方: 第一,一旦这种处于NAT之后的端对端的直连建立之后,连接的双方可以轮流担任 对方的“媒人”,把对方介绍给其他的客户端,这样就极大的降低了服务器S的工作量;第二,应用程序不用关心这个NAT是属于cone还是symmetric,即便要,如果连接的双方有一方或者双方都恰好不处于NAT之后,基于上叙的步骤,他们之间还是可以建立很好的通信通道;第三,打洞技术能够自动运作在多重NAT之后,不论连接的双方经过多少层NAT才到达Internet,都可以进行通信。 UPD端口号预言 如果隐藏在NAT A后的一个不同的客户端X(或者在NAT B后)打开了一个新的“外出”UDP 连接,并且无论这个连接的目的如何;只要这个动作发生在 客户端A 建立了与服务器S 的连接之后,客户端A 与 客户端B 建立连接之前;那么这个无关的客户端X 就会趁人不备地“偷” 到这个我们渴望分配的端口。所以,这个方法变得如此脆弱而且不堪一击,只要任何一个NAT方包含以上碰到的问题,这个方法都不会奏效。 最后,如果P2P的一方处在两级或者两级以上的NAT下面,并且这些NATs 接近这个客户端是 symmetric的话,端口号预言 是无效的!
保持端口绑定 伴随着 cone NAT 的推广,“UDP打洞技术”也被越来越广泛的应用。然而,仍存在一小部分使用 symmetric NAT 的网络,那么在这小部分网络环境中,就不能使用“UDP打洞技术”。 5월 17일 Java JList及其CellRenderer的使用 这是一个视图操作与数据操作相分离的组件。
对视图操作如下,用一个视图图类定义,实现ListCellRenderer接口即可。
public class BWCRoomCellRenderer extends JLabel implements ListCellRenderer
{ private BWCRoomListData data = null; public Component getListCellRendererComponent(JList list, Object value,
int index, boolean isSelected, boolean hasFocus) { // 这里就是列表框的数据,其中BWCRoomListData是另外一个数据类. data = (BWCRoomListData)value; //在JLabel上放上图标和字符串 并设置文本在右边,以及两者的间距 setText(String.valueOf(data.getRoomIndex() + " " + data.getRoomName()
+ " (" + data.getReadyUsers() + "/" + data.getTotalUsers() + ")")); if(data.getReadyUsers() == 2) setIcon(startIcon); else setIcon(waitIcon); setHorizontalTextPosition(SwingConstants.RIGHT); setIconTextGap(5); // setBorder(BorderFactory.createEmptyBorder(0, 5, 0, 5)); // 设置项目被选中时的颜色
if(isSelected) { // setBackground(list.getSelectionBackground()); setBackground(new Color(252, 209, 0)); setForeground(Color.BLACK); /// setForeground(list.getSelectionForeground()); } else { setBackground(list.getBackground()); setForeground(list.getForeground()); } setEnabled(list.isEnabled()); setFont(list.getFont()); setOpaque(true); return this; } } 再添加数据类,如下即可
public class BWCRoomListData
{ private Icon icon; private int roomIndex;
private int readyUsers = 0;
private int totalUsers = 0;
private String roomName;
BWCRoomListData(int roomIndex, String roomName, int readyUsers,
int totalUsers) { this.roomIndex = roomIndex; this.readyUsers = readyUsers; this.totalUsers = totalUsers; this.roomName = roomName; // this.icon = icon; } public int getRoomIndex()
{ return this.roomIndex; } public int getReadyUsers()
{ return this.readyUsers; } public int getTotalUsers()
{ return this.totalUsers; } public String getRoomName()
{ return this.roomName; } } 最后设置好数据,可以为上述数据类对象的数组,然后调用JList的函数
jlRoomList.setCellRenderer(new BWCRoomCellRenderer());
再添加数据jlRoomList.setListData(data);即可。
注意这里会实际更新data里的数据并显示出来,如果data=null,列表视图也为空 5월 15일 Java多线程关于读对象流的一个注意事项 我在写黑白棋网络版,大厅与游戏界面(两个JFrame)切换时遇到一个很隐藏的问题,花了半天时间才搞明白。
具体现象是,游戏界面dispose并调出大厅界面,然后大厅中的readObject()完全读不到东西,异常大概是EOFException。经过长时间分析(怎么解决bug的过程在此略过了) ,确定原因是由于界面类(BWCClient)通过关闭窗口的事件响应里开启大厅线程,而BWCClient里另外一个处理接受消息的线程,包含readObject指令。而大厅线程启动后也开启了监听消息线程,此时原来的BWCClient里并没有完全释放掉资源,线程也未退出,即同时有了两个readObject()函数在执行,造成了异常。
解决方案当然多种多样,我只试了一下通过服务器来转发通知数据包给本界面线程的readObject循环,当读到该通知信息后,线程退出并解除循环等待(Flag = false),窗口监听事件用一个循环等待
while(Flag)
{
Thread.sleep(100);
} Flag = true; 收到解除指令后再开启下一窗口。 另外,用此方法,也可以保证窗口退出时保证先结束监听线程再关闭窗口,就没有异常产生了。 Java 的几个tips与困惑1,ObjectOutputStream osToServer = new ObjectOutputStream(connectToServer
.getOutputStream()); ObjectInputStream isFromServer = new ObjectInputStream(connectToServer .getInputStream()); 这两句话貌似对顺序有一定的要求,换一下会读不出Stream
2,getOutputStream().reset() 在输出一个对象流的时候 往往另一端会读到null(一般不会全是null,而是某些如ArrayList的对象会读出来null),这时候用输出对象的reset可以保证流的传输正确。
-------------------------我素华丽的分割线----------------------
以上两点有些困惑。
3, JTextArea.setCaretPosition(jChat.getText().length());通过每次添加字符串后调用此方法,可以使光标移至最后,保证其自动滚动
4,JTextArea.setLineWrap(boolean wrap)//一般的换行。 5월 13일 [转]P2P 之 UDP穿透NAT的原理与实现P2P 之 UDP穿透NAT的原理与实现(附源代码)
原创:shootingstars 参考:http://midcom-p2p.sourceforge.net/draft-ford-midcom-p2p-01.txt
论坛上经常有对P2P原理的讨论,但是讨论归讨论,很少有实质的东西产生(源代码)。呵呵,在这里我就用自己实现的一个源代码来说明UDP穿越NAT的原理。 首先先介绍一些基本概念: 呵呵,上面的基础知识可能很多人都知道了,那么下面是关键的部分了。 呵呵,现在该轮到我们的正题P2P了。有了上面的理论,实现两个内网的主机通讯就差最后一步了:那就是鸡生蛋还是蛋生鸡的问题了,两边都无法主动发出连接请求,谁也不知道谁的公网地址,那我们如何来打这个洞呢?我们需要一个中间人来联系这两个内网主机。 Server S (219.237.60.1) 首先,Client A登录服务器,NAT A为这次的Session分配了一个端口60000,那么Server S收到的Client A的地址是202.187.45.3:60000,这就是Client A的外网地址了。同样,Client B登录Server S,NAT B给此次Session分配的端口是40000,那么Server S收到的B的地址是187.34.1.56:40000。 |
|
||||||||||||
|
|