说明:文中的示例引用地址http://www.oschina.net/code/snippet_1461886_46846
一.Java网络编程简介
通过java网络通信的支持可以非常方便的访问HTTP,FTP等服务,而且可以直接取得互联网上的远程资源,向远程资源发生GET.POST请求.
Java提供了TCP网络通信支持,建立连接后就可以通过IO流进行通信.
Java也提供了UDP网络通信支持.可以实现接收和发送数据报.多点广播通信等.
二.基本网络工具
1.InetAddress
代表IP地址,有子类Inet4Address,Inet6Address,分别代表IPv4和IPv6的地址.
使用示例:
1: InetAddress idd;2: // idd=InetAddress.getByName("www.baidu.com");
3: idd=InetAddress.getByAddress(new byte[]{(byte)180,97,33,108});4: Object[] objs=new Object[]{
5: "是否可达:",idd.isReachable(10000),
6: "全限定域名:",idd.getCanonicalHostName(),
7: "IP字符串:",idd.getHostAddress(),
8: "主机名:",idd.getHostName(),
9: "本地IP:",InetAddress.getLocalHost()
10: };11: print(objs);
2.URLDecoder和URLEncoder
URLDecoder和URLEncoder完成普通字符串和application/x-www-from-urlencoded MIME字符串之间的互相转换.
使用示例:
1: public static void de_en(String str)throws UnsupportedEncodingException{2: String keyWord;3: keyWord=URLEncoder.encode(str, "utf-8");
4: System.out.println(str+"<=utf-8=>"+keyWord);
5:6: keyWord=URLEncoder.encode(str, "gbk");
7: System.out.println(str+"<=gbk=>"+keyWord);
8:9: keyWord=URLEncoder.encode(str, "unicode");
10: System.out.println(str+"<=unicode=>"+keyWord);
11: }
3. URL,URLConnection和URLPermission(jdk8)
URL(Uniform Resource Locator)对象代表统一资源定位器,它是指向互联网"资源"的指针.资源可以是简单的文件或目录,也可以是复杂对象的引用(数据库或搜索引擎查询).
URL由协议名,主机,端口和资源组成:
protocol://host:port/resourceName
URLConnection,标识应用程序和URL之间的通信连接;
URLPermission,用于管理HttpURLConnection的权限问题
演示示例,向WEB站点发送GET,POST请求,并获取响应内容:
1:2: public class WEBRequestDemo {3: /**
4: * 通用请求头信息5: */6: private static Map<String,String> requestInfo=new HashMap<String,String>();7: static{
8: requestInfo.put("accept","*/*");9: requestInfo.put("Connection","Keep-Alive");10: requestInfo.put("Content-Type","text/html;charset=utf-8");11: requestInfo.put("Accept-Language","zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3");12: requestInfo.put("User-Agent","Mozilla/5.0 (Windows NT 6.1; WOW64; rv:36.0) Gecko/20100101 Firefox/36.0");13: }14: public static void main(String[] args) {15: WEBRequestDemo wrd=new WEBRequestDemo();
16: wrd.sendGet("http://www.w3cschool.cc/search/index.php", "q=哈");17: wrd.sendPost("http://www.w3cschool.cc/search/index.php", "q=哈");18: }19: /**
20: * 使用GET方法获取远程资源21: * @param url22: * @param param 键值对形式的字符串"a=b&c=d"23: */24: public void sendGet(String urlString,String param){25: String urlAddress = urlString;26: try {
27: urlAddress = urlString+"?"+URLEncoder.encode(param,"utf-8");28: } catch (UnsupportedEncodingException e1) {
29: e1.printStackTrace();30: }31: try {
32: //打开连接
33: URL url=new URL(urlAddress);
34: URLConnection conn=url.openConnection();35: //设置请求头
36: setRequestHeader(conn,requestInfo);37: //连接
38: conn.connect();39:40: System.out.println("响应头信息:");
41: Map<String, List<String>> responseInfo=conn.getHeaderFields();42: for(String key : responseInfo.keySet()){
43: System.out.println(key+"="+Arrays.toString(responseInfo.get(key).toArray()));
44: }45: //读取内容到文件
46: read("get_out.txt",conn.getInputStream());
47:48: } catch (MalformedURLException e) {
49: e.printStackTrace();50: } catch (IOException e) {
51: e.printStackTrace();52: }53: }54: /**
55: * 以POST方式获取远程资源56: * @param urlString57: * @param param58: */59: public void sendPost(String urlString,String param){60: try {
61: //打开连接
62: URL url=new URL(urlString);
63: URLConnection conn=url.openConnection();64: //设置请求头
65: setRequestHeader(conn,requestInfo);66: //发送POST请求必须设置下面两行
67: conn.setDoInput(true);
68: conn.setDoOutput(true);
69: try(
70: PrintWriter out=new PrintWriter(conn.getOutputStream());
71: ){72: out.print(URLEncoder.encode(param, "utf-8"));
73: out.flush();74: }75:76: System.out.println("响应头信息:");
77: Map<String, List<String>> responseInfo=conn.getHeaderFields();78: for(String key : responseInfo.keySet()){
79: System.out.println(key+"="+Arrays.toString(responseInfo.get(key).toArray()));
80: }81: //读取内容到文件
82: read("post_out.txt",conn.getInputStream());
83:84: } catch (MalformedURLException e) {
85: e.printStackTrace();86: } catch (IOException e) {
87: e.printStackTrace();88: }89: }90: /**
91: * 设置请求头信息92: * @param conn93: * @param requestInfo94: */95: private void setRequestHeader(URLConnection conn,Map<String, String> requestInfo) {96: for(String key : requestInfo.keySet()){
97: conn.setRequestProperty(key, requestInfo.get(key));98: }99: }100: public void read(String fileName,InputStream in){101: try(
102: BufferedReader bufIn=new BufferedReader(new InputStreamReader(in,"utf-8"));103: PrintStream out=new PrintStream(fileName);
104: ){105: String line;106: while((line=bufIn.readLine())!=null){107: out.println(line);108: }109: }catch(IOException e){
110: e.printStackTrace();111: }112: }113: }
四.基于TCP协议的Socket网络编程
1.TCP通信
java封装了基于TCP/IP协议的网络通信,使用Scoket对象来代表两段的通信端口,并通过Socket产生IO流来进行网络通信.
用法:
创建服务端口 ServerSocket serverSocket = new ServerSocket(port);
监听客户端连接 Socket s = serverSocket.accept();
-----------可以为每一个客户端启动一个线程来一对一服务.
客户端连接服务器 Socket socket = new Socket(host, port);
综合示例:多人聊天室 :工程文件的Client.java和Server.java
2.半关闭的Socket
在不关闭输入Socket的情况下关闭输入输出流,称为半关闭的Socket.
Socket提供了两个半关闭的方法:
shutdownInput() : 只关闭输入流.
shutdownOutput() : 只关闭输出流.
3.使用 NIO 实现非阻塞通信
从1.4开始java提供了NIO API来支持开发高性能的网络服务器.
NIO非阻塞服务器使用Selector来监听通信需求.通过SelectionKey保存要通信的管道.
通过将连接端口注册到Selector来监听通信,当有通信产生时,将管道与Selector连接所代表的SelectionKey加入
集合中,通过调用Selector的selectedKeys()方法获取.
一般建立流程:
1: //实例一个Selector
2: Selector selector=Selector.open();3:4: //通过open()方法打开一个未绑定的ServerScoketChannel实例
5: ServerSocketChannel serverChannel = ServerSocketChannel.open();6: InetSocketAddress ass=new InetSocketAddress(host,port);
7: //绑定IP地址
8: serverChannel.bind(ass);9: //设置为非阻塞模式,并注册到Selector
10: serverChannel.configureBlocking(false);
11: serverChannel.register(selector, SelectionKey.OP_ACCEPT);12:13: //实例化一个Socket管道
14: SocketChannel socketChannel =SocketChannel.open(ass);15: //设置操作与模式
16: socketChannel.configureBlocking(false);
17: socketChannel.register(selector, SelectionKey.OP_READ);18:19: //监控通信需求
20: while(selector.select()>0){
21: //遍历所有要通信的管道
22: for(SelectionKey sk : selector.selectedKeys()){
23: //处理
24: }25: }
完整示例程序: 工程文件的NIOServer.java和NIOClient.java
4.使用Java7的AIO实现非阻塞通信
简单的例子:
1:2: /**
3: * 实现一个简单的AIO服务器例子4: *5: */6: public class SimpleAIOServer {7: public static void main(String[] args) throws Exception {8: new SimpleAIOServer().init();
9: }10: public static int PORT = 9090;11: private Charset charset = Charset.forName("utf-8");12:13: public void init() throws Exception {14: AsynchronousServerSocketChannel serverChannel = AsynchronousServerSocketChannel15: .open().bind(new InetSocketAddress(PORT));
16: while(true){17: AsynchronousSocketChannel socketChannel=serverChannel.accept().get();18: socketChannel.write(charset.encode("欢迎进入")).get();
19: }20: }21: }22:23: /**
24: *AIO简单实现客户端25: *26: */27: public class SimpleAIOClient {28:29: public static void main(String[] args) throws Exception {30: new SimpleAIOClient().init();
31: }32:33: private Charset charset=Charset.forName("utf-8");34: private static int PORT =9090;35: public void init() throws Exception{36: ByteBuffer buffer=ByteBuffer.allocate(1024);37: AsynchronousSocketChannel socketChannel=AsynchronousSocketChannel.open();38: socketChannel.connect(new InetSocketAddress("127.0.0.1",PORT)).get();39: socketChannel.read(buffer).get();40: buffer.flip();41: System.out.println("服务器信息:"+charset.decode(buffer));
42: }43: }
上面的例子非常简单,为了充分展现AIO的优势,应该使用线程池管理IOChannel.
1: //创建线程池
2: ExecutorService executor =Executors.newFixedThreadPool(20);3: //异步ChannelGroup
4: AsynchronousChannelGroup channelGroup=AsynchronousChannelGroup.withThreadPool(executor);5: //通过ChannelGroup打开Server
6: AsynchronousServerSocketChannel server=AsynchronousServerSocketChannel.open(channelGroup).bind(new InetSocketAddress(PORT));
综合示例,使用AIO实现多人聊天室:工程文件AIOServer.java和AIOClient.java
五.UDP协议编程基础
1.在什么情况下使用UDP协议?
UDP协议是无连接的,可靠性低的传输层协议,
UDP和TCP的区别:
UDP不必建立传输链路,不知道接收方是谁,也不必管数据被收到没有.就只是把数据发出去就行了.
TCP是一种可靠的,需要建立连接的协议.又要负责连接,又要负责保证数据正确传达.
因为UDP面向无连接,所以通信效率高,适用于传输少量类容,实时性强,可靠性要求不高的应用环境,例如网络游戏,视频等.
2.Java中如何基于UDP协议编程?
发送用法:请将数据,IP地址,端口给我,我给你打包后发送出去,其他的你不用管.
接收用法:监听一个端口,如果有人传给我一个数据包,你可以从我这取走.
说明:我是谁?我就是DatagramSocket,数据包是什么?DatagramPacket是也.
简单示例:
1:2: /**
3: *4: *基于UDP协议的数据传输5: */6: public class UDPServer {7:8: public static void main(String[] args) throws SocketException {9: new UDPServer().init();
10: }11:12: /**
13: * 一个自发自收的程序14: */15: private DatagramSocket outSocket;
16: private int outNum=0;17: private int inNum=0;18: private DatagramSocket inSocket;
19: private int PORT=9090;20: public void init() throws SocketException{21: outSocket=new DatagramSocket();
22: inSocket=new DatagramSocket(PORT);
23: new Thread(){
24: public void run(){25: while(true){26: try {
27: send();28: Thread.sleep(100);29: } catch (Exception e) {
30: e.printStackTrace();31: }32: }33: }34: }.start();35: new Thread(){
36: public void run(){37: while(true){38: try {
39: receive();40: // Thread.sleep(2000);
41: } catch (Exception e) {
42: e.printStackTrace();43: }44: }45: }46: }.start();47: long start=System.currentTimeMillis();
48: new Thread(){
49: public void run(){50: while(System.currentTimeMillis()-start<1000*60){
51: }52: System.out.println("接收数据"+inNum+",发送数据 "+outNum+",比率="+(inNum*1.0/outNum*100.0)+"%");53: System.exit(0);54: }55: }.start();56:57: }58: public void send() throws IOException{59: byte[] outb=new byte[1024];60: String content=String.valueOf(new Random().nextInt(10000));
61: outb=content.getBytes("utf-8");
62: DatagramPacket outPacket = new DatagramPacket(outb,outb.length,InetAddress.getLocalHost(),PORT);
63: outSocket.send(outPacket);64: outNum++;65: System.out.println("发送数据"+content);
66:67: }68: public void receive() throws IOException{69: DatagramPacket inPacket = new DatagramPacket(new byte[1024],1024);70: inSocket.receive(inPacket);71: byte[] inb=inPacket.getData();
72: String content=new String(inb,"utf-8");73: inNum++;74: System.out.println("收到内容:"+content);
75:76: }77:78: }
3.使用MulticastSocket多点广播
DatagramSocket只能将数据传输到指定地址,而MulticastSocket可以将数据包以广播形式发送到对个地址.
当MulticastSocket用于发送数据时,需要指定发送的多点广播地址.
当MulticastSocket用于接收数据时,需要为其指定端口.
示例,多人聊天室MulticastSocketDemo.java
六.使用代理服务器
1.为什么要使用代理服务器?
一般浏览器访问资源时,直接向目的服务器请求资源.如果使用代理服务器,那么浏览器就向代理服务器发送信息,再由代理服务器向资源所在服务器请求.也就是代理浏览器请求资源.
代理服务器可以缓存请求的资源,当浏览器请求的资源在代理服务器中存在且是最新的,那么代理服务器就直接返回资源给浏览器,加快了资源请求速度.而且使用代理服务器可以隐藏自身IP,突破IP限制.
2.如何使用代理?
URLConnection和Socket的构造方法中都可以传入Proxy.剩下的操作是一样的.
构造一个Proxy需要代理服务器地址和服务器类型两个参数.
1: Proxy proxy=new Proxy(Proxy.Type.HTTP,new InetSocketAddress(ProxyAddress,ProxyPort));2: URLConnection conn=url.openConnection(proxy);3: Socket socket=new Socket(proxy);
4:
3.自动选择代理服务器
如果有多个代理服务器,使用ProxySelector可以为程序自动构建代理服务器,免去了重复构建的繁琐.
为了使用自动代理,你需要实现ProxySelector类的两个方法:
List<Proxy> select(URI uri) : 返回代理服务器列表
connectionFailed(URI uri,SocketAddress sa,IOException) : 连接代理服务器失败时调用.
在实现了ProxySelector类之后,调用ProxySelector.setDefault(ProxySelector ps)注册代理选择器.