网络编程1
2016-11-29 22:55 backyyan 阅读(257) 评论(0) 编辑 收藏 举报网络编程
java语言提供了强大的网络编程功能,能够处理各种各样的网络资源和网络通信,使用户可以用流畅和完善的方式实现网络编程,完成各种复杂的网络应用开发。网络编程最主要的工作就是在发送端把信息通过规划好的协议进行组装包,在接收端按照规定好的协议把包进行解析,从而提取出对应的信息,达到通信的目的。其中,最主要的就是数据包的组装、数据包的过滤、数据包的捕获、数据包的分析、当然最后还要做一些处理。
网络编程基础
java的网络通信可以使用TCP、IP、和UDP协议。在进行java网络编程之前,对这些协议进行简单的介绍。
什么是TCP协议
TCP(Trasnsmission Conctrol Protocol)即传输控制协议,它是网络传输层的协议,主要负责数据的分组和重组。TCP协议提供了一种可靠的数据传输服务,它是面向连接的,大多数的网络应用程序都使用TCP协议来实现传输层。使用TCP协议创建一个网络应用程序非常容易,它可以保证数据传送的时间。顺序和内容的正确无误。但是使用TCP需要大量的网络开销,所以,如果希望实现更加高效的传输,TCP就不适合了。TCP所提供的服务主要特点如下:
面向连接的传输
端到端的通信
高可靠性,确保传输数据的正确性,不出现丢失或乱序
全双工方式传输
采用字节流方式,即以字节为单位传输字节序列
紧急数据传送功能
什么是IP协议
IP是Internet Protocol(网络之间互连的协议)的缩写,中文简称为“网协”,也就是计算机网络相互连接进行通信而设计的协议。在因特网中,它是能使连接到网上的所有计算机网络实现相互通信的一套规则,规定了计算机在因特网上进行通信时应当遵守的规则。任何厂家生产的计算机系统,只要遵守IP协议就可以与因特网互连互通。
IP代表每个计算机在网络中的唯一标识,是比TCP低级的协议。IP地址具有唯一性,根据用户性质的不同,可以分为5类。另外,IP还有进入防护,知识产权,指针寄存器等含义。
IP地址是32位(IPv4)或128位(IPv6)的无符号数字,使用4组数字表示一个固定的编号,数字之间用一个点号隔开,如“172.168.1.52”就代表网络中一个计算机唯一的地址编号。
什么是TCP/IP
TCP/IP(Transmission Conctrol Potocol、Internet Potocol)即传输控制协议/网际协议,是一个工业标准的协议集,它是为广域网(WAN)设计的。它是由APPANET网的研究机构发展起来的。
有时我们会将TCP/IP描述为互联网协议集(InternetPotocolSuite),TCP和IP是其中的两个协议。由于TCP和IP是大家熟悉的协议,以至于用TCP/IP或IP/TCP这个词来代替整个协议集。这尽管有点奇怪,但没必要去争议这个习惯。例如,有时候我们讨论NFS是基于TCP/IP时,尽管它根本没有用到TCP(只用到IP和另一种交互式协议UDP而不是TCP)
什么是UDP协议
UDP(User Datagram Potocol)指的是用户数据包协议。它和TCP协议一样,都是网络传输层上的协议,但是它与TCP有本质的区别。使用UDP协议传输是,不保证数据一定能到达目的地,也不保证到达的顺序性。但是UDP协议占用的资源比较少,所以一般用在一些可靠性要求比较低的网络应用上,如网络视频会议,在线影视和聊天室等音频,视频数据传达。
什么是端口
端口(Port)可以理解为计算机与外界通信交流的窗户。网络上的一台计算机可以提供多个服务,如web服务,FTP服务和Telnet服务。那么,如何分这些服务?单纯依靠IP地址是不行的,因为同一台计算机的IP地址是同一个。实际上,可以通过“IP地址+端口号”的形式来区分不同的服务。当信息到达时,根据其端口号的不同,就可以知道应该提供哪个服务。
常见的端口服务:
HTTP:80
FTP:21
Telnet:23
SMTP:25
什么是套接字
套接字(Socket)是支持TCP/IP的网络通信的基本操作单元,可以看做是不同主机之间的进程双向通信的断面点,简单来说就是通信双方的一种约定,用套接字中的相关函数来完成通信过程。某个程序将一段信息写入套接字中,该套接字就会将这段信息发送给另外一个套接字,就像电话线的两端一样,这样,另外一段的程序就通过另一端的套接字接受到了这段信息。所以,使用套接字编程有时被称为Socket编程。
java.net包
在java的API中,java.net包是被用来提供网络服务的。java.net包中含有各种专门用于开发网络应用程序的类,程序开发人员使用该包中的类可以很容易的建立基于TCP可靠连接的网络程序,以及基于UDP 不可靠连接的网络程序。java.net包大致分为以下两部分。
低级API:用于处理网络地址(也就是网络标识符,如IP地址)、套接字(也就是基本数据双向通信机制)和接口(用于描述网络接口)。
高级API:用于处理URI(表示统一资源标识符)、URL(表示统一资源定位符)、URLConnection连接(表示到URL所指向资源的连接)等。
InetAddress类
任何一台运行在Internet上的主机都有IP地址和当地的DNS能够解析的域名。在java.net包中提供了IP地址的封装类InetAddress。
InetAddress类用于描述和包装一个InternetI拍地址,并提供了相关的常用方法,例如,解析I片地址的主机名称、获取本机IP地址的封闭、测试指定IP地址是否可达等。InetAddress实现了java.io.Serializable接口,不允许继承。它通过以下3个方法返回InetAddress实例。
getLocalhost():返回封装本地地址的实例
getAllByName(String host)返回封装Host地址的InetAddress实例数组。
getByName(String host):返回一个封装Host地址的实例。其中,Host可以是域名或一个合法的IP地址
InetAddress类的常用方法
方法名称 方法说明 返回类型
getLocalHost() 返回本地主机的InetAddress对象 InetAddress
getByName(String host) 返回指定主机名称的IP地址 InetAddress
getAllByName(String host) 返回指定主机名称数组 InetAddress数组
getHostName() 获取本地主机名称 String
getHostAddress() 获取本地主机地址 String
isReachable(int timeout) 在指定的时间(毫秒)内,测试IP地址是否可到达 boolean
在这些静态方法中,最为常用的应该是getByName(String host)方法,只需要传入目的主机的名字,InetAddress会尝试两级DNS服务器,并获取IP地址的操作。代码片段如下:假设下面代码都是默认导入java.net包,在程序开头加上import java.net.*,否则,需要指定类的全名java.net.InetAddress。
InetAddress address=InetAddress.getByName("www.baidu.com");
注意这些方法可能会抛出异常。如果安全管理区不允许访问DNS服务器或禁止网络连接,SecurityException会抛出,如果找不到对应主机的IP地址或发生其他网络I/O错误,这些方法或抛出UnknowHostException。所以需要写如下代码:
try { InetAddress address=InetAddress.getByName("www.baidu.com"); System.out.println(address); } catch(UnknowHostException e) { e.printStackTrace(); } }
下面通过示例使用InetAddress类来获取相关网络信息,代码如下:
import java.net.*; public class test1{ public static void main(String []args)throws Exception{ InetAddress ia=null; //定义变量ia try{ ia=InetAddress.getLocalHost(); } catch(UnknowHostException e){ e.printStackTrace(); }//获取本机的InetAddress对象 System.out.println("本机的主机名为:"+ia.getHostName()); System.out.println("本机的IP为:"+ia.getHostAddress()); } }
URL网络编程
java类库中提供了许多高级别的网络类。其中,URL(统一资源定位符)类就是这样一个网络类。
URL
URL是统一资源定位符,表示Internet上某一资源的地址,又称网页地址。通过URL,开发人员可以访问Internet上的各种资源,如常见的WWW服务或FTP服务。浏览器通过解析URL,就可以找到像对应的资源。例如下面是Google的地址:
http://www.google.com
这个URL由两部分组成:协议标识和资源名称。其中“http”为使用协议,它指的是超文本传输协议(HTTP)。其他常用的协议还包括文件传输协议(FTP)、Gopher、File和News。
URL还提供了多个方法,可以获取URL的对象协议、主机名、端口号、路径、查询字符串、文件名及资源引用。URL的常用方法如下:
方法名称 方法说明
getPotocol() 获得URL的协议名
getAuthority() 获得URL的主机名和端口号
getFile() 获得URL的文件名
getHost() 获得URL的主机名
getPath() 获得URL的路径
getPort() 获得URL的端口号
getQuery() 获得URL的查询字符串
getRef() 获得该URL的引用锚记名
符语法
授权部分一般是服务器的名称或IP地址,有时后面还跟一个冒号和一个端口号。它也可以包含接触服务器必须的用户名称和密码。路径部分包含等级结构的路径定义,一般来说,不同部分之间以/(斜线)分割。询问部分一般用来传送对服务器上的数据库进行动态询问时 所需要的参数。完整的,带有授权部分的普通统一资源标识符语法如下:
协议://用户名@密码:子域名.域名.顶级域名:端口号/目录/文件名.文件名后缀?参数=值#标志
统一资源标识符参考指的是单个的(如超文本传输协议文件中的)统一资源标识符。统一资源标识符参考分绝对参考和相对参考。上述都是绝对的统一资源标识符参考,相对参考只包含体制特殊的部分,它参考的对象位于包含这个参考文件的一个相对位置上。统一资源标识符参考还可以由一个统一资源标识符加上一个#符再加上上述的统一资源标识符内的一个标志点。这个标志点不是统一标识符的一部分,而是让用户浏览器在获得文件后来导航用的,因此它实际上不被传送到服务器。下面通过实例来使用URL的各种方法,代码如下:
import java.net.*; import java.io.*; public class test2{ public static void main(String []args) throws Exception{ URL aURL=null; try{ aURL=new URL("http://www.google.com:80"); } catch(MalformesURLException e){ e.printStackTrace(); //输出URL对象协议 System.out.println("protocol="+aURL.getProtocol()); //输出URL的对象的主机名和端口名 System.out.println("authority="+aURL.getAuthority()); //输出URL的主机名 System.out.println("host="+aURL.getHost()); //获得该URL的端口 System.out.println("port"+aURL.getPort()); } }
运行结果如下:
protocol=http authority=www.google.com:80 host=www.google.com port=80
需要注意的是,在ParseURLDemo类的main()方法声明抛出异常,由系统处理,所以在代码中没有使用try/catch语句处理异常。
URLConnection类
URLConnection类用来表示与URL建立的通信连接。位于java.net包中,调用URL类的openConnection()方法可以获得URLConnection对象。获得对象后,开发人员可以调用该对象的connect()方法来连接远程资源,代码如下:
try{ URL wy=new URL("http://www.163.com/"); URLConnection wyConnection=wy.openConnection();//获得URLConnection对象 wyConnection.connect();//连接远程资源 } catch(MalformedURLExceeption e){//创建URL失败时,会产生此异常 } catch(IOException e){//openConnection() 方法失败时,会产生此异常 }
和远程资源建立连接以后,就可以执行相关操作,包括查询HTTP请求头信息,访问资源数据,以及写入相关内容等。在main()方法声明中抛出异常,由系统处理,所以在代码使用try/catch语句处理异常。
虽然读取内容时与URL的openStream()方法相同,但是使用URLConnection类的getOutputStream()方法获得输出流还可以像输出流中写入数据,在URL 另一单的服务器程序可以接受输入的数据。
TCP的网络编程
TCP(传输控制协议)是一种基于连接的协议,可以在计算机之间提供可靠的数据传输。连接通道的两端通常称为套接字(Socket),基于TCP的网络通信也是如此,先建立起连接,再通过套接字发送和接受数据。
Socket
通过TCP进行通信的双方通常被称为服务器端和客户端。服务器端和客户端可以是两台不同的计算机,也可以是同一台计算机。服务器端运行的是服务器端程序,和运行哎客户端程序有所不同。
根据连接启动的方式以及本地套接字要连接的目标,套接字之间的连接过程可以分为3个步骤:服务器监听、服务端请求、连接确认。
服务器监听:是指服务器端套接字,并不是定位具体的客户端套接字,而是处于等待连接的状态,实时监控网络状态。
客户端请求:是指由客户端的套接字提出连接请求,要连接的目标是服务器端的套接字。为此,客户端的套接字必须首先描述它要连接的服务器的套接字,指出服务器端套接字的地址和端口号,然后就向服务器端套接字提出连接请求。
连接确认:是指的那个服务器套接字监听到货接收到客户端套接字的连接请求,它就响应客户端套接字的请求,建立一个新的线程,把服务器端套接字的描述发给客户端。一旦客户端确认了此项描述,连接就建立好了,而服务器端套接字继续处于监听状态,继续接收其他用户端的套接字连接请求。
在java.net包中,有一个Socket类,用来建立套接字。java.net包中还有一个ServerSocket类,用在服务器端,其中有一个accept方法,用来监听和接收客户端的连接请求。java.net.Socket类是开发网络应用程序的服务器端和客户端程序都要用到的套接字类。它提供了许多方法来获得相关信息。
Socket类的常用方法
方法名称 方法说明
getInputStream() 从套接字中获得输入流
getInetAddress() 从套接字中获得网络地址
getLocalAddress() 获得本机的网络地址
getPort() 获得套接字中的端口
getLocalPort() 获得本机端口
getOutputStream() 从套接字中获得输出流
close() 关闭套接字
重要的Socket API
java.net.Socket继承于java.long.Object,有8个构造器,其方法并不多。下面介绍使用频率最频繁的3个方法:
Accept()方法:用于产生“阻塞”,直到接受到一个连接,并且返回一个客户端的Socket对象实例。“阻塞”是一个术语,它使程序运行暂时“停留”在这个地方,直到一个会话产生,然后程序继续;通常“阻塞”是由循环产生。
GetInputStream()方法:获得网络连接输入,同时返回一个InputStream对象实例。
GetOutputStream()方法:连接另一端将得到输入,同时返回一个OutputStream对象实例,注意,getInputStream和getOutputStream方法均可能会产生一个IOException,它必须被捕获,因为它们返回的流对象通常是会被另一个流对象使用。
服务器端程序设计
服务器端程序需要用到java.net.Socket类和java.nt.ServerSocket类。服务器端程序的建立通常需要以下5个步骤:
1:在服务器端程序中,首先要创建类java.net.ServerSocket的实例对象,注册在服务器端进行连接的端口号及允许连接的最大用户数目。
2:调用ServerSocket的成员方法accept(),等待并监听来自客户端的连接。当有客户端与该服务器端建立连接时,accept()方法将返回Socket连接通道在服务器端的套接字(Socket)。通过该套接字可以与用户端进行数据通信。
3:调用服务器端的套接字Socket方法getInputStream()和getOutputStream(),获得该套接字所对应的输入流(InputStream)和输出流(OuputStream)。
4:通过获得输入流和输出流与客户端记性数据通信,并处理从客户端获得的数据及要像客户端发送的数据。
5:在数据传输结束以后,关闭输入流、输出流和套接字。
在服务器端创建ServerSocket的实例对象,并调用其accept()方法之后,服务端开始一直等待客户端与其连接。ServerSocket类的常用方法如下:
方法名称 方法说明
accept() 阻塞程序等待客户端连接请求。一旦接收到连接请求,则返回一个表示连接已经建立的Socket对象。
close() 关闭当前的Socket
getInetAddress() 获得本机的网络地址
getLocalPort() 获得监听端口
下面介绍一个简单的网络应用程序的服务端程序示例。程序的功能为读取监听客户端的连接请求,并向客户端发送问候信息,当客户发送过来“end”时,结束连接。代码如下:
import java.io.*; import java.net.*; public class test300 { public static void main(String []args)throws IOException{ import java.net.*; public class test300 { public static void main(String []args)throws IOException{ ServerSocket server=null; try{ server=new ServerSocket(5678); }catch(IOException e){ e.printStackTrace(); }//创建ServerSocket实例,参数为监听的端口号 System.out.println("服务器已正常启动,正在等待连接。。。。"); Socket client=null; try { client =server.accept(); }catch(IOException e){ e.printStackTrace(); }//阻塞,等待客户端连接 System.out.println("客户端正在建立连接"); //获得Socket连接的字节输入流并转换为缓冲字符流 BufferedReader in=new BufferedReader(new InputStreamReader(client.getInputStream())); //活儿Socket连接的打印输出流 PrintWriter out=null; try{ out=new PrintWriter(client.getOutputStream()); }catch(IOException e){ e.printStackTrace(); } while(true){ String str=in.readLine(); System.out.println(str); out.println("已接受。。。"); out.flush(); if(str.equals("end"))//如果客户端发过来end,说明已结束连接。 break; } in.close(); out.close(); client.close(); server.close(); } }
运行结果如下:
服务器已启动,正在等待连接。。。
如上面代码所示,首先要在服务器端使用ServerSocket(int port)构造器构造一个ServerSocket的服务器端套接字实例。其中参数port为ServerSocket类要监听的端口。创建ServerSocket可以使用4种构造器。如下所示:
ServerSocket server1=new ServerSocket(); ServerSocket server2=new ServerSocket(5678); ServerSocket server3=new ServerSocket(5678,100); ServerSocket server4=new ServerSocket(int port,int backlog,InetAddressbindAddr);
其中不带参数的构造器为默认构造方法,可以常见为绑定端口号的服务器套接字。带一个参数的构造器,将创建保定到指定端口的服务器套接字对象,并且默认的最大谅解队列长度为50.如果连接数量超过50个,将不会再几首新的连接请求。带有两个参数的构造器使用参数指定的端口号和最大的连接队列长度创建服务器套接字对象。而最后一个构造方法,放服务器有多个IP地址时,使用bindAddr参数知道那个创建服务器套接字的IP地址。