【Java学习笔记】57:认识网络编程

URL对象

URL统一资源标识符,在Java中通常放在URL对象中使用。URL对象通常需要包含:①协议②地址③资源。

import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Scanner;

public class Main {

    public static void main(String[] args) {
        URL url;// URL对象
        Thread th;// 线程对象
        Look lk = new Look();// 目标对象
        System.out.println("从键盘输入URL资源:");
        Scanner sc = new Scanner(System.in);
        String source = sc.nextLine();// 把输入的这行资源读为字符串

        try {
            // 利用输入的url构造一个URL对象
            url = new URL(source);
            lk.setURL(url);// 传给线程的目标对象
            th = new Thread(lk);// 用目标对象去构造线程
            th.start();// 启动这个子线程
        } catch (MalformedURLException e) {
            e.printStackTrace();
        }

    }

}

class Look implements Runnable {
    URL url;

    // 获取外部传入的URL
    void setURL(URL url) {
        this.url = url;
    }

    // URL资源的读取可能引起阻塞,所以需要在一个线程中读取URL资源
    public void run() {
        try {
            // 返回一个指向URL对象所包含的资源的输入流
            InputStream is = url.openStream();
            byte[] b = new byte[1024];// 用适当大的字节数组从输入流读信息
            int n = -1;
            while ((n = is.read(b)) != -1)// 成功读到信息,返回信息的长度
            {
                String str = new String(b, 0, n);// 截取从0~结尾的信息变成字符串
                System.out.print(str);// 输出,不换行,流式信息内该换行的地方自会有换行符
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

    }
}

输出(部分)

从键盘输入URL资源:
http://blog.csdn.net/SHU15121856


<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">

        <html xmlns="http://www.w3.org/1999/xhtml">

        <head>
        <link rel="canonical" href="http://blog.csdn.net/SHU15121856" />
......

InetAddress

两种表示因特网上的主机的方式:①域名②ip地址。InetAddress类用其类方法InetAddress.getByName()获取一个包含域名和ip地址的InetAddress对象:

import java.net.InetAddress;
import java.net.UnknownHostException;

public class Main {

    public static void main(String[] args) {
        try {
            // 通过域名表示主机所在地址,获得InetAddress对象
            InetAddress ia1 = InetAddress.getByName("www.baidu.com");
            // 通过ip地址表示主机所在地址,获得InetAddress对象
            InetAddress ia2 = InetAddress.getByAddress(new byte[] { (byte) 192, (byte) 168, 0, 108 });
            // 获得本机InetAddress对象
            InetAddress ia3 = InetAddress.getLocalHost();
            // getHostNmae()可以获取该对象所包含的主机名
            // getHostAddresss()可以获取该对象包含的ip地址
            System.out.println("ia1对应的主机名" + ia1.getHostName() + ",ip地址" + ia1.getHostAddress());
            System.out.println("ia2对应的主机名" + ia2.getHostName() + ",ip地址" + ia2.getHostAddress());
            System.out.println("ia3对应的主机名" + ia3.getHostName() + ",ip地址" + ia3.getHostAddress());
        } catch (UnknownHostException e) {
            // 当没法找到这个地址时会抛掷UnknownHostException异常
            e.printStackTrace();
        }

    }

}

注意Linux下使用InetAddress.getLocalHost()只会返回主机名和127.0.0.1,因为是直接去读了/etc/hosts文件。另外,域名是通过DNS(域名服务器)转换成ip地址使用的,InetAddress.getByName()也可以直接传ip地址使用。

因为InetAddress.getByAddress()传入的是一个Byte类型的数组,对于ipv4只能是4字节长,对于ipv6只能是12字节长。此外因为Byte类型是从-128~127,对于ipv4点分十进制大于127的数字要强制转换一下。

认识Socket

简述

网络上的通信是进程和进程之间的通信,在网际层有ip地址,在传输层确定用TCP还是UDP传输数据,在应用层确定更精确的通信协议和标识进程的端口号。

Socket本质是提供给程序员开发用的网络编程API,从描述ip地址和端口角度来看,也可以称套接字(端口号和ip地址的组合)。

在Java里,客户端用Socket类建立连接到服务器的Socket对象,服务端用ServerSocket类的对象的.accept()方法建立与客户端相连接的服务端的Socket对象,两者才能通信。

端口号从1~65535(0保留),自己使用时,应使用1024~65535,前面的端口被实现定义的服务使用。

客户端首先需要知道服务器的地址和端口号,反之则不需要。服务器也可以从收到的户端通信消息中获取其地址信息。

两类阻塞

①服务器端的ServerSocket对象调用.accept()方法会阻塞本线程的执行,直到接收到客户端Socket的呼叫,才能建立连接并返回服务器端Socket对象。

②客户端和服务端两者的Socket建立连接后,各自使用.getOutputStream()获得输出流,使用.getInputStream()获得输入流,一方的输入流作为另一方的输出流(连接的实质):
这里写图片描述
不同于文件的读写,在Socket通信时,读数据可能发生在另一端输出数据之前,这时会阻塞本线程,直到这个读方法读到信息。

有关InetAddress

服务器端的Socket对象通过.getInetAddress()获取客户端的InetAddress对象;客户端的Socket对象通过同样的方法获取服务端的InetAddress对象。

程序

服务端MyServer.java

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;

//服务端程序
public class MyServer {

    public static void main(String[] args) {
        ServerSocket ss;// 服务器端连接对象
        Socket sck;// (服务端)Socket对象
        DataInputStream dis;// 数据输入流
        DataOutputStream dos;// 数据输出流
        String[] strs = { "服务端发送的信息1", "服务端发送的信息2", "服务端发送的信息3" };
        try {
            // 建立服务器端连接对象,使用3838端口
            ss = new ServerSocket(3838);
            System.out.println("[+]等待客户端申请连接...");
            sck = ss.accept();// 阻塞,直到连接成功,返回(服务端)Socket对象
            System.out.println("[+]客户端已经连接!");
            // 用.getOutputStream()建立输出流对象
            dos = new DataOutputStream(sck.getOutputStream());
            // 用.getInputStream()建立输入流对象
            dis = new DataInputStream(sck.getInputStream());
            for (String s : strs) {
                // 从输入流中读取信息,即读取了客户端的输出流中的信息
                String readstr = dis.readUTF();
                System.out.println("[+]" + readstr);
                dos.writeUTF(s);// 把要输出给客户端的信息,写入输出流
                Thread.sleep(2000);// 线程休眠2秒
            }
        } catch (IOException e) {
            System.out.println("[-]客户端提前断开");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("[-]交互结束");
    }

}

客户端MyClient.java

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;
import java.net.UnknownHostException;

//客户端程序
public class MyClient {

    public static void main(String[] args) {
        Socket sck;// (客户端)Socket对象
        String[] strs = { "客户端发送的信息1", "客户端发送的信息2", "客户端发送的信息3" };
        DataInputStream dis;// 数据输入流
        DataOutputStream dos;// 数据输出流

        try {
            // 客户端的Socket需要用构造方法来显式构造
            // 需要提供域名和端口号,端口号需要和服务端一致
            sck = new Socket("127.0.0.1", 3838);
            System.out.println("[+]成功连接到服务端");
            // 用.getOutputStream()建立输出流对象
            dos = new DataOutputStream(sck.getOutputStream());
            // 用.getInputStream()建立输入流对象
            dis = new DataInputStream(sck.getInputStream());
            for (String s : strs) {
                dos.writeUTF(s);// 把信息发送给服务端
                // 从输入流中读取信息,即读取了服务端的输出流中的信息
                String readstr = dis.readUTF();
                System.out.println("[+]" + readstr);
                Thread.sleep(1000);// 线程休眠1秒
            }
        } catch (UnknownHostException e) {
            System.out.println("[x]无法解析域名");
        } catch (IOException e) {
            System.out.println("[x]无法连接到服务端");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("[-]交互结束");
    }
}

输出(服务端)

[+]等待客户端申请连接...
[+]客户端已经连接!
[+]客户端发送的信息1
[+]客户端发送的信息2
[+]客户端发送的信息3
[-]交互结束

输出(客户端)

[+]成功连接到服务端
[+]服务端发送的信息1
[+]服务端发送的信息2
[+]服务端发送的信息3
[-]交互结束
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值