2020-12-10

1 网络编程

1.1 网络协议

http协议的里程:

http0.9版本:短链接、只支持GET请求、响应的数据格式只能是html的字符串

http1.0版本:短链接、可支持GET请求、POST请求,增加了状态码、响应的数据格式多元化(数字、字符串、图片、视频等)

http1.1版本:长链接、可支持GET、POST、DELETE、PUT等请求,增加100状态码等

面试题:tcp/ip和udp/ip协议的区别

1.udp是无状态传输协议,不保证安全,容易丢包

2.tcp协议在传输之前会先进行三次握手确认,保证数据的安全性

3.tcp传输慢,效率低,udp效率高,传输快

1.2 InetAddress和InetSocketAddress

InetAddress是Java对IP地址相关信息的封装,代表互联网协议(IP)地址,是构建UDP和TCP协议的低级协议.

public static void main(String[] args) throws UnknownHostException {
    //0 获取InetAddress对象
    InetAddress address = InetAddress.getLocalHost();
    System.out.println("主机名:"+address.getHostName());
    System.out.println("主机地址:"+address.getHostAddress());

    InetAddress address2 = InetAddress.getByName("WIN10-008051646");
    System.out.println("主机名:"+address2.getHostName());
    System.out.println("主机地址:"+address2.getHostAddress());

    InetAddress address3 = InetAddress.getByName("172.16.6.100");
    System.out.println("主机名:"+address3.getHostName());
    System.out.println("主机地址:"+address3.getHostAddress());
}

思考:一台主机上会有很多不同的网络服务(多个web服务、ftp服务等),主机要如何进行区分呢?

通常是通过IP地址+端口号进行区分

端口号范畴:

取值范围为 0 ~ 65535 ,其中0~1023的端口号为系统所保留,例如http服务的端口号为80,telnet服务的端口号为21,ftp服务的端口号为23

InetSocketAddress是SocketAddress的子类,用于实现IP套接字地址(IP+端口号),不依赖任何协议;

public static void main(String[] args) throws UnknownHostException {
    InetSocketAddress socketAddress = new InetSocketAddress("172.16.6.100",8899);
    System.out.println("主机名:"+socketAddress.getHostName());
    System.out.println("端口号:"+socketAddress.getPort());
    //0 转换为InetAddress用来获取主机地址
    InetAddress addr = socketAddress.getAddress();
    System.out.println("主机地址:"+addr.getHostAddress());
}

两者间的区别:

属性InetAddressInetSocketAddress
描述对象IP地址Socket地址(IP地址+端口)
描述IP和主机对象名称IP和主机的对象名称,并包括端口号
解决问题IP到主机名称,主机名称到IPIP到主机名称,主机名称到IP,可以包含端口
获取对象InetAddress.getLocalhost(); InetAddress.getByName(String); InetAddress.getByAddress(String);InetSocketAddress.createUnresolved(String, port);
1.3Socket编程

Socket(套接字)可以看成是两个网络应用程序进行通信时,各自通信连接中的端点,这是一个逻辑上的概念

实现socket编程需要有客户端(client)及服务端(server)

数据传输其实就是客户端到服务端,服务端到客户端的一个过程,数据是以流的形式进行传输的

示例1:使用Socket实现聊天功能

创建服务端

public class Server {

	public static void main(String[] args) throws IOException {
		//定义一个端口号作为标识 0-65535,1023以下的不建议使用
		int port = 8888;
		//创建一个服务端的对象--用于接收客户端传递的信息
		ServerSocket serverSocket = new ServerSocket(port);
		Socket socket = null;
		InputStream inputStream = null;
		InputStreamReader isr = null;
		BufferedReader br = null;
		try {
			while(true) {
				//监听客户端发送的请求
				socket = serverSocket.accept();
				//读取传递的数据,需要有流对象
				inputStream = socket.getInputStream();
				//转换为字符流对象再包装成字符缓冲流对象
				isr = new InputStreamReader(inputStream);
				br = new BufferedReader(isr);
				String line = null;
				while((line=br.readLine())!=null) {
					System.out.println("接收到客户端信息:"+line);
					if(line.equals("over")) {
						break;
					}
				}
			}
		} catch (IOException e) {
			e.printStackTrace();
		}finally {
			if(br!=null) {
				br.close();
			}
			if(isr!=null) {
				isr.close();
			}
			if(inputStream!=null) {
				inputStream.close();
			}
			if(socket!=null) {
				socket.close();
			}
		}
	}
}

//创建客户端
public class Client {

	public static void main(String[] args) {
		int port = 8888;
		String host = "127.0.0.1";
		//创建Socket对象指定请求的地址及端口号
		Socket socket = null;
		OutputStream os = null;
		OutputStreamWriter osw = null;
		try {
			while(true) {
				socket = new Socket(host, port);
				//通过socket获取输出流对象
				os = socket.getOutputStream();
				//转换为字符流
				osw = new OutputStreamWriter(os);
				//从控制台获取用户输入的内容
				Scanner scanner = new Scanner(System.in);
				String content = scanner.nextLine();
				//以流的形式进行写动作
				osw.write(content);
				osw.flush();
				socket.shutdownOutput();
				if(content.equals("under line")) {
					break;
				}
			}
		} catch (UnknownHostException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}finally {
			try {
				if(osw!=null) {
					osw.close();
				}
				if(os!=null) {
					os.close();
				}
				if(socket!=null) {
					socket.close();
				}
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		
	}
}

示例2:使用Socket实现文件传输功能

思路:

//客户端
private static final String SERVER_IP = "127.0.0.1"; // 服务端IP
private static final int SERVER_PORT = 8899; // 服务端端口

public static void main(String[] args) throws IOException {
    Socket client = null;
    FileInputStream fis = null;
    DataOutputStream dos = null;
    DecimalFormat format = new DecimalFormat("#.00");
    try {
        client = new Socket(SERVER_IP, SERVER_PORT);
        File file = new File("E:\\Java课堂\\课堂资料\\Java\\录屏\\day01\\1、java环境配置.mp4");
        if (file.exists()) {
            fis = new FileInputStream(file);
            dos = new DataOutputStream(client.getOutputStream());

            // 文件名和长度
            dos.writeUTF(file.getName());
            dos.flush();
            dos.writeLong(file.length());
            dos.flush();

            // 开始传输文件
            System.out.println("======== 开始传输文件 ========");
            byte[] bytes = new byte[1024 * 1024];
            int length = 0;
            long progress = 0;
            while ((length = fis.read(bytes, 0, bytes.length)) != -1) {
                dos.write(bytes, 0, length);
                dos.flush();
                progress += length;
                System.out.println((100 * progress / file.length()) + "%");
                Thread.sleep(100);
            }
            System.out.println();
            System.out.println("======== 文件传输成功 ========");
        }
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        if (fis != null)
            fis.close();
        if (dos != null)
            dos.close();
        client.close();
    }
}
//服务端
public class Server extends ServerSocket{
	private static final int SERVER_PORT = 8899; // 服务端端口
	 
    private static DecimalFormat df = null;
 
    static {
        // 设置数字格式,保留一位有效小数
        df = new DecimalFormat("#0.0");
        df.setRoundingMode(RoundingMode.HALF_UP);
        df.setMinimumFractionDigits(1);
        df.setMaximumFractionDigits(1);
    }
 
    public Server() throws Exception {
        super(SERVER_PORT);
    }
 
    /**
     * 	使用线程处理每个客户端传输的文件
     * @throws Exception
     */
    public void load() throws Exception {
        while (true) {
            // server尝试接收其他Socket的连接请求,server的accept方法是阻塞式的
            Socket socket = this.accept();
            /**
		             * 我们的服务端处理客户端的连接请求是同步进行的, 每次接收到来自客户端的连接请求后,
		             * 都要先跟当前的客户端通信完之后才能再处理下一个连接请求。 这在并发比较多的情况下会严重影响程序的性能,
		             * 为此,我们可以把它改为如下这种异步处理与客户端通信的方式
             */
            // 每接收到一个Socket就建立一个新的线程来处理它
            new Thread(new Task(socket)).start();
        }
    }
 
    /**
     * 	处理客户端传输过来的文件线程类
     */
    class Task implements Runnable {
 
        private Socket socket;
 
        private DataInputStream dis;
 
        private FileOutputStream fos;
 
        public Task(Socket socket) {
            this.socket = socket;
        }
 
        @Override
        public void run() {
            try {
                dis = new DataInputStream(socket.getInputStream());
 
                // 文件名和长度
                String fileName = dis.readUTF();
                long fileLength = dis.readLong();
                File directory = new File("E:\\Cache");
                if(!directory.exists()) {
                    directory.mkdir();
                }
                File file = new File(directory.getAbsolutePath() + File.separatorChar + fileName);
                fos = new FileOutputStream(file);
 
                // 开始接收文件
                byte[] bytes = new byte[1024];
                int length = 0;
                while((length = dis.read(bytes, 0, bytes.length)) != -1) {
                    fos.write(bytes, 0, length);
                    fos.flush();
                }
                System.out.println("======== 文件接收成功 [File Name:" + fileName + "] [Size:" + getFormatFileSize(fileLength) + "] ========");
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                try {
                    if(fos != null)
                        fos.close();
                    if(dis != null)
                        dis.close();
                    socket.close();
                } catch (Exception e) {}
            }
        }
    }
 
    /**
     * 	格式化文件大小
     * @param length
     * @return
     */
    private String getFormatFileSize(long length) {
        double size = ((double) length) / (1 << 30);
        if(size >= 1) {
            return df.format(size) + "GB";
        }
        size = ((double) length) / (1 << 20);
        if(size >= 1) {
            return df.format(size) + "MB";
        }
        size = ((double) length) / (1 << 10);
        if(size >= 1) {
            return df.format(size) + "KB";
        }
        return length + "B";
    }
 
    /**
     * 	入口
     * @param args
     */
    public static void main(String[] args) {
        try {
            Server server = new Server(); // 启动服务端
            server.load();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值