JAVA网络编程
学习资料:《java疯狂讲义》
网络已经像空气和水一样,成为了人们日常生活离不开的东西。网络也是开发中不可或缺的知识。我们学习理论很重要,将理论运用于实践更能加强对理论的理解,最后软件开发的生产力也将随之提高。
在学校里学的那本《计算机网络》,已经忘得差不多了。。。。
1.网络基础
网络模型:
ip(地址):唯一标识网络中的一个通信实体(主机、打印机、路由器端口等等)
TCP:Transmission Control Protocol,传输层协议。
端口(房间号):一个通信实体可以有多个通信程序同时提供网络服务,每一个通信服务占用一个端口。
URL(locators):代表互联网里的统一资源定位器。可以指向文件、目录、或是更为复杂的对象引用,如数据库或搜索引擎的查询。URL格式如下:
protocol://host:port/resourceName
URI(identifiers):Java的URI不能用于定位任何资源,它的唯一作用就是解析。
TCP/IP网络模型: TCP和IP协议互补,结合使用。原理是在网络两端分别建立socket通信端口,实现虚拟链路,并通过产生的IO通信。
2.java的基本网络支持
java.net包【可以对照API学习】:http://tool.oschina.net/apidocs/apidoc?api=jdk_7u4
InetAddress类代表ip地址(没有加入端口): 它并没有提供太多的方法,但是该类是网络通信的基础,经常被大量使用。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
public class InetAddressTest { public static void main(String[] args) throws IOException { InetAddress ip =InetAddress.getByName("www.crazyit.org"); System.out.println(ip.isReachable(2000)); System.out.println(ip.getHostName()); System.out.println(ip.getHostAddress()); InetAddress local=InetAddress.getLocalHost(); System.out.println(local); System.out.println(local.getHostAddress()); } }
URLEncoder和URLDecoder类:用于完成非西欧字符串(如中文)字符串和application/x-www-form-urlencoded MIME字符串之间的转换。
如:中国<----->%E4%B8%AD%E5%9B%BD (不同编码方式GBK,utf-8,转换结果不一样)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
public class URLDecoderTest { public static void main(String[] args) throws UnsupportedEncodingException { // TODO Auto-generated method stub String keyWord=URLDecoder.decode("%E4%B8%AD%E5%9B%BD","utf-8"); System.out.println(keyWord); String urlStr = URLEncoder.encode("中国", "GBK"); System.out.println(urlStr); }
输出结果:
中国
%D6%D0%B9%FA
URL、URLConnection和URLPermission类:
例1:实现一个简单的多线程下载工具类
从 http://www.crazyit.org/ 下载一个名为"ios.png”的图片。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
/*实现一个多线程下载工具类*/ public class DownUtil { private String path; private String targetFile; private int threadNum; private DownThread[] threads; private int fileSize; public DownUtil(String path, String targetFile, int threadNum) { this.path = path; this.threadNum = threadNum; threads = new DownThread[threadNum];//这里没有初始化的话会报错NULL POINTER this.targetFile = targetFile; } public void download() throws Exception { URL url = new URL(path); HttpURLConnection conn =(HttpURLConnection) url.openConnection(); conn.setConnectTimeout(5*1000); conn.setRequestMethod("GET"); conn.setRequestProperty("Accept","image/gif, image/jpeg, image/pjpeg, image/pjpeg, " +"application/x-shockwave-flash, application/xaml+xaml, " +"application/vnd.ms-xpsdocument, application/x-ms-xbap, " +"application/x-ms-application, application/vnd.ms-excel, " +"application/vnd.ms-powerpoint, application/msword, */*"); conn.setRequestProperty("Accept-Language", "zh-CN"); conn.setRequestProperty("Charset", "UTF-8"); conn.setRequestProperty("Connection", "Keep-Alive"); //得到文件大小 fileSize=conn.getContentLength(); conn.disconnect(); int currentPartSize=fileSize/threadNum+1; RandomAccessFile file=new RandomAccessFile(targetFile,"rw"); System.out.println(file.toString()); //设置本地文件大小 file.setLength(fileSize); file.close(); for(int i =0;i<threadNum;i++) { //计算每个线程下载的开始位置 int startPos=i*currentPartSize; //每个线程使用一个RandomAccessFile进行下载 RandomAccessFile currentPart=new RandomAccessFile(targetFile,"rw"); //定义该线程的下载位置 currentPart.seek(startPos); threads[i] = new DownThread(startPos,currentPartSize,currentPart); //启动下载线程 threads[i].start(); } } public double getCompleteRate() { int sumSize=0; for(int i=0;i<threadNum;i++) { sumSize+=threads[i].length; } return sumSize*1.0/fileSize; } private class DownThread extends Thread{ //当前线程的下载位置 private int startPos; //当前线程负责下载的文件大小 private int currentPartSize; //当前线程需要下载的文件块 //该线程已下载的字节数 public int length; private RandomAccessFile currentPart;//RandomAccessFile类 public DownThread(int startPos, int currentPartSize, RandomAccessFile currentPart) { super(); this.startPos = startPos; this.currentPartSize = currentPartSize; this.currentPart = currentPart; } public void run() { try { URL url = new URL(path); HttpURLConnection conn =(HttpURLConnection) url.openConnection(); conn.setConnectTimeout(5*1000); conn.setRequestMethod("GET"); conn.setRequestProperty("Accept","image/gif, image/jpeg, image/pjpeg, image/pjpeg, " +"application/x-shockwave-flash, application/xaml+xaml, " +"application/vnd.ms-xpsdocument, application/x-ms-xbap, " +"application/x-ms-application, application/vnd.ms-excel, " +"application/vnd.ms-powerpoint, application/msword, */*"); conn.setRequestProperty("Accept-Language", "zh-CN"); conn.setRequestProperty("Charset", "UTF-8"); InputStream inStream=conn.getInputStream(); //跳过startPos个字符,表示该线程只下载自己负责的那部分文件 inStream.skip(this.startPos); byte[] buffer = new byte[1024]; int hasRead=0; //读取网络数据,并写入本地文件 while(length<currentPartSize && (hasRead=inStream.read(buffer))!=-1) { currentPart.write(buffer, 0, hasRead); length+=hasRead; } currentPart.close(); inStream.close(); } catch(Exception e) { e.printStackTrace(); } } } }
测试下载工具类
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
/*测试下载工具类*/ public class MultiThreadDown { public static void main(String[] args) throws Exception { final DownUtil downUtil = new DownUtil("http://www.crazyit.org/" +"attachments/month_1403/1403202355ff6cc9a4fbf6f14a.png","ios.png",5); downUtil.download(); new Thread(() ->{ while(downUtil.getCompleteRate()<1) { System.out.println("已完成:"+downUtil.getCompleteRate()); try { Thread.sleep(1000); } catch(Exception ex){} } }).start(); } }
关于断点下载,加一个配置文件,该配置文件分别记录每个线程已经下载到哪个字节,当断开网络后再次开始下载时,每个线程根据配置文件里记录的位置向后下载即可。
例2:向Web站点发送GET请求、POST请求,并从Web站点取得响应。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
public class GetPostTest { public static void main(String[] args) { // TODO Auto-generated method stub String s =GetPostTest.sentGet("http://localhost:8080/index.jsp", null); System.out.println(s); String s1=GetPostTest.sendPost("http://localhost:8080/abc/login.jsp", "name=crazyit.org&pass=leegang"); System.out.println(s1); } /*向指定URL发送GET方式的请求 @param url @param param 请求参数name1=value2&name2=value2的形式 @return URL 远程资源的响应 */ public static String sentGet(String url,String param) { String result=""; String urlName=url+"?"+param; try { URL realUrl=new URL(urlName); URLConnection conn=realUrl.openConnection(); conn.setRequestProperty("accept", "*/*"); conn.setRequestProperty("connection", "Keep-Alive"); conn.setRequestProperty("user-agent", "Mozilla/4.0(compatible; MSIE 6Map<K, V> Windows NT 5.1; SV1)"); conn.connect(); Map<String, List<String>> map=conn.getHeaderFields(); for(String key:map.keySet()) { System.out.println(key+"-->"+map.get(key)); } try ( //定义bufferedReaders输入流来读取URL的响应 BufferedReader in = new BufferedReader( new InputStreamReader(conn.getInputStream(),"utf-8"))) { String line; while((line=in.readLine())!=null) { result+="\n"+line; } } } catch(Exception e) { System.out.println("发送GET请求出现异常! "+e); e.printStackTrace(); } return result; } /* 向指定URL发送POST方式的请求 @param url @param param @return URL */ public static String sendPost(String url, String param) { String result=""; try { URL realUrl = new URL(url); URLConnection conn= realUrl.openConnection(); conn.setRequestProperty("accept", "*/*"); conn.setRequestProperty("connection", "Keep-Alive"); conn.setRequestProperty("user-agent", "Mozilla/4.0(compatible; MSIE 6Map<K, V> Windows NT 5.1; SV1)"); //发送post请求必须设置如下两行 conn.setDoOutput(true); conn.setDoInput(true); try ( PrintWriter out = new PrintWriter(conn.getOutputStream())) { out.print(param); //flush输出流的缓冲 out.flush(); } try ( //定义bufferedReaders输入流来读取URL的响应 BufferedReader in = new BufferedReader( new InputStreamReader(conn.getInputStream(),"utf-8"))) { String line; while((line=in.readLine())!=null) { result+="\n"+line; } } } catch(Exception e) { System.out.println("发送GET请求出现异常! "+e); e.printStackTrace(); } return result; } }
暴力破解:不停变换用户名和密码提交给登录站点,直至返回成功。
Socket、ServerSocket类
例子:实现一个简单的socket通信
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
public class Server { public static void main(String[] args) throws IOException { ServerSocket ss = new ServerSocket(30000); while(true) { Socket s =ss.accept(); PrintStream ps= new PrintStream(s.getOutputStream()); ps.println("新年快乐!"); ps.close(); s.close(); } } }
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
public class Client { public static void main(String[] args) throws UnknownHostException, IOException { // TODO Auto-generated method stub Socket socket=new Socket("127.0.0.1",30000); BufferedReader br = new BufferedReader( new InputStreamReader(socket.getInputStream())); String line=br.readLine(); System.out.println(line); br.close(); socket.close(); } }
加入多线程:每个客户通过键盘键入一些内容后按回车键,即可在控制台上收到刚刚输入的内容,粗略地实现了一个C/S结构的聊天室的功能。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
public class MyServer { //定义保存所有socket的arraylist,并将其包装为线程安全的 public static List<Socket> socketList =Collections.synchronizedList(new ArrayList<>()); public static void main(String[] args) throws IOException { ServerSocket ss= new ServerSocket(30001); while(true) { //此代码会阻塞,一直等待别人的连接 Socket s =ss.accept(); socketList.add(s); //每当客户端连接后启动一个serverThread线程为该客户端服务 new Thread(new ServerThread(s)).start(); } } }
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
public class ServerThread implements Runnable{ Socket s =null; BufferedReader br = null; public ServerThread(Socket s) throws IOException { this.s=s; br = new BufferedReader(new InputStreamReader(s.getInputStream())); } public void run() { try { String content =null; while((content=readFromClient())!=null) { //遍历socket,将读到的内容向每个socket发一次 for(Socket s:MyServer.socketList) { PrintStream ps = new PrintStream(s.getOutputStream()); ps.println(content); } } } catch(IOException e) { e.printStackTrace(); } } private String readFromClient() { try { return br.readLine(); } catch(IOException e) { MyServer.socketList.remove(s); } return null; } }
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
public class MyClient { public static void main(String[] args) throws UnknownHostException, IOException { // TODO Auto-generated method stub Socket s = new Socket("127.0.0.1",30001); new Thread(new ClientThread(s)).start(); PrintStream ps = new PrintStream(s.getOutputStream()); String line=null; BufferedReader br = new BufferedReader( new InputStreamReader(System.in)); while((line=br.readLine())!=null) { ps.println(line); } } }
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
public class ClientThread implements Runnable{ private Socket s; BufferedReader br =null; public ClientThread(Socket s) throws IOException { this.s=s; br = new BufferedReader(new InputStreamReader(s.getInputStream())); } public void run() { try { String content=null; while((content=br.readLine())!=null) { System.out.println(content); } } catch(Exception e) { e.printStackTrace(); } } }
记录用户(客户端)信息,实现私聊功能
<!----未完待续----!>