网络通讯要素
图示:
TDP/IP模型
以OSI 7层模型讲解数据的传输
InetAddress
InetAddress类主要表示IP地址。
示例代码:
import java.net.InetAddress; import java.net.UnknownHostException; public class IPDemo { public static void main(String[] args) throws UnknownHostException { /* //获得本地IP地址 InetAddress i = InetAddress.getLocalHost(); System.out.println(i.toString()); System.out.println("address:"+i.getHostAddress()); System.out.println("name:"+i.getHostName()); */ /* * 获取任意主机地址 */ InetAddress ia = InetAddress.getByName("www.baidu.com");//以主机名称为主需要解析 System.out.println("address:"+ia.getHostAddress());//百度不止一个主机 System.out.println("name:"+ia.getHostName()); } }
UDP程序设计
需求:通过UDP传输方式,将一段文字数据发送出去。
定义udp的发送端。
思路:
导包(如果用Eclipse,就不用麻烦了啦!!!):
import java.io.IOException; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetAddress;
代码:
class UdpSend { public static void main(String[] args) throws IOException { //1、建立UDPSocket服务,通过DatagramSocket对象 DatagramSocket ds = new DatagramSocket(); //2、确定数据,并封装成数据报包 byte[] buf = "udp ge men lai le ".getBytes(); DatagramPacket dp = new DatagramPacket(buf, buf.length, InetAddress.getByName("10.48.62.209"), 10000); //3、通过socket服务将已有的数据报包发送出去,通过send()方法 ds.send(dp); //4、关闭资源 ds.close(); } }
需求:定义一个应用程序,用于接收udp协议传输的数据并处理的。
定义udp的接收端。
思路:
代码:
class UdpRece { public static void main(String[] args) throws IOException { //1、创建udp socket,建立端点。 DatagramSocket ds = new DatagramSocket(10000);//系统给分配数字标识 while(true) { /* * 第一次运行是可以输出的,但是循环第二次时,发现两个程序用的是同一个端口号, * 所以会出现java.net.BindException:Address already in use port bind * 所以不要这样写。 */ //DatagramSocket ds = new DatagramSocket(10000); //2、定义数据包,用于存储数据 byte[] buf = new byte[1024]; DatagramPacket dp = new DatagramPacket(buf, buf.length); //3、通过服务的receive()方法将收到的数据存入到数据包中 ds.receive(dp);//阻塞式方法。没有数据会一直等待 //4、通过数据包中的方法获取其中的数据。 String ip = dp.getAddress().getHostAddress(); String data = new String(dp.getData(), 0, dp.getLength()); int port = dp.getPort(); System.out.println(ip+"::"+data+"::"+port); } //5、关闭资源 //ds.close(); } }
练习1:从键盘不断录入,并打印在控制台上。
代码:
import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetAddress; public class UdpSend2 { public static void main(String[] args) throws IOException { DatagramSocket ds = new DatagramSocket(); BufferedReader bufr = new BufferedReader( new InputStreamReader(System.in)); String line = null; while((line = bufr.readLine()) != null) { if("886".equals(line)) break; byte[] buf = line.getBytes(); DatagramPacket dp = new DatagramPacket(buf, buf.length, InetAddress.getByName("10.48.62.209"), 10001); ds.send(dp); } ds.close(); } } class UdpRece2 { public static void main(String[] args) throws IOException { DatagramSocket ds = new DatagramSocket(10001); 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, buf.length); System.out.println(ip+"::"+data); } } }
练习2:编写一个聊天程序。
分析:有收数据的部分,和发数据的部分。这两部分需要同时执行。那就需要用到多线程技术。一个线程控制收,一个线程控制发。因为收和发动作是不一致的,所以要定义两个run()方法,而且这两个方法要封装到不同的类中。
代码:
import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetAddress; import java.net.SocketException; class Send implements Runnable { private DatagramSocket ds; Send(DatagramSocket ds) { this.ds = ds; } @Override public void run() { try { BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in)); String line = null; while((line = bufr.readLine()) != null) { if("886".equals(line)) break; byte[] buf = line.getBytes(); DatagramPacket dp = new DatagramPacket(buf, buf.length, InetAddress.getByName("10.48.62.209"), 10002); ds.send(dp); } } catch (IOException e) { throw new RuntimeException("发送端失败"); } } } class Rece implements Runnable { private DatagramSocket ds; Rece(DatagramSocket ds) { this.ds = ds; } @Override 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 (IOException e) { throw new RuntimeException("接收端失败"); } } } public class ChatDemo { public static void main(String[] args) throws SocketException { DatagramSocket sendSocket = new DatagramSocket(); DatagramSocket receSocket = new DatagramSocket(10002); new Thread(new Send(sendSocket)).start(); new Thread(new Rece(receSocket)).start(); } }
图示:
TCP程序设计
演示tcp传输。
需求:给服务端发送给一个文本数据。
分析:定义客户端。通过查阅socket对象,发现在该对象建立时,就可以去连接指定主机。因为tcp是面向连接的,所以在建立socket服务时,就要有服务端存在,并连接成功,形成通路后,在该通道进行数据的传输。
思路:
导包:
import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.ServerSocket; import java.net.Socket;
代码:
class TcpClient { public static void main(String[] args) throws IOException { //1、创建客户端的socket服务,指定目的主机和端口。 Socket s = new Socket("10.48.62.209", 10003); //为了发送数据,应该获取socket流中的输出流。 OutputStream out = s.getOutputStream(); out.write("tcp gemen lai le ".getBytes()); s.close(); } }
需求:定义端点接收数据并打印在控制台上。
定义服务端。
思路:
代码:
class TcpServer { public static void main(String[] args) throws IOException { //建立服务端的socket服务,并监听一个端口 ServerSocket ss = new ServerSocket(10003); //通过accpet()方法获取连接过来的客户端对象 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的传输的客户端和服务端的互访。
需求:客户端给服务端发送数据,服务端收到后,给客户反馈信息。
代码:
客户端:
class TcpClient2 { public static void main(String[] args) throws IOException { Socket s = new Socket("10.48.62.209", 10004); OutputStream out = s.getOutputStream(); out.write("服务端,你好".getBytes()); InputStream in = s.getInputStream(); byte[] buf = new byte[1024]; int len = in.read(buf); System.out.println(new String(buf, 0, len)); s.close(); } }
服务端
class TcpServer2 { public static void main(String[] args) throws IOException, InterruptedException { ServerSocket ss = new ServerSocket(10004); 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)); OutputStream out = s.getOutputStream(); Thread.sleep(10000); out.write("哥们收到,你也好".getBytes()); s.close(); ss.close(); } }
图示客户端/服务端通讯原理:
练习1,建立一个文本转换服务器。客户端给服务端发送文本,服务端会将文本转换成大写再返回给客户端。而且客户端可以不断的进行文本转换,当客户端输入over时,转换结束。
分析:
客户端:
既然是操作设备上的数据,那么就可以使用io技术,并按照io的操作规律来思考。
源:键盘录入。
目的:网络设备,网络输出流。
而且操作的是文本数据,可以选择字符流。
都是文本数据,可以使用字符流进行操作,同时提高效率,加入缓冲。
步骤:
class TransClient { public static void main(String[] args) throws IOException { Socket s = new Socket("10.48.62.209", 10005); /* * 定义读取键盘数据的流对象 */ BufferedReader bufr = new BufferedReader( new InputStreamReader(System.in)); /* * 定义目的,将数据写入到socket输出流,发给服务端 */ //BufferedWriter bufOut = new BufferedWriter( //new OutputStreamWriter(s.getOutputStream())); //简写 PrintWriter out = new PrintWriter(s.getOutputStream(), true);//不需要写刷新代码out.flush();自动刷新。 /* * 定义一个socket读取流,读取服务端返回的大写信息。 */ BufferedReader bufIn = new BufferedReader( new InputStreamReader(s.getInputStream())); String line = null; while((line = bufr.readLine()) != null) { if("over".equals(line)) break; out.println(line); /* * 如若像这样定义: * BufferedWriter bufOut = new BufferedWriter(new OutputStreamWriter(s.getOutputStream())); * 则必须要加换行符,以及刷新。不然要出大问题啊!!! */ //bufOut.write(line); //bufOut.newLine();// //bufOut.flush();//要刷新缓冲区 String str = bufIn.readLine(); System.out.println("server:"+str); } bufr.close(); s.close();//socket流中加了一个-1,相当于给流中加入一个结束标记,即-1 } }
服务端:
源:socket读取流。
目的:socket输出流。
都是文本,装饰。
class TransServer { public static void main(String[] args) throws IOException { 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())); //简写: PrintWriter out = new PrintWriter(s.getOutputStream(), true);//不需要写刷新代码out.flush();自动刷新。 String line = null; while((line = bufIn.readLine()) != null) { System.out.println(line);//服务端没收到。(加入测试语句,判断有没执行到此处) out.println(line); /* * 如若像这样定义: * BufferedWriter bufOut = new BufferedWriter(new OutputStreamWriter(s.getOutputStream())); * 则必须要加换行符,以及刷新。不然要出大问题啊!!! */ //bufOut.write(line.toUpperCase()); //bufOut.newLine(); //bufOut.flush(); } s.close(); ss.close(); } }
该例子出现的问题:
现象:客户端和服务端都在莫名的等待。
为什么呢?
因为客户端和服务端都有阻塞式的方法(底层调用的都是read()),这些方法没有读到结束标记(换行符\r\n),那么就一直等,而导致两端都在等待。
练习2:将客户端的文本文件存到服务端中。
代码:
import java.io.BufferedReader; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintWriter; import java.net.ServerSocket; import java.net.Socket; class TextClient { public static void main(String[] args) throws IOException { Socket s = new Socket("10.48.62.209", 10006); BufferedReader bufr = new BufferedReader(new FileReader("IPDemo.java")); PrintWriter out = new PrintWriter(s.getOutputStream(), true); /* * 第二种方式: * 发数据之前,定义一个时间戳 */ //DataOutputStream dos = new DataOutputStream(s.getOutputStream()); //long time = System.currentTimeMillis(); //out.println(time); //dos.writeLong(time); String line = null; while((line = bufr.readLine()) != null) { out.println(line); } /* * 由于在服务端一直在循环读取socket的输出流, * 所以客户端发完数据,应该给服务端一个标记,到哪儿结束 * 即自定义标记 * * 第一种方式: */ //out.println("over"); //dos.writeLong(time); //第三种方式 s.shutdownOutput();//关闭客户端的输出流,相当于给流中加入一个结束标记,即-1 BufferedReader bufIn = new BufferedReader( new InputStreamReader(s.getInputStream())); String str = bufIn.readLine(); System.out.println(str); bufr.close(); s.close(); } } class TextServer { public static void main(String[] args) throws IOException { ServerSocket ss = new ServerSocket(10006); Socket s = ss.accept(); String ip = s.getInetAddress().getHostAddress(); System.out.println(ip+"..........connected"); /* * 服务端接收到结束标记 */ //DataInputStream dis = new DataInputStream(s.getInputStream()); //long time = dis.readLong(); BufferedReader bufIn = new BufferedReader( new InputStreamReader(s.getInputStream())); PrintWriter out = new PrintWriter(new FileWriter("server.txt"), true); String line = null; while((line = bufIn.readLine()) != null) { /* * 服务端接收到标记并判断 */ //if("over".equals(line)) //break; out.println(line); } PrintWriter pw = new PrintWriter(s.getOutputStream(), true); pw.println("上传成功"); out.close(); s.close(); ss.close(); } }