class="p0">Java?网络编程
网络基本知识:
在Java中,网络程序有两种协议:TCP和UDP。TCP通过握手协议进行可靠的连接,UDP则是不可靠的连接。
IP地址:用于标记一台计算机的“身份证”。
IP地址由网络地址(确定网络)和主机地址(网络中的主机)组成。
子网掩码:为了区分网络地址和主机地址。
IP地址分为A类、B类、C类(常用)、D类和E类地址。
127.0.0.1?(localhost)是本机地址。
IPv4?和?IPv6
IPv4使用32位二进制表示,可以表示为4个十进制数。
SMTP是简单邮件传输协议,端口号是25.
Telnet用于连接远程计算机或互联网计算机提供的服务。每个服务都会设定一个端口。
给出类似?telnet?ip?port?即可与特定的服务进行通信
如果要连接互联网的服务,不仅要给出端口,还要给出计算机的名称,只有给出IP地址和端口号时,才能够请求服务,并接收到应答。
URL和URI
URI:统一资源标识符,用于标识一个web资源,包含了两个部分。
(1)URL:统一资源定位符。能够精确的定位数据的URI
(2)URN:统一资源名称。除了URL的URI。
在Java中URI和URL是分开的两个类,URI类专门用于解析,URL用于通信。
URL
1.?URI分类
绝对和相对:
(1)绝对URI是指有确定的协议。比如http,ftp。后面以/进行分隔。
(2)相对URI是没有scheme的。
透明和不透明:
(1)不透明URI是不能够被解析的URI。不透明URI是绝对URI。Scheme后面的部分不是以/进行分割。
分层和不分层:
(1)分层是绝对透明URI或相对URI。
默认的网页端口是80.
2.?URI的作用:
(1)?解析
[scheme:]scheme-specific-part[#fragment]
Scheme表示用的协议,可以是http\https\ftp\file等。
Scheme-specific-part是其余部分。
进一步细分:
[scheme:][//authority][path][?query][#fragment]
常用方法:
getScheme()获得scheme
getSchemeSpecificPart()
getPath()
getAuthority()
(2)?相对标识符和绝对标识符的转换
函数resolve和relative
示例代码:
任务1:取得特定网址的html代码。
任务2:分析地址信息。
任务3:绝对地址和相对地址转换
//示例代码1
package com.chenjo.net; import java.net.URI; import java.net.URL; import java.util.Scanner; public class URLTest1 { public static void main(String[] args) throws Exception { URL url = new URL("http://www.ecnu.edu.cn"); Scanner in = new Scanner(url.openStream()); while (in.hasNextLine()) { String str = in.nextLine(); System.out.println(str); } URI uri = new URI("http://kanglecrj.iteye.com"); System.out.println(uri.getScheme()); System.out.println(uri.getSchemeSpecificPart()); System.out.println(uri.getAuthority()); System.out.println(uri.getUserInfo()); System.out.println(uri.getHost()); System.out.println(uri.getPort()); System.out.println(uri.getPath()); System.out.println(uri.getQuery()); System.out.println(uri.getFragment()); String str = "/article/details/6705033"; URI combined = uri.resolve(str);// 根据uri的路径把str变成绝对地址 System.out.println(combined.getScheme() + combined.getSchemeSpecificPart()); URI relative = uri.relativize(new URI(str)); System.out.println(relative.getSchemeSpecificPart()); } } /* ...... http //blog.csdn.net/xiazdong blog.csdn.net null blog.csdn.net -1 /xiazdong null null http//blog.csdn.net/article/details/6705033 /article/details/6705033 */
?
URL和URLConnection
URL的作用
1.?如果想要获取每个网页的html源码,比如http://kanglecjr.iteye.com?则只需要:
(1)URL?url?=?new?URL("http://kanglecjr.iteye.com");
(2)Scanner?in?=?new?Scanner(url.openStream());
即可。
2.?获取消息头信息
URLConnection?conn?=?url.openConnection
conn.getHeaderFields();?返回一个Map<String,?List<String>>
conn.getContentLength();
conn.getContentType();
conn.getDoOutput(true);?获得输出流
conn.getOutputStream();
conn.getInputStream();
//代码示例二
package com.chenjo.net; import java.net.*; //import sun.misc.*; import java.util.*; //import java.io.*; public class URLConnectionTest { public static void main(String[] args) throws Exception { String urlName = "http://java.sun.com"; URL url = new URL(urlName); URLConnection connection = url.openConnection(); Map<String, List<String>> map = connection.getHeaderFields(); for (Map.Entry<String, List<String>> entry : map.entrySet()) { String key = entry.getKey(); List<String> value = entry.getValue(); System.out.println(key + ":" + value); } } } /* null:[HTTP/1.1 200 OK] X-Frame-Options:[SAMEORIGIN] Content-Language:[en] Access-Control-Allow-Origin:[*] Date:[Mon, 21 Jul 2014 11:42:53 GMT] Content-Length:[47758] X-Akamai-Transformed:[9 - 0 pmb=mRUM,1] Content-Type:[text/html; charset=utf-8] Connection:[keep-alive] X-Powered-By:[Servlet/2.5 JSP/2.1] Server:[Oracle-Application-Server-11g Oracle-Web-Cache-11g/11.1.1.6.0 (TH;max-age=300+0;age=65;ecid=11957576370342462,0:1)] */
?
在网页中如果要提交数据给web服务器,通常要把数据发送给web服务器,然后web服务器委派一个脚本对数据进行处理,返回一个应答。
通常发送数据的方法有两种:get和post。
(1)get方法是直接把数据跟在url的后面,以name=value进行传输,每个数据之间用&进行分隔,value中的空格用+替换,非字母数字用%替换,并猴哥两个16进制数,这种编码方式称为URL编码。URLEncoder和URLDecoder
(2)post方法是通过URLConnection发送给服务器,编码方式和get一样。URLEncoder.encode(VALUE,?"UTF-8);?一般在传输中文时会运用编码和解码。
示例:通过URLEncoder和URLDecoder编码和解码
(略)
InetAddress?根据域名得到IP地址或名称
没有构造方法,通过:
(1)InetAddress?i1?=?InetAddress.getByName(String)?返回一个InetAddress实例。
(2)如果一个主机有多个ip地址,比如google,有5个ip地址,就调用
InetAddress[]?i2?=?InetAddress.getAllByName(String);??
InetAddress.getLocalhost()?获得本机的InetAddress实例。
//代码实例
package com.chenjo.net; import java.net.InetAddress; public class InetAddressTest { public static void main(String[] args) throws Exception{ InetAddress local = InetAddress.getLocalHost(); System.out.println("本机地址:"+local.getHostAddress()); System.out.println("本机名称:"+local.getHostName()); InetAddress[] remote = InetAddress.getAllByName("www.google.com"); for(InetAddress a : remote) { System.out.println("地址:"+a.getHostAddress()); System.out.println("名称:"+a.getHostName()); } } } /* 本机地址:172.16.4.18 本机名称:netbar-8f-08 地址:173.194.127.112 名称:www.google.com 地址:173.194.127.113 名称:www.google.com 地址:173.194.127.116 名称:www.google.com 地址:173.194.127.114 名称:www.google.com 地址:173.194.127.115 名称:www.google.com */
?
Socket(TCP)
Socket是一个用于机器之间通信的类。
Socket客户端:
(1)Socket?s?=?new?Socket(ip,?port);?打开一个?套接字,发送请求
(2)InputStream?istream?=?s.getInputStream();?接收数据
(3)OutputStream?ostream?=?s.getOutputStream();?发送数据
需要用PrintWriter和Scanner进行包装,并且注意PrintWriter的自动缓冲。
Socket服务器:注意多个客户端同时访问服务器的问题:多线程
(1)ServerSocket?server?=?new?ServerSocket(port);?创建一个端口
(2)Socket?s?=?server.accept();?只有当有客户端请求并连接,函数才会返回
(3)InputStream?istream?=?s.getInputStream();?接收数据
(4)OutputStream?ostream?=?s.getOutputStream();?发送数据
需要用PrintWriter?和?Scanner?进行包装,并且注意PrintWriter的自动缓冲。
我们在使用PrintWriter时需要使用println()函数;
当服务器或客户端任意一方请求结束通信,则立刻停止。
问题1:在套接字中会发生阻塞的地方:
(1)实例化Socket时,会阻塞。
(2)在in.nextLine()类似操作时会阻塞。
解决方法:
(1)对于第一个问题,解决方法:
Socket?s?=?new?Socket();?建立无连接socket
s.connect(new?InetSocketAddress(host,?pot),?timeout);?设置超时。
(2)对于第二个问题,解决方法是设置s.setSoTimeout(long)?设置超时时长
问题2:当客户端想要关闭套接字时,但却不能确定服务器是否还在发送数据,但是只要一关闭就立刻断开。
解决方法:
socket.shutdownOutput()?关闭输出流
socket.shutdownInput()?关闭输入流
为半关闭的示例代码:客户端发送hello给服务器,同时关闭输出流,服务器接收到后关闭输入流,等待5秒发送ECHO?hello给客户端。
package com.chenjo.net; import java.net.*; import java.io.*; import java.util.*; public class ShutdownOutputClient { public static void main(String[] args)throws Exception { Socket s = new Socket("localhost",8819); Scanner in = new Scanner(s.getInputStream()); PrintWriter out = new PrintWriter(s.getOutputStream(),true); out.println("Hello");//输出hello s.shutdownOutput(); //关闭输出流 System.out.println("关闭连接"); while(in.hasNextLine()){ System.out.println(in.nextLine()); } s.close(); } }
?
package com.chenjo.net; import java.net.*; import java.io.*; import java.util.*; public class ShutdownOutputServer { public static void main(String[] args)throws Exception { ServerSocket server = new ServerSocket(8819); Socket s = server.accept(); Scanner in = new Scanner(s.getInputStream()); PrintWriter out = new PrintWriter(s.getOutputStream(),true); String str = in.nextLine(); System.out.println(str); s.shutdownInput(); System.out.println("关闭输入流"); Thread.sleep(5000); out.println("Echo:"+str); s.close(); } }
?
?
综合代码举例:实现一个简单的对等通信程序,通过多线程,一个线程接收数据,一个线程发送数据。
//Client.java
package com.chenjo.net; import java.util.*; import java.io.*; import java.net.*; public class Client{ public static void main(String[]args)throws Exception{ Socket s = new Socket("localhost",8819); PrintWriter out = new PrintWriter(s.getOutputStream(),true); Thread t = new Thread(new Receive(s)); t.start(); //以下代码用于发送数据 Scanner in = new Scanner(System.in);//键盘输入 while(in.hasNextLine()){ //一直不断 out.println(in.nextLine()); //发送键盘输入数据 } } } class Receive implements Runnable //这个类用于接收数据 { private Socket s; public Receive(Socket s) { this.s = s; } public void run() { try{ Scanner in = new Scanner(s.getInputStream()); //in:接收数据 String str = null; while(true) { str = in.nextLine(); System.out.println("服务器说:"+str); //打印接收数据 } } catch(Exception e){} } } /* C: Hello 服务器说:S: Hi! C: Are you the boss? 服务器说:S: Yes, I am the boss. C: OK. See you tomorrow, my boss! 服务器说:S: See you! */
?
//Server.java
package com.chenjo.net; import java.util.*; import java.io.*; import java.net.*; public class Server{ public static void main(String[]args)throws Exception{ ServerSocket server = new ServerSocket(8819); Socket s = server.accept(); PrintWriter out = new PrintWriter(s.getOutputStream(),true); Thread t = new Thread(new Receive1(s)); t.start(); //以下代码用于发送数据 Scanner in = new Scanner(System.in);//键盘输入 while(in.hasNextLine()){ //一直不断 out.println(in.nextLine()); //发送键盘输入数据 } } } class Receive1 implements Runnable //这个类用于接收数据 { private Socket s; public Receive1(Socket s) { this.s = s; } public void run() { try{ Scanner in = new Scanner(s.getInputStream()); //in:接收数据 String str = null; while(true) { str = in.nextLine(); System.out.println("客户端说:"+str); //打印接收数据 } } catch(Exception e){} } } /* 客户端说:C: Hello S: Hi! 客户端说:C: Are you the boss? S: Yes, I am the boss. 客户端说:C: OK. See you tomorrow, my boss! S: See you! */
?
以上程序属于C/S,需要同时维护客户端和服务器的代码。
B/S:?浏览器和服务器,只需要维护一方代码即可。
?
聊天工具使用UDP非常多,因为我们通常也会遇到我们发给对方一条消息,对方却没有收到的情况。
DatagramPacket?和?DatagramSocket?数据报
代码举例:实现服务器发送数据报到客户端
package com.chenjo.net; import java.net.*; import java.io.*; public class DatagramClient { public static void main(String[] args) throws Exception{ byte[]buf = new byte[1024]; DatagramPacket packet = new DatagramPacket(buf,1024); DatagramSocket client = new DatagramSocket(9000); client.receive(packet); String str = new String(buf,0,packet.getLength()); System.out.println(packet.getAddress().getHostName()+":"+str); client.close(); } } /** Result: 172.16.4.18:hello world */
?
package com.chenjo.net; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetAddress; public class DatagramServer { public static void main(String[] args)throws Exception { DatagramSocket server = new DatagramSocket(3000); String str = "hello world"; DatagramPacket packet = new DatagramPacket(str.getBytes(),str.length(),InetAddress.getLocalHost(),9000); server.send(packet); server.close(); } }
?
?
PrintWriter?的autoflush
如果?PrintWriter?writer?=??new?PrintWriter(out,?true);
则调用println()、printf()、format()函数时会自动刷新,其他函数都不会,比如write()、print()函数时不会自动刷新。
?
网络编程常见异常
?
第1个异常是java.net.BindException:?Address?already?in?use:?JVM_Bind。该异常发生在服务器端进行new?ServerSocket(port)?(port?是?一个?0-65536的整型值)操作时。异常的原因是因为该port端口已经被占用被监听。此时用netstat?-an?命令,可以看到一个Listening?状态的端口。只需要找一个没有被占用的端口就能解决这个问题。
?
第2个异常是java.net.ConnectionException:?Connection?refused:?connect。该异常发生在客户端进行new?Socket(ip,?port)?操作时。该异常发生的原因是或者具有ip地址的机器无法找到(也就是说当前机器不存在到指定ip的路由),或者该ip存在,但找不到指定的被监听端口。出行该问题,首先检查客户端的ip和port是否写错了,如果正确则从客户端ping一下服务器看能否ping通(服务器端把ping禁掉则需要另外的办法),则看在服务器端的指定监听端口的程序是有已启动,肯定能解决这个问题。
?
第3个异常时java.net.SocketException:?Socket?is?closed,该异常在客户端和服务器均可能发生。异常的原因是己方主动关闭了连接后(调用了Socket的close方法)再对网络连接进行读写操作。
?
第4个异常是java.net.SocketException:?(Connection?reset?或者?Connect?reset?by?peer:?Socket?write?error)。该异常在客户端和服务器端均可能发生,引起该异常的原因有两个,第一个就是如果一端的Socket被关闭(或主动关闭或因为异常退出而引起的关闭),另一端仍发送数据,发送的第一个数据包引发该异常(Connection?reset?by?peer)。另一个是一端退出,但退出时并未关闭该连接,另一端如果再从连接中读数据则被抛出该异常(Connection?reset)。简单地说就是在连接断开后的读和写操作引起的。
?
第5个异常是java.net.SocketException:?Broken?pipe。该异常在客户端和服务器端均有可能发生。在第4个异常的第一种情况中(也就是抛出SocketException:?Connect?reset?by?peer:?Socket?write?error后),如果再继续写数据则抛出该异常。前两个异常的解决方法是首先确保程序退出前关闭所有的网络连接,其次是要检测对方的关闭连接操作,发现对方关闭连接后自己也要关闭该连接。
?
类QQ客户端:
package com.chenjo.net; import java.awt.BorderLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.BufferedReader; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.PrintStream; import java.io.PrintWriter; import java.net.Socket; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTextArea; import javax.swing.JTextField; public class Client3 extends JFrame{ static JTextArea area; JTextField field; JButton button; static PrintWriter writer; public Client3(){ this.setTitle("客户端"); this.setSize(400,500); area = new JTextArea(25,30); area.setEditable(false); field = new JTextField(20); button = new JButton("提交"); JScrollPane sp = new JScrollPane(area); JPanel panel = new JPanel(); this.add(sp,BorderLayout.CENTER); panel.add(field); panel.add(button); this.add(panel,BorderLayout.SOUTH); this.setVisible(true); this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); button.addActionListener(new ActionListener(){ @Override public void actionPerformed(ActionEvent e) { String text = field.getText(); writer.println(text); area.append("我:"+text+"\n"); field.setText(""); } }); } public static void main(String[] args) throws Exception{ Client3 c = new Client3(); Socket socket = new Socket("127.0.0.1",8899); OutputStream out = socket.getOutputStream(); BufferedReader reader1 = new BufferedReader(new InputStreamReader(socket.getInputStream())); writer = new PrintWriter(out,true); System.out.println("已经成功和服务器连接..."); while(true){ String line = reader1.readLine(); area.append("服务器:"+line+"\n"); } } }
?
类QQ服务器:
package com.chenjo.net; import java.awt.BorderLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.BufferedReader; import java.io.InputStreamReader; import java.io.PrintStream; import java.net.InetAddress; import java.net.ServerSocket; import java.net.Socket; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTextArea; import javax.swing.JTextField; public class Server3 extends JFrame{ static JTextArea area; JTextField field; JButton button; static PrintStream writer; public Server3(){ this.setTitle("服务器"); this.setSize(400,500); area = new JTextArea(25,30); area.setEditable(false); field = new JTextField(20); button = new JButton("提交"); JPanel panel = new JPanel(); JScrollPane sp = new JScrollPane(area); this.add(sp,BorderLayout.CENTER); panel.add(field); panel.add(button); this.add(panel,BorderLayout.SOUTH); this.setVisible(true); this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); button.addActionListener(new ActionListener(){ @Override public void actionPerformed(ActionEvent e) { String text = field.getText(); writer.println(text); area.append("我:"+text+"\n"); field.setText(""); } }); } public static void main(String[] args) throws Exception { Server3 s = new Server3(); ServerSocket server = new ServerSocket(8899); System.out.println("开始监听..."); Socket socket = server.accept(); InetAddress address = socket.getInetAddress(); String name = address.getLocalHost().getHostName(); System.out.println(name+"已连接"); BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream())); writer = new PrintStream(socket.getOutputStream(), true); while (true) { String line = null; line = reader.readLine(); if (line != null) { area.append("客户端:"+line+"\n"); } } } }
?
原文出处:
http://blog.csdn.net/xiazdong/article/details/6713691
?
?
<!--EndFragment-->