java网络编程 下

来源:互联网 发布:网络杀手是什么意思 编辑:程序博客网 时间:2024/05/16 23:01

如何检测和解决端口冲突问题

在命令行中使用netstat命令可以查看当前正使用的TCP端口号。为了彻底解决端口冲突的问题,需通过一个配置参数来指定TCP服务程序所使用的端口号。当某一个TCP程序与其他的程序发生端口号冲突的时候,就可以通过配置参数设置其端口号,而不用修改程序。

任何一种网络协议都要有默认端口号,如http协议默认的端口号为80

为了避免每当启动程序的时候都要用户指定端口号的麻烦,TCP服务程序应当允许在用户没有指定端口号的情况下使用一个默认的端口号。我们还可以将用户所指定的端口号保存到一个文件中,当服务器程序下次启动运行时,直接从文件中读取哪个端口号。这样用户想修改端口号时,只需改那个文件就可以了。

 

TCP客户端程序的编写

连接服务器的IP地址和端口号不要固定编写在程序代码中,而是通过程序的运行时参数来指定,以提供较好的灵活性和通用性。

import java.io.*;

import java.net.*;

publicclass TcpClient {

      public static void main(String[] args)

      {

             if(args.length<2)

             {

                    System.out.println("Usage:java TcpClient ServerIP ServerPort");

                    return;

             }

             try

             {

                    Socket s=new Socket(args[0],Integer.parseInt(args[1]));

                    InputStream ips=s.getInputStream();

                    OutputStream ops=s.getOutputStream();

                    BufferedReader brNet=new BufferedReader(new InputStreamReader(ips));

                    PrintWriter pw=new PrintWriter(ops,true);

                    BufferedReaderbr=new BufferedReader(

new InputStreamReader(System.in));

                    while(true)

                    {

                           String str=br.readLine();pw.println(str);

                           if(str.equals("quit"))

                           {

                                  break;

                           }

                           System.out.println(brNet.readLine());

                    }

                    br.close();pw.close();brNet.close();s.close();

             }

             catch(Exception e)

             {

                    e.printStackTrace();

             }

      }

}

 

TCP网络连接上传递对象

ObjectInputStreamObjectOutputStream可以从底层输入流中读取对象类型的数据和将对象类型的数据写入到底层输出流。注意创建该对象的类必须实现Serializable接口

使用ObjectInputStreamObjectOutputStream来包装底层网络字节流,TCP服务器和TCP客户端之间就可以传递对象类型的数据。使用writeObjectreadObject方法读取对象。

 

java.net包中还有一些用于访问资源的类:URLURLDecoderURLEncoderURLConnectionHttpURLConnecti等类。

 

URL的基本组成包括协议、主机名、端口号、资源名四个部分,而相对URL不包括协议和主机地址信息,表示他的路径和当前文档或其他网络资源的访问协议的主机名相同,甚至有相同的目录路径,他的使用与计算机的相对目录的使用相似。如/a.html:以一个斜杠开头表示主机上的某协议的根目录;如:../a.html两个点和一个斜杠表示当前资源所在目录的父目录;如:../../a.html用两个两个点和一个斜杠则表示父目录的父录;而直接使用文件名或目录名表示当前目录下的文件或子目录。以一个点和一个斜杠开头与直接使用直接使用文件名或目录名的效果相同。

http协议中浏览器不能像web服务器那样直接传递直接传递某些字符,必须对这些字符进行URL编码后再传输。URL编码规则:

将空格转换为加号(+;

0-9a-zA-Z之间的字符保持不变;

对于所有其他的字符,用这个字符的当前字符集编码在内存中的十六进制格式表示,并在每个字节前加上一个百分号(%)。如字符“+”用%2B表示;字符中用%D6%D0表示。

对于空格也可以直接使用其十六进制编码方式,即用%20表示,而不用将它转换为加号(+)。

 

java.net包中提供了URLEncoderURLDecoder这两个类,来实现URL编码和解码。

 

相对于http协议1.0http协议1.1可以在一次连接中处理多个文件请求,并且不管前面的请求是否已经响应,即可发送新的请求。文件的一次请求或响应被称为消息。

一个完整的请求消息包括:一个请求行、若干消息头、以及实体内容。其中消息头和实体内容是可选的,消息头和实体内容之间要有空行隔开。没有实体内容时,消息头后面也要有空行。消息头是由一系列的行组成的,每个行中都包含一个字段的名称(如时间、语言等),后面是冒号,冒号后面是空格,接着是字段的内容。只有使用postdeleteput方式的请求消息才能有实体内容。

同样一个完整的响应消息包括:一个状态行、若干消息头、以及实体内容。其中有关消息头和实体内容的规定与上同。

有实体内容的消息,消息头中必须含有实体长度字段。(对于http1.1而言的)

 

几个HTTP消息头

Connection:用于指定处理完本次请求/响应后,客户端与服务器端是否继续保持连接。设置只可以为Keep-Aliveclose,在http1.1协议中该字段的默认值为Keep-Alive

Accept-Language:用于指出客户机期望服务器返回的文档所使用的国家语言,可以指定多个,以逗号分隔的国家语言。有些服务器可以根据用户所用的浏览器的语言,返回相应的语言的文档。

Content-Length:用于表示实体内容的长度。(字节数)

Range:为请求消息头字段,用于指定服务器只需返回部分内容及内容范围,多用于较大文件的断点续传,有以下几种使用格式:

Range: bytes=100-599(返回第100个到第599个字节,字节顺序从0开始计算)

Range: bytes=100-(返回第100个字节以后的所有内容)

Range: bytes=-100(返回最后100个字节的内容)

Content-Range:Range配合使用,用于指定服务器返回的部分实体内容的位置信息,只有客户使用Range消息头,让服务器返回部分实体信息时服务器才会响应该消息头,例如:Content-Range: bytes 2543-4532/7898,其中2543-4532表明返回的内容为第2543字节到第4532字节,7898则指明了整个实体内容的长度。

 

URL

使用URL类,可以创建对应某个网络资源的URL对象。

构造函数(都可引发MalformedURLException异常):

public URL(String spec):参数为表示网络资源的URL字符串

public URL(String protocol,String host,int port,String file):几个参数分别表示:URL的协议名;主机名;端口号;资源本身的名称

public URL(String protocol,String host,int port,String file,URLStreamHandler handler):在第二个构造函数的基础上,还指出了处理资源所用到的协议处理器对象URL类不负责与远端资源的连接以及获取远端资源内容的工作,而是交给协议处理器(URLStreamHandler)的子类来处理。

public URL(URL context,Srting spec):在一个已经有的URL对象的基础上,再增加一个相对的URL字符文本来创建一个新的URL对象。

其他的一些获取URL元素的方法:getProtocolgetHostgetPortgetFile等方法。但是URL类不能获取网络资源的具体内容。

URL类提供了openConnection方法,该方法会调用协议处理器的openConnection方法,返回URLConnection对象,该对象代表URL所指向的网络资源的连接,程序调用URLConnection对象中的方法就可以获取网络资源的具体内容。

 

工厂设计模式

URL类有个setURLStreamHandlerFactory(URLStreamHandlerFactory fac)静态方法,URLStreamHandlerFactory对象用于建立协议名与协议处理器的对应关系。

当调用URL对象的时候,java虚拟机会根据其中出现的协议名,调用URLStreamHandlerFactory类中的createURLStreamHandler (String protocol)方法,将协议名传递给这个方法,在这个方法内部会根据传递过来的协议名创建协议处理器对象,然后返回给URL对象,该URL对象就会使用返回的协议处理器。

java虚拟机中维持着一个协议名与协议处理器的对照表,当URL没有调用setURLStreamHandlerFactory方法,以及StreamHandlerFactory类中的createURLStreamHandler (String protocol)方法的返回值为null,这时程序就会查找java中的那个对照表,如果对照表也不存在那个协议名所对应的协议处理器,或者根本找不到协议处理器所对应的class文件,那么URL的构造方法就会抛出MalformedURLException异常。

如果程序当中要用到一个或多个自定义的协议处理器,才需要编写一个或多个实现了URLStreamHandlerFactory接口的类,并在这个实现类的createURLStreamHandler方法中,根据传入的协议名产生相应的协议处理器。编写完这个实现类后,还要调用URL类的setURLStreamHandlerFactory方法,在java虚拟机中设置URLStreamHandlerFactory对象,在一个java虚拟机中最多调用一次setURLStreamHandlerFactory方法,并且该方法应在创建任何URL实例对象前被调用。

 

 

工厂设计模式的工作原理

X为一个抽象类或者接口,而XyXz为继承或实现X的类。工厂类中有一个getClass()方法,它可以利用参数a传递过来的值的不同,来决定生成实现了X的子类哪种对象。而对于调用者只须知道getClass方法返回的是实现了X的实例对象,该对象中含有X的方法,而对于实现的方式不用关心。

 

URLConnectionHttpURLConnection

应用程序与各种不同的URL网络资源的连接,是分别用不同的URLConnection子类来表示的,HttpURLConnection类就是支持Http协议的URLConnection的子类。、

在产生URLConnection对象后,URLConnection对象并没有与URL所指向的网络资源建立底层的网络连接。在URLConnection对象与URL所指向的网络资源建立底层的网络连接之前URLConnection可以调用各种set方法来设置它们两者之间的连接参数和请求属性。

调用setRequestProperty方法,就可以设置http请求协议的请求消息头

URLConnection对象调用connect方法与远端的资源建立连接以后,这些资源就变成可用的了,那么URLConnection对象就可以调用各种get方法去读取远端资源的信息。如getHeaderFileds方法就可以读取http服务器会送的所有响应消息头,如果URL对象已经与远端的资源建立了网络连接,应用程序就不能在调用URLConnection对象的设置连接参数和请求属性的set方法了。如果调用URLConnection对象的connect方法也会被忽略。

如果在URLConnection对象并没有与URL所指向的网络资源建立底层的网络连接的时候,调用URLConnectionget方法时,这些方法就会自动调用URLConnectionconnect方法先建立连接,然后再返回相应的值。

URLConnection类中还提供了getInputStreamgetOutputStream方法,分别返回用来读取http响应实体内容InputStream对象和写入http请求消息实体内容OutputStream对象。

URLConnection提供了用于读取各种头信息的通用方法:getHeaderFiled.比如传递一个Http响应消息的头字段,那么该方法就会返回该头字段的设置值。

URLConnection中还提供了getContentLengthgetContentEncodinggetContentType等读取头信息的方法。

每一个URLConnection对象只能用于一次对话请求。即它只能向服务器发出一次请求,得到一次响应结果。但是应用程序与http服务器之间建立的底层网络连接,可以被多个HttpURLConnection对象所共享。如果调用HttpURLConnection对象的InputStreamOutputStream对象的close方法,只能关闭HttpURLConnection对象相关的网络资源,而不会影响到底层的Http网络共享连接。如果底层的http网络连接处于空闲状态,即不存在HttpURLConnection对象来通过这个连接来读取和写入数据,那么调用HttpURLConnectiondisconnect方法可以关闭底层的共享网络。

 

编程举例:

import java.net.*;

import java.util.*;

import java.io.*;

public class GetGoogle {

      public static void main(String[] args) {

             System.out.println("获取日文页面");

             getContentByLanguage("ja");

             System.out.println("/n");

             System.out.println("获取中文页面");

             getContentByLanguage("zh-cn");

      }

   

      public static void getContentByLanguage(String country)

      {

             URL urlGoogle=null;

             HttpURLConnection googleConnection=null;

             try

             {

                    urlGoogle=new URL("http://www.google.com");

                 googleConnection=

                    (HttpURLConnection)urlGoogle.openConnection();

                 googleConnection.setRequestProperty("Accept-Language", country);

                 Map requests=googleConnection.getRequestProperties();

                 Set reqFiles=requests.keySet();//关键字集合

                 Iterator itrReq=reqFiles.iterator();

                 while(itrReq.hasNext())

                 {

                        String field=(String)itrReq.next();

                        System.out.println(field+":"+googleConnection.getRequestProperty(field));

                 }

                 //googleConnection.connect();

                 Map responses=googleConnection.getHeaderFields();

                 Set resFiles=responses.keySet();//关键字集合

                 Iterator itrRes=reqFiles.iterator();

                

                 while(itrRes.hasNext())

                 {

                        String field=(String)itrRes.next();

                        System.out.println(field+":"+googleConnection.getHeaderField(field));

                 }

                

                 //获取消息实体

                 InputStream is=googleConnection.getInputStream();

                 BufferedReader br=new BufferedReader(new InputStreamReader(is));

                 String strLine=null;

                 while((strLine=br.readLine())!=null)

                 {

                        System.out.println(strLine);

                 }

                 br.close();

                 googleConnection.disconnect();

             }

             catch(Exception e)

             {

                    e.printStackTrace();

             }

                    

      }

}