第十七章.网络编程
Posted lanshanxiao
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了第十七章.网络编程相关的知识,希望对你有一定的参考价值。
Java的基本网络支持:
使用InetAddress:
1 import java.net.InetAddress; 2 3 public class InetAddressTest{ 4 public static void main(String[] args) throws Exception{ 5 //根据主机名来获取对应的InetAddress实例 6 InetAddress ip = InetAddress.getByName("www.lanshanxiao.cc"); 7 //判断是否可达 8 System.out.println("lanshanxiao是否可达:" + ip.isReachable(2000)); 9 //获取该InetAddress实例的IP字符串 10 System.out.println(ip.getHostAddress()); 11 //根据原始IP地址来获取对应的InetAddress实例 12 InetAddress local = InetAddress.getByAddress(new byte[] {127,0,0,1}); 13 System.out.println("本机是否可达:" + local.isReachable(5000)); 14 //获取该InetAddress实例对应的权限定域名 15 System.out.println(local.getCanonicalHostName()); 16 } 17 }
注意上面程序中:InetAddress local = InetAddress.getByAddress(new byte[] {127,0,0,1});大括号中的127,0,0,1之间的符号是逗号
使用URLDecoder和URLEncoder:
URLDecoder和URLEncoder用于完成普通字符串和application/x-www-form-urlencoded MIME字符串之间的相互转换。
1 import java.net.URLEncoder; 2 import java.net.URLDecoder; 3 4 public class URLDecoderTest{ 5 public static void main(String[] args) throws Exception{ 6 //将application/x-www-form-urlencoded字符串 7 //转换成普通字符串 8 String keyWord = URLDecoder.decode("%E7%96%AF%E7%8B%82java", "utf-8"); 9 System.out.println(keyWord); 10 //将普通字符串转换成 11 //application/x-www-form-urlencoded字符串 12 String urlStr = URLEncoder.encode("疯狂android讲义", "GBK"); 13 System.out.println(urlStr); 14 } 15 }
URL、URLConnection、URLPermission:
URL(Uniform Resource Locator)对象代表统一资源定位器,它是指向互联网“资源”的指针。资源可以是简单的文件或目录,也可以是复杂的对象的引用,如:对数据库
或搜索引擎的查询。
URL可以由协议名、主机、端口、资源组成:
protocol://host:port/resourceName
如下:
http://www.crazyit.org/index.php
多线程下载工具类:
1 import java.io.RandomAccessFile; 2 import java.io.InputStream; 3 import java.net.URL; 4 import java.net.HttpURLConnection; 5 6 public class DownUtil{ 7 //定义下载资源的路径 8 private String path; 9 //指定所下载的文件的保存位置 10 private String targetFile; 11 //定义需要使用多少个线程下载资源 12 private int threadNum; 13 //定义下载的线程对象 14 private DownThread[] threads; 15 //定义下载的文件的总大小 16 private int fileSize; 17 18 public DownUtil(String path, String targetFile, int threadNum){ 19 this.path = path; 20 this.threadNum = threadNum; 21 //初始化threads数组 22 threads = new DownThread[threadNum]; 23 this.targetFile = targetFile; 24 } 25 26 public void download() throws Exception{ 27 URL url = new URL(path); 28 HttpURLConnection conn = (HttpURLConnection) url.openConnection(); 29 conn.setConnectTimeout(5 * 1000); 30 conn.setRequestMethod("GET"); 31 conn.setRequestProperty( 32 "Accept", 33 "image/gif, image/jpeg, image/pjpeg, image/pjpeg, " 34 + "application/x-shockwave-flash, application/xaml+xml, " 35 + "application/vnd.ms-xpsdocument, application/x-ms-xbap, " 36 + "application/x-ms-application, application/vnd.ms-excel, " 37 + "application/vnd.ms-powerpoint, application/msword, */*"); 38 conn.setRequestProperty("Accept-Language", "zh-CN"); 39 conn.setRequestProperty("Charset", "UTF-8"); 40 conn.setRequestProperty("Connection", "Keep-Alive"); 41 //得到文件大小 42 fileSize = conn.getContentLength(); 43 conn.disconnect(); 44 int currentPartSize = fileSize / threadNum + 1; 45 RandomAccessFile file = new RandomAccessFile(targetFile, "rw"); 46 //设置本地文件大小 47 file.setLength(fileSize); 48 file.close(); 49 for (int i = 0; i < threadNum; i++) 50 { 51 // 计算每条线程的下载的开始位置 52 int startPos = i * currentPartSize; 53 // 每个线程使用一个RandomAccessFile进行下载 54 RandomAccessFile currentPart = new RandomAccessFile(targetFile, 55 "rw"); 56 // 定位该线程的下载位置 57 currentPart.seek(startPos); 58 // 创建下载线程 59 threads[i] = new DownThread(startPos, currentPartSize, 60 currentPart); 61 // 启动下载线程 62 threads[i].start(); 63 } 64 } 65 66 // 获取下载的完成百分比 67 public double getCompleteRate() 68 { 69 // 统计多条线程已经下载的总大小 70 int sumSize = 0; 71 for (int i = 0; i < threadNum; i++) 72 { 73 sumSize += threads[i].length; 74 } 75 // 返回已经完成的百分比 76 return sumSize * 1.0 / fileSize; 77 } 78 79 private class DownThread extends Thread 80 { 81 // 当前线程的下载位置 82 private int startPos; 83 // 定义当前线程负责下载的文件大小 84 private int currentPartSize; 85 // 当前线程需要下载的文件块 86 private RandomAccessFile currentPart; 87 // 定义已经该线程已下载的字节数 88 public int length; 89 90 public DownThread(int startPos, int currentPartSize, 91 RandomAccessFile currentPart) 92 { 93 this.startPos = startPos; 94 this.currentPartSize = currentPartSize; 95 this.currentPart = currentPart; 96 } 97 98 @Override 99 public void run() 100 { 101 try 102 { 103 URL url = new URL(path); 104 HttpURLConnection conn = (HttpURLConnection)url 105 .openConnection(); 106 conn.setConnectTimeout(5 * 1000); 107 conn.setRequestMethod("GET"); 108 conn.setRequestProperty( 109 "Accept", 110 "image/gif, image/jpeg, image/pjpeg, image/pjpeg, " 111 + "application/x-shockwave-flash, application/xaml+xml, " 112 + "application/vnd.ms-xpsdocument, application/x-ms-xbap, " 113 + "application/x-ms-application, application/vnd.ms-excel, " 114 + "application/vnd.ms-powerpoint, application/msword, */*"); 115 conn.setRequestProperty("Accept-Language", "zh-CN"); 116 conn.setRequestProperty("Charset", "UTF-8"); 117 InputStream inStream = conn.getInputStream(); 118 // 跳过startPos个字节,表明该线程只下载自己负责哪部分文件。 119 inStream.skip(this.startPos); 120 byte[] buffer = new byte[1024]; 121 int hasRead = 0; 122 // 读取网络数据,并写入本地文件 123 while (length < currentPartSize 124 && (hasRead = inStream.read(buffer)) != -1) 125 { 126 currentPart.write(buffer, 0, hasRead); 127 // 累计该线程下载的总大小 128 length += hasRead; 129 } 130 currentPart.close(); 131 inStream.close(); 132 } 133 catch (Exception e) 134 { 135 e.printStackTrace(); 136 } 137 } 138 } 139 }
程序中DownUtil类中的download()方法负责按如下步骤实现多线程下载:
1.创建URL对象
2.获取指定URL对象所指向资源的大小(通过getContentLength()方法获得),此处用到了URLConnection类,该类代表Java应用程序和URL之间的通信链接。
3.在本地磁盘上创建一个与网络资源具有相同大小的空文件。
4.计算每个线程应该下载网络资源的哪个部分(从哪个字节开始,到哪个字节结束)
5.依次创建、启动多个线程来下载网络资源的指定部分
上面程序已经实现了多线程下载的核心代码,若要实现断点下载,则需要额外增加一个配置文件(读者可以发现,所有的断点下载工具都会在下载开始时生成两个文件:一个是与网络资源具有相同大小的空文件,一个是配置文件),该配置文件分别记录每个线程已经下载到哪个字节,当网络断开后再次下载时,每个线程根据配置文件里记录的位置向后下载即可。
1 public class MultiThreadDown 2 { 3 public static void main(String[] args) throws Exception 4 { 5 // 初始化DownUtil对象 6 final DownUtil downUtil = new DownUtil("http://www.crazyit.org/" 7 + "attachments/month_1403/1403202355ff6cc9a4fbf6f14a.png" 8 , "ios.png", 4); 9 // 开始下载 10 downUtil.download(); 11 new Thread(() -> { 12 while(downUtil.getCompleteRate() < 1) 13 { 14 // 每隔0.1秒查询一次任务的完成进度, 15 // GUI程序中可根据该进度来绘制进度条 16 System.out.println("已完成:" 17 + downUtil.getCompleteRate()); 18 try 19 { 20 Thread.sleep(1000); 21 } 22 catch (Exception ex){} 23 } 24 }).start(); 25 } 26 }
通常创建一个和URL的连接,并发送请求、读取此URL引用的资源需要如下几个步骤:
1.通过调用URL对象的openConnection()方法来创建URLConnection对象
2.设置URLConnection的参数和普通请求属性
3.若只是发送GET方式请求,则使用connect()方法建立和远程资源之间的实际连接即可;若要发送POST方式的请求,则需要获取URLConnection实例对应的输出流来发
送请求参数
4.远程资源变为可用,程序可以访问远程资源的头字段或通过输入流读取远程资源的数据
若既要使用输入流读取URLConnection响应的内容又要使用输出流发送请求参数,则一定要先使用输出流,在使用输入流。
下面程序示范了如何向Web站点发送GET请求、POST请求,并从Web站点取得响应:
1 import java.net.URL; 2 import java.net.URLConnection; 3 import java.util.Map; 4 import java.util.List; 5 import java.io.BufferedReader; 6 import java.io.InputStreamReader; 7 import java.io.PrintWriter; 8 9 public class GetPostTest 10 { 11 /** 12 * 向指定URL发送GET方法的请求 13 * @param url 发送请求的URL 14 * @param param 请求参数,格式满足name1=value1&name2=value2的形式。 15 * @return URL所代表远程资源的响应 16 */ 17 public static String sendGet(String url , String param) 18 { 19 String result = ""; 20 String urlName = url + "?" + param; 21 try 22 { 23 URL realUrl = new URL(urlName); 24 // 打开和URL之间的连接 25 URLConnection conn = realUrl.openConnection(); 26 // 设置通用的请求属性 27 conn.setRequestProperty("accept", "*/*"); 28 conn.setRequestProperty("connection", "Keep-Alive"); 29 conn.setRequestProperty("user-agent" 30 , "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1)"); 31 // 建立实际的连接 32 conn.connect(); 33 // 获取所有响应头字段 34 Map<String, List<String>> map = conn.getHeaderFields(); 35 // 遍历所有的响应头字段 36 for (String key : map.keySet()) 37 { 38 System.out.println(key + "--->" + map.get(key)); 39 } 40 try( 41 // 定义BufferedReader输入流来读取URL的响应 42 BufferedReader in = new BufferedReader( 43 new InputStreamReader(conn.getInputStream() , "utf-8"))) 44 { 45 String line; 46 while ((line = in.readLine())!= null) 47 { 48 result += "\\n" + line; 49 } 50 } 51 } 52 catch(Exception e) 53 { 54 System.out.println("发送GET请求出现异常!" + e); 55 e.printStackTrace(); 56 } 57 return result; 58 } 59 /** 60 * 向指定URL发送POST方法的请求 61 * @param url 发送请求的URL 62 * @param param 请求参数,格式应该满足name1=value1&name2=value2的形式。 63 * @return URL所代表远程资源的响应 64 */ 65 public static String sendPost(String url , String param) 66 { 67 String result = ""; 68 try 69 { 70 URL realUrl = new URL(url); 71 // 打开和URL之间的连接 72 URLConnection conn = realUrl.openConnection(); 73 // 设置通用的请求属性 74 conn.setRequestProperty("accept", "*/*"); 75 conn.setRequestProperty("connection", "Keep-Alive"); 76 conn.setRequestProperty("user-agent", 77 "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1)"); 78 // 发送POST请求必须设置如下两行 79 conn.setDoOutput(true); 80 conn.setDoInput(true); 81 try( 82 // 获取URLConnection对象对应的输出流 83 PrintWriter out = new PrintWriter(conn.getOutputStream())) 84 { 85 // 发送请求参数 86 out.print(param); 87 // flush输出流的缓冲 88 out.flush(); 89 } 90 try( 91 // 定义BufferedReader输入流来读取URL的响应 92 BufferedReader in = new BufferedReader(new InputStreamReader 93 (conn.getInputStream() , "utf-8"))) 94 { 95 String line; 96 while ((line = in.readLine())!= null) 97 { 98 result += "\\n" + line; 99 } 100 } 101 } 102 catch(Exception e) 103 { 104 System.out.println("发送POST请求出现异常!" + e); 105 e.printStackTrace(); 106 } 107 return result; 108 } 109 // 提供主方法,测试发送GET请求和POST请求 110 public static void main(String args[]) 111 { 112 // 发送GET请求 113 String s = GetPostTest.sendGet("http://localhost:8888/abc/a.jsp" 114 , null); 115 System.out.println(s); 116 // 发送POST请求 117 String s1 = GetPostTest.sendPost("http://localhost:8888/abc/login.jsp" 118 , "name=crazyit.org&pass=leegang"); 119 System.out.println(s1); 120 } 121 }
这一部分需要创建Web应用,现在我还不会。但是会在接下来学习《轻量级Java EE企业应用实战》的时候创建Web应用。Web应用的代码我上传到GitHub网站(会在文章最后写出),是一个abc的文件夹。
基于TCP协议的网络编程:
使用ServerSocket创建TCP服务器端:
Java中能接收其他通信实体连接请求的类是ServerSocket,ServerSocket对象用于监听来自客户端的Socket连接,若没有连接,它将一直处于等待状态。ServerSocket包含
一个监听来自客户端连接请求的方法。
1.Socket accept():若接收到一个客户端Socket的连接请求,该方法返回一个与客户端Socket对应的Socket;否则该方法一直处于等待状态,线程也被阻塞
使用Socket进行通信:
1 import java.net.ServerSocket; 2 import java.net.Socket; 3 import java.io.PrintStream; 4 import java.io.IOException; 5 6 public class Server 7 { 8 public static void main(String[] args) 9 throws IOException 10 { 11 // 创建一个ServerSocket,用于监听客户端Socket的连接请求 12 ServerSocket ss = new ServerSocket(30000); 13 // 采用循环不断接受来自客户端的请求 14 while (true) 15 { 16 // 每当接受到客户端Socket的请求,服务器端也对应产生一个Socket 17 Socket s = ss.accept(); 18 // 将Socket对应的输出流包装成PrintStream 19 PrintStream ps = new PrintStream(s.getOutputStream()); 20 // 进行普通IO操作 21 ps.println("您好,您收到了服务器的新年祝福!"); 22 // 关闭输出流,关闭Socket 23 ps.close(); 24 s.close(); 25 } 26 } 27 }
1 import java.net.Socket; 2 import java.io.BufferedReader; 3 import java.io.InputStreamReader; 4 import java.io.IOException; 5 6 public class Client 7 { 8 public static void main(String[] args) 9 throws IOException 10 { 11 Socket socket = new Socket("127.0.0.1" , 30000); // ① 12 // 将Socket以上是关于第十七章.网络编程的主要内容,如果未能解决你的问题,请参考以下文章