Java基础 - 网络编程


IP和端口

Java中使用 java.net.InetAddress 这个类描述IP地址。该类的常用方法如下:

方法 描述
public static InetAddress getLocalHost() throws UnknowHostException 获取本地主机的 InetAddress 对象
public static InetAddress getByName(String host) throws UnknowHostException 通过主机名称创建 InetAddress 对象
String getHostName() 获取主机名称
public String getHostAddress() 获取主机 IP 地址
public static InetAddress getByAddress(String host, byte[] addr) throws UnknowHostException 通过主机名称和IP地址创建 InetAddress对象
public static InetAddress getByAddress(byte[] addr) throws UnknowHostException 通过IP地址创建 InetAddress 对象
public class Main {
    public static void main(String[] args) throws UnknownHostException {
        InetAddress inetAddress = InetAddress.getLocalHost();
        System.out.println("主机IP地址:" + inetAddress.getHostAddress());
        System.out.println("主机名称:" + inetAddress.getHostName());
        InetAddress inetAddress1 = InetAddress.getByName("127.0.0.1");
        System.out.println(inetAddress1);
        InetAddress inetAddress2 = InetAddress.getByAddress(new byte[]{'a','b','c','d'});
        System.out.println(inetAddress2);
    }
}

如果把IP比作一栋大厦的地址,那么端口 ( port)就是不同房间的门牌号。IP地址需要和端口结合起来使用,好比快递小哥必须要通过大厦地址和房间号才能准确找到你。计算机主机就相当于大厦,网络中的请求需要通过IP地址来找到主机,同时一台主机上会同时运行很多个服务,如何把不同的请求分配给不同的服务就需要使用到端口了。

URL 和 URLConnection

通常讲的网络资源实际是指网络中真实存在的一个实体,比如文字、图片、视频、音频等,如果我们要在程序获取网络实体,应该怎么做呢?可以用URI (Uniform ResourceIdentifier)统一资源定位符指向目标实体,URI的作用是用特定的语法来标识某个网络资源。Java中专门封装了一个类用来描述URI,这个类就是 java.net.URI,使用URI 的实例化对象,可以用面向对象的方式来管理网络资源。

public class Main {
    public static void main(String[] args) throws URISyntaxException {
        URI uri = new URI("http://localhost:8080/index/login.html");
        System.out.println(uri.getHost());
        System.out.println(uri.getPort());
        System.out.println(uri.getPath());
    }
}
// 输出
localhost
8080
/index/login.html

URL (Uniform Resource Locator)是统一资源位置,在URI的基础上进行了扩充,在定位资源的同时还提供了对应的网络地址,Java也对URL进行了封装,java.net.URL类常用方法如下。

方法 描述
public URL(String protocol, String host, int port, String file) throws MalformedURLException 根据协议、IP地址、端口号、资源名称获取URL对象
public final InputStream openStream() throws java.io.IOException 获取输入流对象
public class Main {
    public static void main(String[] args) throws IOException {
        InputStream inputStream = null;
        Reader reader = null;
        BufferedReader bufferedReader = null;
        try {
            URL url = new URL("http","127.0.0.1",80,"/URLtest/index.html");
            inputStream = url.openStream();
            reader = new InputStreamReader(inputStream);
            bufferedReader = new BufferedReader(reader);
            String str = null;
            while ((str = bufferedReader.readLine()) != null){
                System.out.println(str);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            bufferedReader.close();
            reader.close();
            inputStream.close();
        }
    }
}
// 输出
<html>
<head>
    <title>URL测试页面</title>
</head>
<body>
    <h1>url1</h1>
    <h2>url2</h2>
    <h3>url3</h3>
</body>
</html>

同时也可以通过URL获取资源的其他信息,如主机地址,端口等。

public class Main {
    public static void main(String[] args) throws IOException {
        URL url = new URL("http","127.0.0.1",80,"/URLtest/index.html");
        System.out.println("url中的IP地址:" + url.getHost());
        System.out.println("url中的资源地址:" + url.getPath());
        System.out.println("url访问的端口:" + url.getPort());
    }
}
// 输出
url中的IP地址:127.0.0.1
url中的资源地址:/URLtest/index.html
url访问的端口:80

URLConnection

URLConnection用来描述 URL 指定资源的连接,是一个抽象类,常用的子类有HttpURLConnection,URLConnection 底层是通过HTTP协议来处理的,它定义了访问远程网络资源的方法。通过URLConnection可以获取到URL资源的相关信息,该类常用的方法如下所示。

方法 描述
public int getContentLength() 返回资源的长度,返回值为 int 类型
public long getContentLengthLong() 返回资源的长度,返回值为 long 类型
public String getContentType() 返回资源的类型
public abstract void connect() throws IOException 判断连接的打开或关闭状态
public Inputstream getInputStream() throws IOException 获取输入流对象
public class Main {
    public static void main(String[] args) throws IOException {
        URL url = new URL("http://localhost/URLtest/index.html");
        URLConnection urlConnection = url.openConnection();
        System.out.println(urlConnection.getURL());
        System.out.println(urlConnection.getContentLength());
        System.out.println(urlConnection.getContentLengthLong());
        System.out.println(urlConnection.getContentType());
        System.out.println("----- 网页资源内容 -----");
        InputStream inputStream = urlConnection.getInputStream();
        Reader reader = new InputStreamReader(inputStream);
        BufferedReader bufferedReader = new BufferedReader(reader);
        String str = null;
        while ((str = bufferedReader.readLine()) != null){
            System.out.println(str);
        }
    }
}
// 输出
http://localhost/URLtest/index.html
130
130
text/html
----- 网页资源内容 -----
<html>
<head>
    <title>URL测试页面</title>
</head>
<body>
    <h1>url1</h1>
    <h2>url2</h2>
    <h3>url3</h3>
</body>
</html>

TCP协议

Java通过Socket来完成TCP程序的开发,Socket是一个类,使用该类可以在服务端与客户端之间建立可靠的连接。在实际开发中,Socket表示客户端,服务端使用ServerSocket来表示,ServerSocket也是一个类,ServerSocket和 Socket都存放在java.net包中。具体的开发思路是在服务端创建ServerSocket对象,然后通过该对象的 accept()方法可以接收到若干个表示客户端的Sokcet对象。

ServerSocket类的常用方法

方法 描述
public ServerSocket(int port) throws IOException 根据端口创建ServerSocket实例对象
public ServerSocket(int port, int backlog) throws IOException 根据端口和backlog创建ServerSocket实例对象
public ServerSocket(int port,int backlog, InetAddress address) throws IOException 根据端口、backlog和IP地址创建ServerSocket实例对象
public ServerSocket() throws IOException 创建没有绑定服务器的ServerSocket实例对象
public synchronized int getSoTimeout() throws IOException 获取Sotimeout的设置
public InetAddress getInetAddress() 获取服务器的IP地址
public Socket accept() throws IOException 等待客户端请求,并返回Socket对象
public void close() throws IOException 关闭ServerSocket
public boolean isClosed() 返回ServerSocket的关闭状态
public void bind(SocketAddress endpoint) throws IOException 将ServerSocket实例对象绑定到指定地址
public int getLocalPort() 返回ServerSocket的端口

Socket类的常用方法

方法 描述
public Socket(String host,int port) throws IOException,UnknowHostException 根据主机,端口创建要连接的Socket对象
public Socket(InetAddress host,int port) throws IOException 根据IP地址,端口创建要连接的Socket对象
public Socket(String host,int port,InetAddress localAddress, int localPort) throws IOException 根据主机,端口创建要连接的Socket对象并将其连接到指定的远程主机上的指定端口
public Socket(InetAddress host,int port,InetAddress localAddress,int localPort) throws IOException 根据主机,端口创建要连接的Socket对象并将其连接到指定远程地址上的指定端口
public Socket() 创建没有连接的Socket对象
public InputStream getInputStream() throws IOException 返回Socket的输入流
public synchronized void close() throws IOException 关闭Socket
public boolean isClose() 返回Socket的关闭状态

首先启动ServerSocket,等待接收客户端请求,当接收到客户端请求后,打印信息,并向客户端返回信息。

// 服务端
public class Server {
    public static void main(String[] args) throws IOException {
        ServerSocket serverSocket = null;
        Socket socket = null;
        OutputStream outputStream = null;
        InputStream inputStream = null;
        DataOutputStream dataOutputStream = null;
        DataInputStream dataInputStream = null;
        Scanner scanner = new Scanner(System.in);
        try{
            serverSocket = new ServerSocket(8081);
            System.out.println("------ 服务器 ------");
            System.out.println("服务端已启动,等待接收客户端请求 ... ");
            while (true){
                socket = serverSocket.accept();
                inputStream = socket.getInputStream();
                dataInputStream = new DataInputStream(inputStream);
                String request = dataInputStream.readUTF();
                System.out.println("接收到了客户端的请求:" + request);
                outputStream = socket.getOutputStream();
                dataOutputStream = new DataOutputStream(outputStream);
                System.out.print("请输入发送给客户端的信息:");
                String response = scanner.next();
                dataOutputStream.writeUTF(response);
                System.out.println("发送给客户端的信息:" + response);
            }
        } catch (IOException e){
            e.printStackTrace();
        } finally {
            dataOutputStream.close();
            outputStream.close();
            dataInputStream.close();
            inputStream.close();
            socket.close();
            serverSocket.close();
        }
    }
}

// 客户端
public class Client {
    public static void main(String[] args) throws IOException {
        Socket socket = null;
        OutputStream outputStream = null;
        DataOutputStream dataOutputStream = null;
        InputStream inputStream = null;
        DataInputStream dataInputStream = null;
        Scanner scanner = new Scanner(System.in);
        try{
            socket = new Socket("127.0.0.1", 8081);
            System.out.println("------客户端------");
            System.out.print("请输入发送给服务端的信息:");
            String request = scanner.next();
            System.out.println("客户端发送:" + request);
            outputStream = socket.getOutputStream();
            dataOutputStream = new DataOutputStream(outputStream);
            dataOutputStream.writeUTF(request);
            inputStream = socket.getInputStream();
            dataInputStream = new DataInputStream(inputStream);
            String response = dataInputStream.readUTF();
            System.out.println("服务端响应:" + response);

        } catch (IOException e){
            e.printStackTrace();
        } finally {
            dataInputStream.close();
            inputStream.close();
            dataOutputStream.close();
            outputStream.close();
            socket.close();
        }
    }
}

UDP协议

TCP协议可以建立稳定可靠的连接,保证数据的完整。但是TCP协议的缺点也很明显,先建立可靠连接,再进行操作的方式必然会造成系统运行效率低下。在实际开发中,某些业务场景对系统的运行效率要求较高,使用TCP协议很显然就不合适了,这时候就需要使用另外一种传输协议 -— UDP。

UDP所有的连接都是不可靠的,即不需要建立连接,直接发送数据即可。例如通过微信聊天,发送方只需要把信息发出去,接收方可能因为网络差或者其他原因没有收到信息,但是发送方并不会因为接收方无法接收到信息而等待,它可以连续发送数据。所以UDP的速度更快,但是可能会造成数据丢失,安全性不高,追求速度的应用可以选择UDP。例如语音聊天或者视频聊天,对于这类应用流畅性更重要,偶尔丢失几个数据包并不会有太大影响。Java提供了DatagramSocket类和 DatagramPacket类,来帮助开发者编写基于UDP协议的程序。
DatagramSocket类的常用方法

方法 描述
public DatagramSocket(int port) throws SocketException 根据端口创建DatagramSocket实例对象
public void send(DatagramPacket p) throws IOException 发送数据报
public synchronized void receive(DatagramPacket p) throws IOException 接收数据报
public InetAddress getInetAddress() 获取DatagramSocket对应的 InetAddress对象
public boolean isConnected() 判断是否连接到服务
方法 描述
public DatagramPacket(byte buf[], int length, InetAddress address, int port) 根据发送的数据,数据长度,IP地址,端口创建DatagramPacket实例对象
public synchronized byte[] getData() 获取接收的数据
public synchronized int getLength() 获取数据长度
public synchronized int getPort() 获取发送数据的Socket端口
public synchronized SocketAddress getSocketAddress() 获取发送数据的Socket信息
public class UserA {
    public static void main(String[] args) throws IOException {
        // 接收数据
        byte[] buf = new byte[1024];
        DatagramPacket datagramPacket = new DatagramPacket(buf, buf.length);
        DatagramSocket datagramSocket = new DatagramSocket(8888);
        datagramSocket.receive(datagramPacket);
        String message = new String(datagramPacket.getData(),0,datagramPacket.getLength());
        System.out.println("我是UserA,收到了 " + datagramPacket.getPort() + " 发送的数据:" + message);

        // 发送数据
        String reply = "我是UserA,收到";
        SocketAddress socketAddress = datagramPacket.getSocketAddress();
        DatagramPacket datagramPacket1 = new DatagramPacket(reply.getBytes(), reply.getBytes().length,socketAddress);
        datagramSocket.send(datagramPacket1);
    }
}

public class UserB {
    public static void main(String[] args) throws IOException {
        String message = "我是UserB,UserA你好!";
        // 发送数据
        InetAddress inetAddress = InetAddress.getByName("localhost");
        DatagramPacket datagramPacket = new DatagramPacket(message.getBytes(), message.getBytes().length,inetAddress,8888);
        DatagramSocket datagramSocket = new DatagramSocket(6666);
        datagramSocket.send(datagramPacket);

        // 接收数据
        byte[] bytes = new byte[1024];
        DatagramPacket datagramPacket1 = new DatagramPacket(bytes, bytes.length);
        datagramSocket.receive(datagramPacket1);
        String reply = new String(datagramPacket1.getData(),0, datagramPacket1.getLength());
        System.out.println("我是UserB,收到了 " + datagramPacket1.getPort() + " 的数据:" + reply);
    }
}

多线程下的网络编程

public class ServerRunnable implements Runnable{
    private Socket socket;
    public ServerRunnable(Socket socket){
        this.socket = socket;
    }
    @Override
    public void run() {
        InputStream inputStream = null;
        DataInputStream dataInputStream = null;
        try {
            inputStream = this.socket.getInputStream();
            dataInputStream = new DataInputStream(inputStream);
            String message = dataInputStream.readUTF();
            System.out.println(message);
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            try {
                dataInputStream.close();
                inputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}
public class ServerThread {
    public static void main(String[] args) throws IOException {
        ServerSocket serverSocket = null;
        serverSocket = new ServerSocket(9999);
        System.out.println("服务端启动 ... ");
        while (true){
            Socket socket = serverSocket.accept();
            new Thread(new ServerRunnable(socket)).start();
        }
    }
}

public class ClientRunnable implements Runnable{
    private int num;
    public ClientRunnable(int num){
        this.num = num;
    }
    @Override
    public void run() {
        Socket socket = null;
        OutputStream outputStream = null;
        DataOutputStream dataOutputStream = null;
        try {
            socket = new Socket("localhost", 9999);
            String message = "我是客户端 " + this.num;
            outputStream = socket.getOutputStream();
            dataOutputStream = new DataOutputStream(outputStream);
            dataOutputStream.writeUTF(message);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                dataOutputStream.close();
                outputStream.close();
                socket.close();
            } catch (IOException e) {
            }
        }
    }
}
public class ClientThread {
    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            new Thread(new ClientRunnable(i)).start();
        }
    }
}

简单的聊天小程序

public class SocketRunnable implements Runnable{
    private Socket socket;
    public SocketRunnable(Socket socket){
        this.socket = socket;
    }
    @Override
    public void run() {
        InputStream inputStream = null;
        DataInputStream dataInputStream = null;
        Scanner scanner = new Scanner(System.in);
        String message = null;
        try {
            while (true){
                // 读
                inputStream = socket.getInputStream();
                dataInputStream = new DataInputStream(inputStream);
                message = dataInputStream.readUTF();
                System.out.println("客户端:" + message);
                // 写
                System.out.print("服务端:");
                message = scanner.next();
                OutputStream outputStream = null;
                DataOutputStream dataOutputStream = null;
                try {
                    outputStream = socket.getOutputStream();
                    dataOutputStream = new DataOutputStream(outputStream);
                    dataOutputStream.writeUTF(message);
                } catch (IOException e){

                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
public class ServerTalk {
    public static void main(String[] args) throws IOException {
        ServerSocket serverSocket = new ServerSocket(8088);
        Socket socket = null;
        System.out.println("服务器已启动......");
        while (true){
            socket = serverSocket.accept();
            new Thread(new SocketRunnable(socket)).start();
        }
    }
}
public class ClientTalk {
    public static void main(String[] args) throws IOException {
        Socket socket = new Socket("127.0.0.1",8088);

        System.out.println("客户端已启动......");
        Scanner scanner = new Scanner(System.in);
        String message = null;
        while (true){
            // 写
            System.out.print("客户端:");
            message = scanner.next();
            OutputStream outputStream = socket.getOutputStream();
            DataOutputStream dataOutputStream = new DataOutputStream(outputStream);
            dataOutputStream.writeUTF(message);
            // 读
            InputStream inputStream = socket.getInputStream();
            DataInputStream dataInputStream = new DataInputStream(inputStream);
            message = dataInputStream.readUTF();
            System.out.println("服务端:" + message);
        }
    }
}

文章作者: zerollone
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 zerollone !
  目录