• java网络编程


    网络基本知识:

    在java中网络程序有两种协议:TCP和UDP,TCP通过握手协议进行可靠的连接,UDP则是不可靠连接。

    IP地址:用于标记一台计算机的身份证。

    IP地址由网络地址(确定网络)和主机地址(网络中的主机)组成。

    子网掩码:为了区分网络地址和主机地址。

    IP地址分为A类地址、B类地址、C类地址(经常使用)、D类地址、E类地址。

    127.0.0.1(localhost)是本机地址。

    IPV4和IPV6

    IPV4使用4个十进制数表示,即32位二进制。

    SMTP是简单邮件传输协议,port号是25.


    telnet用于连接远程计算机或者因特网计算机提供的服务。每一个服务都会设定一个port。

    给出类似     telnet ip    port    就可以和特定的服务进行通信

    假设要连接因特网的服务,不仅要给出port,还要给出计算机的名称,仅仅有给出IP地址和port号时,才可以请求服务,并接收到应答。


    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。

    全部的网页port都是80.

    2.URI的作用:

    (1)解析

    URI的格式:

    [scheme:]scheme-specific-part[#fragment]

    scheme表示用的协议,能够是httphttpsftpfile等。

    scheme-specific-part是其余部分。

    进一步细分:

    [scheme:][//authority][path][?query][#fragment]

    经常用法:

    • getScheme()获得scheme;
    • getSchemeSpecificPart()
    • getPath()
    • getAuthority()

    (2)相对标识符和绝对标识符的转换

    resolve和relative函数。

    演示样例代码:

    任务1:取得特定网址的html代码。

    任务2:分析地址信息。

    任务3:绝对地址和相对地址转换

    package org.core;
    
    import java.net.*;
    import java.util.*;
    
    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://blog.csdn.net/xiazdong");
    		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());
    
    	}
    
    }
    


    URL和URLConnection

    URL的作用

    1.假设想要获取某个网页的html源码,比方http://blog.csdn.net/xiazdong 则仅仅须要:

    (1)URL url = new URL("http://blog.csdn.net/xiazdong");

    (2)Scanner in = new Scanner(url.openStream());

    就可以.

    2.获取消息头信息

    • URLConnection connection = url.openConnection();
    • connection.getHeaderFields()返回一个Map<String,List<String>>
    • connection.getContentLength();
    • connection.getContentType();
    • connection.setDoOutput(true)获得输出流
    • connection.getOutputStream();
    • connection.getInputStream();

    代码演示样例:

    package org.core;
    
    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);
    		}	
    
    	}
    
    }
    



    在网页中假设要提交数据给webserver,通常要把数据发送给webserver,然后webserver委派一个脚本对数据进行处理,返回一个对应。

    通常发送数据的方法有两种:get和post。

    (1)get方法是直接把数据跟在url的后面,以name=value进行传输,

    每一个数据之间用&进行切割,value中的空格用+替换,非字母数字用%替换,并后跟两个16进制数,这样的编码方式称为URL编码。URLEncoder和URLDecoder

    (2)post方法是通过URLConnection发送给server,编码方式和get一样。URLEncoder.encode(VALUE,"UTF-8");

    一般在传输中文时会运用编码和解码。

    演示样例:通过URLEncoder和URLDecoder编码和解码


    InetAddress  依据域名得到IP地址或名称

    没有构造方法,通过:

    (1)InetAddress i1 = InetAddress.getByName(String)返回一个InetAddress实例。

    (2)假设一个地址有多个ip地址,比方google,有3个ip地址,就调用InetAddress[] i2 = InetAddress.getAllByName(String);

    InetAddress.getLocalhost()获得本机的InetAddress实例。

    代码实例:

    package org.core;
    
    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());
    		}	
    	}
    }
    


    Socket(TCP)

    Socket是一个用于机器之间通信的类。

    Socketclient:

    (1)Socket s = new Socket(ip,port);打开一个套接字,发送请求    

    (2)InputStream istream = s.getInputStream();接收数据

    (3)OutputStream ostream  = s.getOutputStream();发送数据

    须要用PrintWriter和Scanner进行包装,而且注意PrintWriter的自己主动缓冲。

    Socketserver:注意多个client同一时候訪问server的问题:多线程

    (1)ServerSocket server = new ServerSocket(port);创建一个端口

    (2)Socket s = server.accept();        仅仅有当有client请求并连接,函数才会返回

    (3)InputStream istream = s.getInputStream();接收数据

    (4)OutputStream ostream  = s.getOutputStream();发送数据

    须要用PrintWriter和Scanner进行包装,而且注意PrintWriter的自己主动缓冲。

    我们在使用PrintWriter时须要使用println()函数;


    当server或client随意一方请求结束通信,则立马停止。

    问题1:在套接字中会发生堵塞的地方:

    (1)实例化Socket时,会堵塞。

    (2)在in.nextLine()类似操作时会堵塞。

    解决方法:

    (1)对于第一个问题,解决方法:

    • Socket s = new Socket();建立无连接socket
    • s.connect(new InetSocketAddress(host,port),timeout);设置超时。

    (2)对于第二个问题,解决方法是设置s.setSoTimeout(long)设置超时时间

    问题2:当client想要关闭套接字时,但却不能确定server是否还在发送数据,可是仅仅要一关闭就立马断开。

    解决方法:

    socket.shutdownOutput()关闭输出流

    socket.shutdownInput()关闭输入流

    半关闭演示样例代码:client发送hello给server,同一时候关闭输出流,server接收到后关闭输入流,等待5秒发送ECHO hello给client。

     Client:

    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();
    	}
    
    }
    

    Server:

    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();
    	}
    
    }
    


    综合代码举例:实现一个简单的对等通信程序,通过多线程,一个线程接收数据,一个线程发送数据。

    用户1:

    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("server说:"+str);	//打印接收数据
    			}
    			
    		}
    		catch(Exception e){}
    	}
    }

    用户2:

    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("client说:"+str);	//打印接收数据
    			}
    			
    		}
    		catch(Exception e){}
    	}
    }


    以上的程序属于C/S,须要同一时候维护client和server的代码。

    B/S:浏览器和server,仅仅须要维护一方代码就可以。


    聊天工具使用UDP许多,由于我们通常也会遇到我们发给还有一个人一条消息,还有一个人却没有收到的情况。

    DatagramPacket和DatagramSocket  数据报

    代码举例:实现server发送数据报到client。

    Client:

    package org.core;
    
    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();
    	}
    
    }
    

    Server:

    package org.core;
    
    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();
    	}
    }
    

    QQ聊天应用

    Server端

    package org.xiazdong.server;
    
    
    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+"
    ");
    				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("client:"+line+"
    ");
    			}
    
    		}
    	}
    
    }
    

    Client端

    package org.xiazdong.client;
    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+"
    ");
    				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("已经成功和server连接...");
    		while(true){
    			String line = reader1.readLine();
    			area.append("server:"+line+"
    ");
    		}
    	}
    
    }


    PrintWriter的autoflush

    假设PrintWriter writer = new PrintWriter(out,true);

    则调用println()、printf()、format()函数时会自己主动刷新,其它函数都不会,比方write()、print()函数时不会自己主动刷新

     网络编程常见异常


    第1个异常是java.net.BindException:Address already in use: JVM_Bind。该异常发生在server端进行new ServerSocket(port)(port是一个0,65536的整型值)操作时。异常的原因是以为与port一样的一个端口已经被启动,并进行监听。此时用netstat –an命令,能够看到一个Listending状态的port。仅仅须要找一个没有被占用的port就能解决问题。 

    第2个异常是java.net.ConnectException: Connection refused: connect。该异常发生在client进行new Socket(ip, port)操作时,该异常发生的原因是或者具有ip地址的机器不能找到(也就是说从当前机器不存在到指定ip路由),或者是该ip存在,但找不到指定的端口进行监听。出现该问题,首先检查client的ip和port是否写错了,假设正确则从clientping一下server看能否ping通,假设能ping通(服务server端把ping禁掉则须要另外的办法),则看在server端的监听指定端口的程序是否启动,这个肯定能解决问题。 

    第3个异常是java.net.SocketException: Socket is closed,该异常在client和server均可能发生。异常的原因是己方主动关闭了连接后(调用了Socket的close方法)再对网络连接进行读写操作。 

    第4个异常是java.net.SocketException: (Connection reset或者Connect reset by peer:Socket write error)。该异常在client和server端均有可能发生,引起该异常的原因有两个,第一个就是假设一端的Socket被关闭(或主动关闭或者由于异常退出而引起的关闭),还有一端仍发送数据,发送的第一个数据包引发该异常(Connect reset by peer)。还有一个是一端退出,但退出时并未关闭该连接,还有一端假设在从连接中读数据则抛出该异常(Connection reset)。简单的说就是在连接断开后的读和写操作引起的。 

    第5个异常是java.net.SocketException: Broken pipe。该异常在client和server均有可能发生。在第4个异常的第一种情况中(也就是抛出SocketExcepton:Connect reset by peer:Socket write error后),假设再继续写数据则抛出该异常。前两个异常的解决方法是首先确保程序退出前关闭全部的网络连接,其次是要检測对方的关闭连接操作,发现对方关闭连接后自己也要关闭该连接。

  • 相关阅读:
    编程语言
    俄罗斯方块
    四则运算
    2019-2020-1 20191312《信息安全专业导论》第七周学习总结
    20191312-获奖感想与学习心得
    2019-2020-1学期 20192428 《网络空间安全专业导论》第九周小组讨论
    2019-2020-1学期 20192428 《网络空间安全专业导论》第九周学习总结
    2019-2020-1学期 20192428 第八周作业——小学四则运算实践
    2019-2020-1学期 20192428《网络空间安全专业导论》第八周学习总结
    2019-2020-1学期 20192428 《网络空间安全专业导论》第七周学习总结
  • 原文地址:https://www.cnblogs.com/gcczhongduan/p/4291440.html
Copyright © 2020-2023  润新知