`
Lanyef
  • 浏览: 10581 次
  • 性别: Icon_minigender_1
  • 来自: 长沙
社区版块
存档分类
最新评论

网络通信中常见问题

阅读更多

1.  IP  Port

1.有关IP unknowhost 

我还没遇到这样的情况,自己模拟也没模拟出来,主要不是很明白这个UnKnowHost 异常,我看了java.net.Socket.class 中的源码,创建对象是,会抛出这个异常,描述:

 if the IP address of the host could not be determined .

google 翻译了下,说是:如果该主机的IP地址无法确定,和我理解的差不多,就不知道这个无法确定是嘛意思。

遇到的另一个关于IP 地址的错误,是在使用 UDP 协议时。UDP 是通信相对不可靠的,并不需要建立连接,发送方只要知道目标地址就可以了。发送方创建DatagramSocket 对象,用于发送数据包。关键代码如下:

 

// 1. 创建一个用来发送的本地地址对象

     java.net.SocketAddress localAddr=new java.net.InetSocketAddress("192.168.0.15",10000);

     try {

        // 2. 创建发送 Socket 对象

        java.net.DatagramSocket sender=new java.net.DatagramSocket(localAddr);

    }

 

DatagramSocket 创建时,要传入地址和端口,是发送数据包的地址和端口,即本地地址和一个端口号,地址如果写错了,会抛出如下的异常:

 

java.net.BindException: Cannot assign requested address: Cannot bind

    at java.net.PlainDatagramSocketImpl.bind0(Native Method)

    at java.net.PlainDatagramSocketImpl.bind(PlainDatagramSocketImpl.java:82)

    at java.net.DatagramSocket.bind(DatagramSocket.java:368)

    at java.net.DatagramSocket.<init>(DatagramSocket.java:210)

    at network.udp.p2p01.DatagramSender.main(DatagramSender.java:22)

 

2.端口已被占用

 

 

int port=9000;

        // 创建服务器对象,指定 port 端口

        java.net.ServerSocket serverSocket=new java.net.ServerSocket(port);

第二次运行时报出异常:

java.net.SocketException: Unrecognized Windows Sockets error: 0: JVM_Bind

    at java.net.PlainSocketImpl.socketBind(Native Method)

    at java.net.PlainSocketImpl.bind(PlainSocketImpl.java:365)

    at java.net.ServerSocket.bind(ServerSocket.java:319)

    at java.net.ServerSocket.<init>(ServerSocket.java:185)

    at java.net.ServerSocket.<init>(ServerSocket.java:97)

    at network.problem.Test01.run(Test01.java:21)

 

 

 

2.  等待连接时,不同的循环方式

Server 端运行后,应该是一直运行等待 Client 的连接。显然,会用到while循环。while循环在使用中,最大的问题就是会经常死循环,导致程序错误。在通信中,Server 端用 while循环来一直等待Client 的连接,代码书写中会出现这样的问题:循环中的代码和循环外代码位置错误。

代码示例如下:

 

int port=9000;

while(true){

           // 创建服务器对象,指定 port 端口

           java.net.ServerSocket serverSocket=new java.net.ServerSocket(port);

           java.net.Socket clientSocket=serverSocket.accept(); 

        }

代码中,是什么问题呢?代码肯定是可以执行的,当有一个客户机连接以后,Server 端就报错了。因为又创建了一个Socket 对象。画线的一行是创建Socket 对象,应该在 while 的前面。至于报出的错误,就是一个端口被占用的异常。PSwhiletrue)本身就是死循环,所以应该尽量减少使用,减少出错的可能,这里可以添加一个Flag 属性,解决死循环的问题。

 

3.  协议不对称

网络通信中,什么最重要?当然是协议。比如常见的TCP 协议、IP 协议,UDP协议、XMPP协议等等。接触了一段时间的通信后,对协议有些了解。我觉得,协议就是一种规则,只要通信的双方可以理解就够了。例如,我们打电话的时候,你这边说着一口标准的长沙话,那边操着一口流利的江苏话,你一句我一句的,谁也没听懂对方说什么,那还打电话做什么咯。网络通信也是一样的,你发送数据给对方,都是一个一个的Byte,对方需要解读,就必须知道相应的协议,每一个Byte是怎样的意义。所以,通信前,必须要做的就是制定你们自己的协议,然后,通信中严格按照规定的协议进行数据的传输。

 

4.  read( [] ) readFully ( ) 区别

    通信中,协议很重要,但是输入、输出流也是不可或缺。我们需要用输入、输出流读取和写入数据,然后发送。之前在做简单的IM 聊天系统中,我使用自定义字节流协议,用到了 DataInputStream 对象。DataInputStream 有这样两个方法,见代码:

 

public final int read(byte b[], int off, int len) throws IOException {

   return in.read(b, off, len);

    }

 

 

public final void readFully(byte b[], int off, int len) throws IOException {

   if (len < 0)

       throw new IndexOutOfBoundsException();

   int n = 0;

   while (n < len) {

       int count = in.read(b, off + n, len - n);

       if (count < 0)

      throw new EOFException();

       n += count;

   }

    }

而我们常使用的readbyte[] b)和readFully(byte[] b)又是分别调用上面两个方法。看代码就很容易知道,其实在这两个方法中,还是调用 InputStream read(byte[] b,int off,int len) 方法,传入一个byte数组,存储读取的数据,off为偏移量,len 要读取的长度。无论怎样读取,在最底层,输入流依然是一个字节一个字节的读取。但是,DataInputStream 的这两个方法有怎样的区别呢?我们知道,在InputStream read()方法中,当读取到流的末尾,返回-1,方法结束。所以,DataInputStreamreadbyte[] b)不一定会读取到你想要读取的字节数,会提前返回,而且没有征兆,代码没有错误,但是运行以后,或许会出错,而且出错以后会让人有点儿丈二和尚的感觉。然而,出现这样的情况后, readFully(byte[] b)方法会抛出异常,经典的EOF,告诉你读取中 end of file 了。就比如说,流中有10个字节的数据,程序要读取100 个字节的数据,read方法就会读取10个字节储存在byte[]中,然后方法返回;而readFully也会读取10个字节,但是会抛出EOF异常。 我个人认为呢,readFully 的意义就在于while 循环中每次的 if 判断,这个类似于一中人性化的纠错机制。

5.  读取数据,导致内存错误

   1. 数组创建时,大小不能超过int的值,即 2,147,483,647 。如下代码:  

  Byte[] b=new byte[10000000000000000];

当然,超过了没有语法错误,但是运行时就会出错。这个问题很容易理解,初始化数组,太大了必然造成内存的浪费,影响系统的运行,降低了程序的性能。这样的错误呢,实在不值得说,估计大家都没有出现过,但是原理还是要明白。

 

Exception in thread "main" java.lang.Error: Unresolved compilation problem:

    The literal 10000000000000000000 of type int is out of range

 

 

  2.另外一个关于内存的问题,就是在平常不过的内存溢出了。我们知道,机器的内存总是有限的,虽然现在的机器内存越来越大,但是不可能无限大。所以,在循环的时候,尤其while 循环,是否需要每一次循环都创建对象,是个值得考究的问题。否则呢,一不小心就 OutOFMemory 了。

6.  EOFException

    前面说read readFully 的时候,捎带的提了一下,可能不是很容易理解。这里,详细的说下我的理解。EOF 就是 end of file ,到文件的末尾了。我们知道,在TCP通信中,必须要建立Socket连接。这个Socket连接就好像建立的连接双方的通道,数据就是在这个通道上传送。起初呢,我就是这样理解的,了解TCP以后,发现,这个通道其实是个虚拟的很东西,很抽象,在现实世界中并不存在,而存在的是网线。这个虚拟的通道时承载在网络传输介质上的。在程序中,Socket对象的输出流写入数据以后,经过网卡,网线然后到达对方的机器,然后网卡,在返回给程序。所以,这样一来,在数据的传输中,会出现这样的一种情况,有的数据已经被接收方读取了,但是有的数据还没有到,或者在网卡,或者还没有发送(当然这是比较极端的情况),接收方输入流读取发现没有了,但是又没有读取len 个字节,抛出EOF异常。这个就好比在公共汽车站,汽车会等乘客,但是也不是无休止的等,当汽车该发车的时候,即使没有坐满,也会发车。一个个的字节就相当于乘客,汽车就是输入流,没有坐满也发车的情况就是一个EOF

7.  断开连接 (正常的异常 )

    连接可以建立,就一定也有可能断开。断开的原因又有很多,比如说,网线掉了、断了,连接的双方中有人当机了,或者主动关闭程序了,都会出现断开连接的异常。这个是比较好的异常,因为很正常,因为断开就想意外,总是无法避免的。

  

8. try-catch  or  throws

   至于这个呢,其实我也不是很明白,慢慢学,慢慢体会吧,明白了在补上。

1
0
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics