java之网络通信

来源:互联网 发布:上古世纪精灵捏脸数据 编辑:程序博客网 时间:2024/06/05 01:42

一、网络通信简介

网络通信是指物理上位于计算机上的两个进程之间通过网络交换信息的过程。JAVA语言对网络通信提供了全面的支持。
JAVA支持网络功能的四大类:

这里写图片描述

二、Java Socket应用—通信是这样练成的

1、InetAddress类

InetAddress 类用于标识网络上的硬件资源,实际上主要用来表示IP地址。它没有构造方法,无法通过new创建对象,只能通过静态方法获取对象:

// 一、获取本机的InetAdress实例InetAddress address = InetAddress.getLocalHost();
// 二、根据主机名称获取InetAdress实例InetAddress address2 = InetAddress.getByName("hostName");
// 三、根据IP地址获取实例InetAddress address2 = InetAddress.getByName("ipAdress");

下面通过一个实例来熟悉InetAddress类的创建及常用方法的使用

import java.net.InetAddress;import java.net.UnknownHostException;import java.util.Arrays;/* * InetAddress类 */public class Test01 {    public static void main(String[] args) throws UnknownHostException {        // 获取本机的InetAddress实例        InetAddress address = InetAddress.getLocalHost();        System.out.println("计算名:" + address.getHostName());        System.out.println("IP地址:" + address.getHostAddress());        byte[] bytes = address.getAddress();// 获取字节数组形式的IP地址        System.out.println("字节数组形式的IP:" + Arrays.toString(bytes));        System.out.println(address);// 直接输出InetAddress对象        // 根据机器名获取InetAddress实例        // InetAddress address2=InetAddress.getByName("laurenyang");        InetAddress address2 = InetAddress.getByName("1.1.1.10");        System.out.println("计算名:" + address2.getHostName());        System.out.println("IP地址:" + address2.getHostAddress());    }}

运行结果:

这里写图片描述

2.URL类

url又称统一资源定位符,表示Internet上的某一资源的地址,由两部分组成:协议名称和资源名称,中间用冒号隔开,如http://www.baidu.com。在java.net包中,提供了URL类来表示URL。
下面也通过一个实例来熟悉URL对象的创建及常用方法的使用

import java.net.MalformedURLException;import java.net.URL;/* * URL常用方法 */public class Test02 {    public static void main(String[] args) {        try {            //创建一个URL实例            URL imooc=new URL("http://www.imooc.com");            //?后面表示参数,#后面表示锚点            URL url=new URL(imooc, "/index.html?username=tom#test");            System.out.println("协议:"+url.getProtocol());            System.out.println("主机:"+url.getHost());            //如果未指定端口号,则使用默认的端口号,此时getPort()方法返回值为-1            System.out.println("端口:"+url.getPort());            System.out.println("文件路径:"+url.getPath());            System.out.println("文件名:"+url.getFile());            System.out.println("相对路径:"+url.getRef());            System.out.println("查询字符串:"+url.getQuery());        } catch (MalformedURLException e) {            e.printStackTrace();        }    }}

运行结果:

这里写图片描述

下面的这个实例展示了如何使用URL读取网页内容

import java.io.BufferedReader;import java.io.IOException;import java.io.InputStream;import java.io.InputStreamReader;import java.net.MalformedURLException;import java.net.URL;/* * 使用URL读取网页内容 */public class Test03 {    public static void main(String[] args) {        try {            //创建一个URL实例            URL url = new URL("http://www.baidu.com");            //通过URL的openStream方法获取URL对象所表示的资源的字节输入流            InputStream is = url.openStream();            //将字节输入流转换为字符输入流            InputStreamReader isr = new InputStreamReader(is, "utf-8");            //为字符输入流添加缓冲            BufferedReader br = new BufferedReader(isr);            String data = br.readLine();//读取数据            while (data != null) {//循环读取数据                System.out.println(data);//输出数据                data = br.readLine();            }            br.close();            isr.close();            is.close();        } catch (MalformedURLException e) {            e.printStackTrace();        } catch (IOException e) {            e.printStackTrace();        }    }}

运行结果:

这里写图片描述

读取出来的是百度首页的源代码,我将其复制到一个文档中,这样看更加清楚,如下所示:

这里写图片描述

3.Socket类

相关知识概述:
1、Socket通信是网络上运行的程序之间双向通信链路的终结点(IP地址和端口号组成了Socket),它是TCP和UDP的基础。
2、端口号的范围为0~65525,其中1024以下(0~1023)为系统保留,如果要自定义端口号要用1024以上的。
3、常见的应用层协议有下面几种:
HTTP 超文本传输协议
ftp 文件传输协议
smtp 简单邮件传输协议
telnet 远程登陆服务

Socket通信实现步骤
1.创建ServerSocket(服务器端)和Socket(客户端)
2.打开链接到Socket的输入/输出流
3.按照协议对Socket进行读/写操作
4.关闭输入输出流、关闭Socket
模型图示如下:

这里写图片描述

通过Socket实现TCP编程

服务器端操作:
这里写图片描述

客户端操作:

这里写图片描述
首先,要把模拟服务器的程序(Server.java)启动起来,等待客户端的连接,再运行客户端程序(Client.java),与服务器建立连接后会得到服务器的响应,控制台会输入“服务器欢迎您”字样,同时客户端也将用户名和密码发送给了服务器,此时再切换到服务器控制台下面会显示用户名和密码。
控制台效果如下:

这里写图片描述

这里写图片描述

这里写图片描述

Server.java

import java.io.BufferedReader;import java.io.IOException;import java.io.InputStream;import java.io.InputStreamReader;import java.io.OutputStream;import java.io.PrintWriter;import java.net.InetAddress;import java.net.ServerSocket;import java.net.Socket;/* * 基于TCP协议的Socket通信,实现用户登陆 * 服务器端 */public class Server {    public static void main(String[] args) {        try {            //1.创建一个服务器端Socket,即ServerSocket,指定绑定的端口,并监听此端口            ServerSocket serverSocket=new ServerSocket(8888);            //2.调用accept()方法开始监听,等待客户端的连接            System.out.println("***服务器即将启动,等待客户端的连接***");            Socket socket=serverSocket.accept();//阻塞状态,侦听请求            //3.获取输入流,并读取客户端信息            InputStream is = socket.getInputStream();//字节输入流            InputStreamReader isr = new InputStreamReader(is);//将字节流转换为字符流            BufferedReader br = new BufferedReader(isr);//为输入流添加缓冲            String info = null;            while((info=br.readLine())!= null){                System.out.println("我是服务器,客户端说:"+info);            }            socket.shutdownInput();//关闭输入流            //4.获取输出流,响应客户端的请求            OutputStream os = socket.getOutputStream();            PrintWriter pw = new PrintWriter(os);//包装为打印流                       pw.write("欢迎您");            pw.flush();//调用flush()方法将缓冲输出            //5.关闭资源            os.close();            pw.close();            br.close();            isr.close();            is.close();            socket.close();            serverSocket.close();        } catch (IOException e) {            e.printStackTrace();        }    }}

Client.java

import java.io.BufferedReader;import java.io.IOException;import java.io.InputStream;import java.io.InputStreamReader;import java.io.OutputStream;import java.io.PrintWriter;import java.net.Socket;import java.net.UnknownHostException;/* * 客户端 */public class Client {    public static void main(String[] args) {        try {            //1.创建客户端Socket,指定服务器地址和端口            Socket socket=new Socket("localhost", 8888);            //2.获取输出流,向服务器端发送信息            OutputStream os=socket.getOutputStream();//字节输出流            PrintWriter pw=new PrintWriter(os);//将输出流包装为打印流            pw.write("用户名:alice;密码:789");            pw.flush();            socket.shutdownOutput();//关闭输出流            //3.获取输入流,并读取服务器端的响应信息            InputStream is=socket.getInputStream();            BufferedReader br=new BufferedReader(new InputStreamReader(is));            String info=null;            while((info=br.readLine())!=null){                System.out.println("我是客户端,服务器说:"+info);            }            //4.关闭资源            br.close();            is.close();            pw.close();            os.close();            socket.close();        } catch (UnknownHostException e) {            e.printStackTrace();        } catch (IOException e) {            e.printStackTrace();        }    }}

使用多线程实现多客户端的通信

应用多线程来实现服务器与多客户端之间的通信基本步骤:
1、服务器端创建ServerSocket,循环调用accept()等待客户端连接
2、客户端创建一个socket并请求和服务器端连接。
3、服务器端接受客户端请求,创建socket与该客户建立专线连接。
4、建立连接的两个socket在一个单独的线程上对话。
5、服务器端继续等待新的连接。

首先,创建一个ServerThread.java类来实现多线程,然后将上面的例子稍加修改一下
ServerThread.java

import java.io.BufferedReader;import java.io.IOException;import java.io.InputStream;import java.io.InputStreamReader;import java.io.OutputStream;import java.io.PrintWriter;import java.net.Socket;/* * 服务器线程处理类 */public class ServerThread extends Thread {    // 和本线程相关的Socket    Socket socket = null;    public ServerThread(Socket socket) {        this.socket = socket;    }    //线程执行的操作,响应客户端的请求    public void run(){        InputStream is=null;        InputStreamReader isr=null;        BufferedReader br=null;        OutputStream os=null;        PrintWriter pw=null;        try {            //获取输入流,并读取客户端信息            is = socket.getInputStream();            isr = new InputStreamReader(is);            br = new BufferedReader(isr);            String info=null;            while((info=br.readLine())!=null){//循环读取客户端的信息                System.out.println("我是服务器,客户端说:"+info);            }            socket.shutdownInput();//关闭输入流            //获取输出流,响应客户端的请求            os = socket.getOutputStream();            pw = new PrintWriter(os);            pw.write("欢迎您!");            pw.flush();//调用flush()方法将缓冲输出        } catch (IOException e) {            // TODO Auto-generated catch block            e.printStackTrace();        }finally{            //关闭资源            try {                if(pw!=null)                    pw.close();                if(os!=null)                    os.close();                if(br!=null)                    br.close();                if(isr!=null)                    isr.close();                if(is!=null)                    is.close();                if(socket!=null)                    socket.close();            } catch (IOException e) {                e.printStackTrace();            }        }    }}

Server.java

import java.io.BufferedReader;import java.io.IOException;import java.io.InputStream;import java.io.InputStreamReader;import java.io.OutputStream;import java.io.PrintWriter;import java.net.InetAddress;import java.net.ServerSocket;import java.net.Socket;/* * 基于TCP协议的Socket通信,实现用户登陆 * 服务器端 */public class Server {    public static void main(String[] args) {        try {            //1.创建一个服务器端Socket,即ServerSocket,指定绑定的端口,并监听此端口            ServerSocket serverSocket=new ServerSocket(8888);            Socket socket=null;            //记录客户端的数量            int count=0;            System.out.println("***服务器即将启动,等待客户端的连接***");            //循环监听等待客户端的连接            while(true){                //调用accept()方法开始监听,等待客户端的连接                socket=serverSocket.accept();                //创建一个新的线程                ServerThread serverThread=new ServerThread(socket);                //启动线程                serverThread.start();                count++;//统计客户端的数量                System.out.println("客户端的数量:"+count);                InetAddress address=socket.getInetAddress();                System.out.println("当前客户端的IP:"+address.getHostAddress());            }        } catch (IOException e) {            e.printStackTrace();        }    }}

控制台显示:

这里写图片描述

Client.java

import java.io.BufferedReader;import java.io.IOException;import java.io.InputStream;import java.io.InputStreamReader;import java.io.OutputStream;import java.io.PrintWriter;import java.net.Socket;import java.net.UnknownHostException;/* * 客户端 */public class Client {    public static void main(String[] args) {        try {            //1.创建客户端Socket,指定服务器地址和端口            Socket socket=new Socket("localhost", 8888);            //2.获取输出流,向服务器端发送信息            OutputStream os=socket.getOutputStream();//字节输出流            PrintWriter pw=new PrintWriter(os);//将输出流包装为打印流            pw.write("用户名:alice;密码:789");            pw.flush();            pw.close();//不能关闭输出流,会导致socket也被关闭,在下面直接关闭socket就行,这样就会关闭输出流            socket.shutdownOutput();//关闭输出流            //3.获取输入流,并读取服务器端的响应信息            InputStream is=socket.getInputStream();            BufferedReader br=new BufferedReader(new InputStreamReader(is));            String info=null;            while((info=br.readLine())!=null){                System.out.println("我是客户端,服务器说:"+info);            }            //4.关闭资源            br.close();            is.close();            pw.close();            os.close();            socket.close();        } catch (UnknownHostException e) {            e.printStackTrace();        } catch (IOException e) {            e.printStackTrace();        }    }}

控制台显示:

这里写图片描述

客户端与服务器建立连接后,服务器会收到客户端发送过去的信息,如下:

这里写图片描述

为了模拟多用户同时连接服务器,将客户端程序的用户名和密码修改一下:

这里写图片描述

再次运行Client.java,切换到服务器控制台下:

这里写图片描述

因为是同一台主机,所以此处客户端IP地址是一样的,而实际生活中是不同的,要注意一下。

注意:

  1. 对于同一个socket,如果关闭了输出流,则与该输出流关联的socket也会被关闭,所以一般不用关闭流,直接关闭socket即可。
  2. 进行TCP通信时,客户端向服务器传输数据,我们演示的以字符串的形式传输,而实际应用中一般都是以对象的形式进行传输的。如下图所示:
    这里写图片描述
    3.当用socket传输文件时,可以用输入输出流读取文件,再进行传输。

通过Socket实现UDP编程

一、UDP编程
UDP协议(用户数据报协议)是无连接的、不可靠的、无序的。
UDP协议以数据报作为数据传输载体。进行数据传输时,首先将要传输的数据定义成数据报(Datagram),在数据报中指明数据所要达到的Socket(主机地址和端口号),然后再将数据报发送出去。

二、相关操作类
DatagramPacket:表示数据报包
DatagramSocket:进行端到端通信的类

服务器端实现步骤:
1、创建DatagramSocket,指定端口号
2、创建DatagramPacket
3、接收客户端发送的数据信息
4、读取数据

客户端:
1、定义发送信息
2、创建DatagramPacket:包含将要发送信息
3、创建DatagramSocket
4、发送数据

下面通过实例进行演示:
UDPServer.java

import java.io.IOException;import java.net.DatagramPacket;import java.net.DatagramSocket;import java.net.InetAddress;/* * 服务器端,实现基于UDP的用户登陆 */public class UDPServer {    public static void main(String[] args) throws IOException {        /*         * 接收客户端发送的数据         */        //1.创建服务器端DatagramSocket,指定端口        DatagramSocket socket=new DatagramSocket(8800);        //2.创建数据报,用于接收客户端发送的数据        byte[] data =new byte[1024];//创建字节数组,指定接收的数据包的大小        DatagramPacket packet=new DatagramPacket(data, data.length);        //3.接收客户端发送的数据        System.out.println("****服务器端已经启动,等待客户端发送数据****");        socket.receive(packet);//此方法在接收到数据报之前会一直阻塞        //4.读取数据        String info=new String(data, 0, packet.getLength());        System.out.println("我是服务器,客户端说:"+info);        /*         * 向客户端响应数据         */        //1.定义客户端的地址、端口号、数据        InetAddress address=packet.getAddress();        int port=packet.getPort();        byte[] data2="欢迎您!".getBytes();        //2.创建数据报,包含响应的数据信息        DatagramPacket packet2=new DatagramPacket(data2, data2.length, address, port);        //3.响应客户端        socket.send(packet2);        //4.关闭资源        socket.close();    }}

运行后控制台显示:

这里写图片描述
UDPClient.java

import java.io.IOException;import java.net.DatagramPacket;import java.net.DatagramSocket;import java.net.InetAddress;import java.net.SocketException;import java.net.UnknownHostException;/* * 客户端 */public class UDPClient {    public static void main(String[] args) throws IOException {        /*         * 向服务器端发送数据         */        //1.定义服务器的地址、端口号、数据        InetAddress address=InetAddress.getByName("localhost");        int port=8800;        byte[] data="用户名:admin;密码:123".getBytes();        //2.创建数据报,包含发送的数据信息        DatagramPacket packet=new DatagramPacket(data, data.length, address, port);        //3.创建DatagramSocket对象        DatagramSocket socket=new DatagramSocket();        //4.向服务器端发送数据报        socket.send(packet);        /*         * 接收服务器端响应的数据         */        //1.创建数据报,用于接收服务器端响应的数据        byte[] data2=new byte[1024];        DatagramPacket packet2=new DatagramPacket(data2, data2.length);        //2.接收服务器响应的数据        socket.receive(packet2);        //3.读取数据        String reply=new String(data2, 0, packet2.getLength());        System.out.println("我是客户端,服务器说:"+reply);        //4.关闭资源        socket.close();    }}

控制台显示:

这里写图片描述

可见,客户端收到了服务器的响应,再切换到服务器控制台查看:

这里写图片描述

服务器也接收到了来自客户端的信息。
这里只是模拟了单个用户与服务器进行交互,其实基于UDP的Socket通信也可以像基于TCP那样,通过多线程实现多客户端同时访问服务器,在这里就不再演示了。

三、远程方法调用

远程方法调用(RMI,Remote Method Invocation),是允许运行在一个Java虚拟机的对象调用运行在另一个Java虚拟机上的对象的方法。 这两个虚拟机可以是运行在相同计算机上的不同进程中,也可以是运行在网络上的不同计算机中。
目前还未真正接触到这个,日后再深入研究。

0 0
原创粉丝点击