[关闭]
@liayun 2016-07-01T11:07:41.000000Z 字数 13085 阅读 1549

Java 网络编程(一)

java基础


网络通讯要素

TCP/IP模型


以OSI 7层模型讲解数据的传输:

InetAddress

InetAddress类主要表示IP地址,互联网协议 (IP) 地址。
此类主要的方法如下:

UDP和TCP

UDP和TCP的区别

Socket

UDP程序设计

需求:通过UDP传输方式,将一段文字数据发送出去。即定义UDP的发送端。
解:
步骤:

  1. 建立UDP Socket服务(即建立端点)。
  2. 提供数据并将数据封装到数据报包中。
  3. 通过Socket服务的发送功能,将数据报包发出去。
  4. 关闭资源。

    • 建立UDP Socket服务,通过DatagramSocket对象。

      1. DatagramSocket ds = new DatagramSocket();
    • 确定数据,并封装成数据报包。

      1. byte[] buf = "udp ge men lai le".getBytes();
      2. DatagramPacket dp = new DatagramPacket(buf, buf.length, InetAddress.getByName("10.48.16.235"), 10000);
    • 通过socket服务将已有的数据报包发送出去,通过send()方法。

      1. ds.send(dp);
    • 关闭资源。

      1. ds.close();

需求:定义一个应用程序,用于接收UDP协议传输的数据并处理的。即定义UDP的接收端。
解:
步骤:

  1. 定义UDP Socket服务。通常会监听一个端口,其实就是给这个接收网络应用程序定义一个数字标识。方便于明确哪些数据过来该应用程序可以处理。
  2. 定义一个数据报包,因为要存储接收到的字节数据,因为数据包对象中有更多功能可以提取字节数据中的不同数据信息。
  3. 通过Socket服务的receive()方法将接收到的数据存入已定义好的数据报包中。
  4. 通过数据包对象的特有功能,将这些不同的数据取出,打印在控制台上。
  5. 关闭资源。

    • 创建UDP Socket服务,建立端点,监听一个端口。

      1. DatagramSocket ds = new DatagramSocket(10000); // 默认系统给分配数字标识
    • 定义数据报包,用于存储数据。

      1. byte[] buf = new byte[1024];
      2. DatagramPacket dp = new DatagramPacket(buf, buf.length);
    • 通过服务的receive()方法将收到的数据存入到数据报包中。

      1. ds.receive(dp); // 阻塞式方法。没有数据会一直等待
    • 通过数据报包中的方法获取其中的数据。

      1. String ip = dp.getAddress().getHostAddress();
      2. String data = new String(dp.getData(), 0, dp.getLength());
      3. int port = dp.getPort();
      4. System.out.println(ip+"::"+data+"::"+port);
    • 关闭资源。

      1. ds.close();

    因为服务器一天24小时都要对外提供服务,所以都不会关闭,所以以上代码亦可写到一个循环中。如:

    1. class UdpRece {
    2. public static void main(String[] args) throws Exception {
    3. // 1.创建udp socket服务,建立端点,监听一个端口
    4. DatagramSocket ds = new DatagramSocket(10000); // 默认系统给分配数字标识
    5. while(true) {
    6. // 2.定义数据包,用于存储数据
    7. byte[] buf = new byte[1024];
    8. DatagramPacket dp = new DatagramPacket(buf, buf.length);
    9. // 3.通过服务的receive()方法将收到的数据存入到数据包中
    10. ds.receive(dp); // 阻塞式方法。没有数据会一直等待
    11. // 4.通过数据包中的方法获取其中的数据。
    12. String ip = dp.getAddress().getHostAddress();
    13. String data = new String(dp.getData(), 0, dp.getLength());
    14. int port = dp.getPort();
    15. System.out.println(ip+"::"+data+"::"+port);
    16. }
    17. }
    18. }

    注意:DatagramSocket ds = new DatagramSocket(10000);不可在循环中,否则报异常——端口已经被使用
    图示UDP传输过程:

练习1:发送端从键盘不断录入,将数据发送给接收端;接收端接收到数据,并打印在控制台上。
解:

  1. import java.net.*;
  2. import java.io.*;
  3. class UdpSend {
  4. public static void main(String[] args) throws Exception {
  5. DatagramSocket ds = new DatagramSocket();
  6. BufferedReader bufr =
  7. new BufferedReader(new InputStreamReader(System.in));
  8. String line = null;
  9. while((line=bufr.readLine()) != null) {
  10. if("886".equals(line))
  11. break;
  12. byte[] buf = line.getBytes();
  13. DatagramPacket dp =
  14. new DatagramPacket(buf, buf.length, InetAddress.getByName("10.48.16.255"), 10001);
  15. ds.send(dp);
  16. }
  17. ds.close();
  18. }
  19. }
  20. class UdpRece {
  21. public static void main(String[] args) throws Exception {
  22. DatagramSocket ds = new DatagramSocket(10001);
  23. while(true) {
  24. byte[] buf = new byte[1024];
  25. DatagramPacket dp = new DatagramPacket(buf, buf.length);
  26. ds.receive(dp);
  27. String ip = dp.getAddress().getHostAddress();
  28. String data = new String(dp.getData(), 0, dp.getLength());
  29. System.out.println(ip+":"+data);
  30. }
  31. }
  32. }

练习2:编写一个聊天程序。
解:
分析:有收数据的部分,和发数据的部分,这两部分需要同时执行,那就需要用到多线程技术。一个线程控制收,一个线程控制发,因为收和发动作是不一致的,所以要定义两个run()方法,而且这两个方法要封装到不同的类中。

TCP程序设计

演示TCP传输

需求:给服务端发送给一个文本数据。即定义客户端。
解:
分析:通过查阅Socket对象,发现在该对象建立时,就可以去连接指定主机。因为TCP是面向连接的,所以在建立Socket服务时,就要有服务端存在,并连接成功,形成通路后,在该通道进行数据的传输。
步骤:

  1. 创建Socket服务,并指定要连接的主机和端口。
  2. 获取Socket流中的输出流,发送数据。
  3. 关闭资源。

    • 创建客户端的Socket服务,指定目的主机和端口。

      1. Socket s = new Socket("10.48.16.235", 10003);
    • 为了发送数据,应该获取socket流中的输出流。

      1. OutputStream out = s.getOutputStream();
      2. out.write("tcp ge men lai le".getBytes());
    • 关闭资源。

      1. s.close();

需求:定义端点接收数据并打印在控制台上。即定义服务端。
步骤:

  1. 建立服务端的Socket服务,通过ServerSocket()建立,并监听一个端口。
  2. 获取连接过来的客户端对象。通过ServerSocket的accept()方法。没有连接就会等,所以这个方法是阻塞式的。
  3. 客户端如果发过来数据,那么服务端要使用对应的客户端对象,并获取到该客户端对象的读取流来读取发过来的数据,并打印在控制台。
  4. 关闭服务端。(可选)

    • 建立服务端的Socket服务,并监听一个端口。

      1. ServerSocket ss = new ServerSocket(10003);
    • 通过accpet()方法获取连接过来的客户端对象。

      1. Socket s = ss.accept();
    • 获取客户端的ip地址。

      1. String ip = s.getInetAddress().getHostAddress();
      2. System.out.println(ip+"....connected");
    • 获取客户端发送过来的数据,那么要使用客户端对象的读取流来读取数据。

      1. InputStream in = s.getInputStream();
      2. byte[] buf = new byte[1024];
      3. int len = in.read(buf);
      4. System.out.println(new String(buf, 0, len));
    • 关闭客户端。

      1. s.close();
    • 关闭服务端。只对外服务一次,服务结束即关闭服务器,不过不现实。

      1. ss.close();

    图示TCP传输过程:

演示TCP传输的客户端和服务端的互访。

需求:客户端给服务端发送数据,服务端收到后,给客户反馈信息。
解:
定义客户端。
步骤:

  1. 建立Socket服务,指定要连接的主机和端口。
  2. 获取Scoket流中的输出流,将数据写到该流中,通过网络发送给服务端。
  3. 获取Socket流中的输入流,将服务端反馈的数据获取到,并打印。
  4. 关闭客户端资源。
  1. class TcpClient {
  2. public static void main(String[] args) throws Exception {
  3. Socket s = new Socket("10.48.16.235", 10004);
  4. OutputStream out = s.getOutputStream();
  5. out.write("服务端,你好".getBytes());
  6. InputStream in = s.getInputStream();
  7. byte[] buf = new byte[1024];
  8. int len = in.read(buf);
  9. System.out.println(new String(buf, 0, len));
  10. s.close();
  11. }
  12. }

定义服务端。

  1. class TcpServer {
  2. public static void main(String[] args) throws Exception {
  3. ServerSocket ss = new ServerSocket(10004);
  4. Socket s = ss.accept();
  5. String ip = s.getInetAddress().getHostAddress();
  6. System.out.println(ip+".....connected");
  7. InputStream in = s.getInputStream();
  8. byte[] buf = new byte[1024];
  9. int len = in.read(buf);
  10. System.out.println(new String(buf, 0, len));
  11. OutputStream out = s.getOutputStream();
  12. Thread.sleep(10000); // 延迟10s后反馈信息给客户端
  13. out.write("哥们收到,你也好".getBytes());
  14. s.close();
  15. ss.close();
  16. }
  17. }

练习1:建立一个文本转换服务器。客户端给服务端发送文本,服务端会将文本转换成大写再返回给客户端。而且客户端可以不断的进行文本转换,当客户端输入over时,转换结束。
解:
客户端:
既然是操作设备上的数据,那么就可以使用io技术,并按照io的操作规律来思考。
源:键盘录入。
目的:网络设备,网络输出流。
而且操作的是文本数据,可以选择字符流。都是文本数据,可以使用字符流进行操作,同时提高效率,加入缓冲。
步骤:

  1. 建立服务。
  2. 获取键盘录入。
  3. 将数据发送给服务端。
  4. 获取服务端返回的大写数据。
  5. 结束,关资源。

    • 创建客户端的Socket服务,指定目的主机和端口。

      1. Socket s = new Socket("10.48.16.235", 10005);
    • 定义读取键盘数据的流对象。

      1. BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));
    • 定义目的,将数据写入到Socket输出流,发给服务端。

      1. BufferedWriter bufOut = new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));
    • 定义一个Socket读取流,读取服务端返回的大写信息。

      1. BufferedReader bufIn = new BufferedReader(new InputStreamReader(s.getInputStream()));
      2. String line = null;
      3. while((line=bufr.readLine()) != null) {
      4. if("over".equals(line))
      5. break;
      6. bufOut.write(line);
      7. bufOut.newLine(); // 则必须要加换行符,以及刷新。不然要出大问题啊!!!
      8. bufOut.flush(); // 要刷新缓冲区
      9. String str = bufIn.readLine();
      10. System.out.println("server:"+str);
      11. }
    • 关闭资源。尤其注意s.close();,其意思就是在Socket流中加了一个-1,相当于给流中加入一个结束标记,即-1。服务端从Socket流中读到-1后,循环结束

      1. bufr.close();
      2. s.close();

服务端:
源:Socket读取流。
目的:Socket输出流。
都是文本,装饰。

该例子出现的问题:现象——客户端和服务端都在莫名的等待。为什么呢?
答:因为客户端和服务端都有阻塞式的方法(底层调用的都是read()),这些方法没有读到结束标记(换行符\r\n),那么就一直等,而导致两端都在等待。
还有另一种解决方案,代码如下:

练习2:将客户端的文本文件存到服务端中。
解:
此题最关键的点是:由于服务端一直在循环读取Socket的输出流, 所以客户端发完数据,应该给服务端一个标记,到哪儿结束,即自定义标记

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注