------ >Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------
Java语言是第一个完全融入网络的语言,Java语言之所以适合编写网络应用程序有诸多有点。所有连入Internet的终端设备(包括计算机、PDA、打印机以及其他的电子设备)都有一个唯一的索引,这个索引被称为IP地址。现在Internet上的IP地址大多由四个字节组成,这种IP地址叫做IPv4。除了这种由四个字节组成的IP,在Internet上还存在一种IP,这种IP由16个字节组成,叫做IPv6。IPv4和IPv6后面的数字是Internet协议(Internet
Protocol,IP)的版本号。
网络通信需要三个基本条件:
1. 找到对方IP;
2. 数据要发送到对方指定的应用程序上,为了标识这些应用程序,所以给这些网络应用程序都用数字进行标识,为了方便称呼这个数字,叫做端口或者叫逻辑端口。端口范围0~65536,通常0~1024被系统使用或保留。
3. 定义通信规则,这个规则称为协议。国际组织定义了通用协议 TCP/IP,常用的协议还有UDP。
数据在ISO七层模型中的传输过程其实是数据的封包和拆包过程。
InetAddress类是Java中用于描述IP地址的类。它在java.net包中。InetAddress没有public的构造方法,因此,要想创建InetAddress对象,必须得依靠它的四个静态方法。InetAddress可以通过getLocalHost方法得到本机的InetAddress对象,也可以通过getByName、getAllByName和getByAddress得到远程主机的InetAddress对象。
使用getLocalHost可以得到描述本机IP的InetAddress对象。getByName方法是InetAddress类最常用的方法。它可以通过指定域名从DNS中得到相应的IP地址。getByName一个String类型参数,可以通过这个参数指定远程主机的域名。
使用getAllByName方法可以从DNS上得到域名对应的所有的IP。这个方法返回一个InetAddress类型的数组。
getByAddress方法必须通过IP地址来创建InetAddress对象,而且IP地址必须是byte数组形式。
演示代码如下:
import java.net.*; class IPDemo { public static void main(String[] args) throws Exception { //InetAddress i =InetAddress.getLocalHost(); //获取本机地址 //System.out.println(i.toString()); InetAddress ia= InetAddress.getByName("www.baidu.com"); //获取百度的主机地址和主机名 System.out.println("address:"+ia.getHostAddress()); System.out.println("name:"+ia.getHostName()); } }
网络应用分为客户端和服务端两部分,而Socket类是负责处理客户端通信的Java类。通过这个类可以连接到指定IP或域名的服务器上,并且可以和服务器互相发送和接受数据。一般网络客户端程序在连接服务程序时要进行以下三步操作:
1.
连接服务器
2.
发送和接收数据
3.
关闭网络连接
在客户端可以通过两种方式来连接服务器,一种是通过IP的方式来连接服务器,而另外一种是通过域名方式来连接服务器。
在很多程序设计语言或开发工具中(如C/C++、Delphi)使用域名方式连接服务器时必须自己先将域名解析成IP,然后再通过IP进行连接,而在Java中已经将域名解析功能包含在了Socket类中,因此,我们只需象使用IP一样使用域名即可。通过Socket类连接服务器程序最常用的方法就是通过Socket类的构造函数将IP或域名以及端口号作为参数传入Socket类中。
在Socket类中最重要的两个方法就是getInputStream和getOutputStream。这两个方法分别用来得到用于读取和写入数据的InputStream和OutputStream对象。在这里的InputStream读取的是服务器程序向客户端发送过来的数据,而OutputStream是客户端要向服务端程序发送的数据。
网络通信中常用的两种协议有TCP和UDP协议,二者区别如下:
UDP:面向无连接,将数据及源和目的封装在数据包中,发数据前无需建立连接,每个数据包大小限制在64k内。因无连接,是不可靠协议,无需建立连接,速度快。聊天,视频会议,都使用的是UDP协议。
TCP :建立连接,形成数据传输通道,在连接过程中进行大数据量传输,通过三次握手完成传输(在吗?在,我知道你在了),是可靠协议,必须建立连接,效率稍低。下载使用的是TCP协议。
下面是一个小练习,需求:动过UDP传输方式,将一段文字数据发送出去。
思路:1.建立UDPsocket服务;
2.提供数据,并将数据封装到数据包中
3.通过socket服务的发送功能,将数据包发送出去
4.关闭资源。代码实现如下:
import java.net.*; class UDPSend { public static void main(String[] args) throws Exception { DatagramSocket ds = new DatagramSocket(); //创建UDP服务 //确定数据,并封装成数据包 DatagramPacket(byte[] buf,int length,InetAddress address,int port) byte[] buf= "udp laile".getBytes(); DatagramPacket dp = new DatagramPacket(buf,buf.length,InetAddress.getByName("192.168.1.101"),10000); ds.send(dp); // 发送数据包 ds.close(); } }
需求: 定义一个应用程序,用于接收udp协议传输的数据并处理
思路:1.定义udpsocket服务;
2.定义一个数据包,存储接收到的字节数据,因为数据包对象中有更多功能,提取字节数据中的不同数据信息;
3.通过socket服务的receive方法将接收到的数据存入到定义好的数据包中;
4,通过数据包对象的特有功能,将这些不同的数据取出,打印在控制台;
5,关闭资源。实现代码:
class UDPRece { public static void main(String[] args) throws Exception { DatagramSocket ds = new DatagramSocket(10000); //创建udpsocket 建立端点 //定义数据包,用于存储数据 byte[] buf = new byte[1024]; DatagramPacket dp = new DatagramPacket(buf,buf.length); //通过服务的receive方法将收到的数据存入数据包中 ds.receive(dp); //阻塞式方法,没数据就等 //通过数据包的方法获取其中的数据 String ip = dp.getAddress().getHostAddress(); String data = new String(dp.getData(),0,dp.getLength()); int port = dp.getPort(); System.out.println(ip+"::"+data+"::"+port); //ds.close(); //关闭资源 } }
练习:编写聊天程序,
分析:有收数据和发数据部分,需要两个线程,分别负责收和发,因为收和发动作不一致,所以要定义两个run方法,而且这两个方法要封装到不同的类当中。
import java.net.*; import java.io.*; class Send implements Runnable { private DatagramSocket ds; public Send(DatagramSocket ds) { this.ds=ds; } public void run() { try { BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in) ); String line = null; while ((line = bufr.readLine())!=null) { if("886".equals(line)) //如果发送886,聊天程序结束 break; byte[] buf= line.getBytes(); DatagramPacket dp= new DatagramPacket(buf,buf.length,InetAddress.getByName("192.168.1.101"),10002); ds.send(dp); } } catch(Exception e) { throw new RuntimeException("发送端失败"); } } } class Rece implements Runnable { private DatagramSocket ds; public Rece(DatagramSocket ds) { this.ds=ds; } public void run() { try { while (true) //循环接收 { byte[] buf = new byte[1024]; //将数据接收到数据包中 DatagramPacket dp = new DatagramPacket(buf,buf.length); ds.receive(dp); String ip = dp.getAddress().getHostAddress(); String data=new String(dp.getData(),0,dp.getLength()); System.out.println(ip+"::"+data); } } catch (Exception e) { throw new RuntimeException("接收端失败"); } } } class ChatDemo { public static void main(String[] args) throws Exception { DatagramSocket sendSocket= new DatagramSocket(); DatagramSocket receSocket= new DatagramSocket(10002); new Thread(new Send(sendSocket)).start(); new Thread(new Rece(receSocket)).start(); } }
TCP传输分客户端和服务端,对应的对象分别是Socket和ServerSocket。
客户端:通过查阅Socket对象,发现该对象建立时就可以连接指定的主机,因为TCP面向连接,所以建立socket服务时,就要有服务端存在,并连接成功,形成通路后,在该通道进行数据传输。
需求:给服务端发送一个文本数据
步骤 1.创建socket对象,指定要连接的主机和端口;
2.发送数据。实现代码如下:
import java.io.*; import java.net.*; class TCPClient { public static void main(String[] args) throws Exception { //创建服务端socket对象,指定要连接的主机和端口 Socket s=new Socket("192.168.1.101",10003); //为了发送数据,获取socket流中的输出流 OutputStream out=s.getOutputStream(); out.write("TCP 数据来了 ".getBytes()); s.close(); } }
需求:定义端点接收数据并打印在控制台上
服务端:1.建立服务端的socket服务,ServerSocket(),并监听一个端口;
2.获取连接过来的客户端对象,通过ServerSocket的accept方法,没有连接就会等,该方法是阻塞式方法;
3.客户端如果发过来数据,服务端要使用对应的客户端对象,并获取该服务端对象的读取流,读取发送过来的数据;
4.关闭服务器(可选)。
class TCPServer { public static void main(String[] args) throws Exception { ServerSocket ss=new ServerSocket(10003); //1. //通过acept方法获取连接过来的客户端对象 Socket s=ss.accept(); String ip=s.getInetAddress().getHostAddress(); System.out.println(ip+"......connected"); //获取客户端发送过来的数据,使用客户端对象的读取流来读取数据 InputStream in=s.getInputStream(); byte[] buf=new byte[1024]; int len = in.read(buf); System.out.println(new String(buf,0,len)); s.close();//关闭客户端 ss.close(); } }
TCP通信方式练习:
需求:建立一个文本转换服务器,客户端给服务端发送文本,服务端将文本转成大写再返回给客户端。而且客户端可以不断的进行文本转换,客户端输入over时,转换结束。
分析:
客户端:既然操作的是设备上的数据,就可以使用IO技术,并按照IO的操作规律思考。
源:键盘录入。 目的:网络设备,网络输出流,操作的是文本数据,可选字符流。
步骤:
1.建立服务; 2.获取键盘录入;3.将数据发送给服务端;4.去服务端返回大写数据;5.结束,关资源。都是文本数据,可以使用字符流操作,同时提高效率,加入缓冲。
实现代码:
import java.io.*; import java.net.*; class TransClient { public static void main(String[] args) throws Exception { Socket s=new Socket("192.168.1.101",10005); //定义读取键盘数据的流对象 BufferedReader bufr =new BufferedReader(new InputStreamReader(System.in));//源 //定义目的,将数据写入到Socket输出流,发给服务端 BufferedWriter bufOut=new BufferedWriter(new OutputStreamWriter(s.getOutputStream())); //目的 //定义一个socket读取流,读取服务器返回的大写信息 BufferedReader bufIn= new BufferedReader(new InputStreamReader(s.getInputStream())); String line=null; while ((line =bufr.readLine())!=null) { if ("over".equals(line)) break; bufOut.write(line); bufOut.newLine(); //换行符,结束标记 bufOut.flush(); String str=bufIn.readLine(); System.out.println("Server::"+ str); } bufr.close(); s.close(); } } class TransServer { public static void main(String[] args) throws Exception { ServerSocket ss=new ServerSocket(10005); Socket s =ss.accept(); String ip= s.getInetAddress().getHostAddress(); System.out.println(ip+"...connected"); //读取socket读取流中的数据 BufferedReader bufIn= new BufferedReader(new InputStreamReader(s.getInputStream())); //目的,socket输出流,将大写数据写入到socket输出流,并发送给客户端 BufferedWriter bufOut= new BufferedWriter(new OutputStreamWriter(s.getOutputStream())); String line=null; while ((line =bufIn.readLine())!=null) { System.out.println(line); bufOut.write(line.toUpperCase()); bufOut.newLine(); //换行符,结束标记 bufOut.flush(); } s.close(); ss.close(); } }
编写代码过程中遇到的问题:现象:客户端和服务端都在莫名的等待;
原因:客户端和服务端都有阻塞式方法,这些方法没有读到结束标记就一直等待。解决方法:添加换行符,作为结束标记。