网络编程

网络编程笔记

利用 协议 + IP地址 + 端口号 三元组合,就可以标识网络中的进程了,那么进程间的通信就可以利用这个标识与其
它进程进行交互。
服务器必须明确两件事情:
1.多个客户端同时和服务器进行交互,服务器必须明确和哪个客户端进行交互
在服务器端有一个方法accept,客户端获取到请求的客户端对象
2.多个客户端同时和服务器进行交互,需要使用多个IO流对象
服务器是没有IO流的,可以获取到请求的客户端对象Socket,使用每个客户端Socket中提供的IO流和客户端进行交互

客户端向服务器发送数据

服务端实现:

public class ServerTCP {
public static void main(String[] args) throws IOException {
		System.out.println("服务端启动 , 等待连接 .... ");
		// 1.创建 ServerSocket对象,绑定端口,开始等待连接
		ServerSocket ss = new ServerSocket(6666);
		// 2.接收连接 accept 方法, 返回 socket 对象.
		Socket server = ss.accept();
		// 3.通过socket 获取输入流
		InputStream is = server.getInputStream();
		// 4.一次性读取数据
		// 4.1 创建字节数组
		byte[] b = new byte[1024];
		// 4.2 据读取到字节数组中.
		int len = is.read(b)// 4.3 解析数组,打印字符串信息
		String msg = new String(b, 0, len);
		System.out.println(msg);
		//5.关闭资源.
		is.close();
		server.close();
	}
}

客户端实现:

public class ServerTCP {
public static void main(String[] args) throws IOException {
		System.out.println("服务端启动 , 等待连接 .... ");
		// 1.创建 ServerSocket对象,绑定端口,开始等待连接
		ServerSocket ss = new ServerSocket(6666);
		// 2.接收连接 accept 方法, 返回 socket 对象.
		Socket server = ss.accept();
		// 3.通过socket 获取输入流
		InputStream is = server.getInputStream();
		// 4.一次性读取数据
		// 4.1 创建字节数组
		byte[] b = new byte[1024];
		// 4.2 据读取到字节数组中.
		int len = is.read(b)// 4.3 解析数组,打印字符串信息
		String msg = new String(b, 0, len);
		System.out.println(msg);
		// =================回写数据=======================
		// 5. 通过 socket 获取输出流
		OutputStream out = server.getOutputStream();
		// 6. 回写数据
		out.write("我很好,谢谢你".getBytes());
		// 7.关闭资源.
		out.close();
		is.close();
		server.close();
	}
}

文档上传案例阻塞问题:

FileInputStream的read方法,如果没有输入可用,会处于阻塞状态。写法不会读到-1,即文件结束标记,就不会把结束标记回写给服务器,此后服务器的应答和客户端的接收以及资源的关闭都不会执行到,因此程序一直处于运行状态。
![在这里插入图片描述](https://img-blog.csdnimg.cn/20200917014524359.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQ0MzA0NjQ4,size_16,color_FFFFFF,t_70#pic_center
解决:上传完文件,给服务器写一个结束标记
socket.void shutdownOutput() 禁用此套接字的输出流。
对于 TCP 套接字,任何以前写入的数据都将被发送,并且后跟 TCP 的正常连接终止序列。

文件上传优化

  1. 文件名称写死的问题
    自定义一个文件的命名规则:防止同名的文件被覆盖
    规则:域名+毫秒值+随机数 String fileName = “itcast”+System.currentTimeMillis()+new Random().nextInt(999999)+".jpg";
    服务端,保存文件的名称如果写死,那么最终导致服务器硬盘,只会保留一个文件,建议使用系统时间优
    化,保证文件名称唯一,代码如下:
FileOutputStream fis = new FileOutputStream(System.currentTimeMillis()+".jpg") // 文件名称
BufferedOutputStream bos = new BufferedOutputStream(fis);
  1. 循环接收的问题
    服务端,指保存一个文件就关闭了,之后的用户无法再上传,这是不符合实际的,使用循环改进,可以不断
    的接收不同用户的文件,代码如下:
// 每次接收新的连接,创建一个Socket
whiletrue{
Socket accept = serverSocket.accept();
......
}
  1. 效率问题
    服务端,在接收大文件时,可能耗费几秒钟的时间,此时不能接收其他用户上传,所以,使用多线程技术优
    化,代码如下:
whiletrue{
	Socket accept = serverSocket.accept();
	// accept 交给子线程处理.
	new Thread(()> {
	......
	InputStream bis = accept.getInputStream();
	......
	}).start();
}

优化实现

public class FileUpload_Server {
public static void main(String[] args) throws IOException {
	System.out.println("服务器 启动..... ");
	// 1. 创建服务端ServerSocket
	ServerSocket serverSocket = new ServerSocket(6666);
	// 2. 循环接收,建立连接
	while (true) {
		Socket accept = serverSocket.accept();
		/*
		3. socket对象交给子线程处理,进行读写操作
			Runnable接口中,只有一个run方法,使用lambda表达式简化格式
		*/
		new Thread(()> {
			try (
				//3.1 获取输入流对象
				BufferedInputStream bis = new BufferedInputStream(accept.getInputStream());
				//3.2 创建输出流对象, 保存到本地 .
				FileOutputStream fis = new FileOutputStream(System.currentTimeMillis() +".jpg");
				BufferedOutputStream bos = new BufferedOutputStream(fis);) {
				// 3.3 读写数据
				byte[] b = new byte[1024 * 8];
				int len;
				while ((len = bis.read(b)) !=1) {
					bos.write(b, 0, len);
				}
				//4. 关闭 资源
				bos.close();
				bis.close();
				accept.close();
				System.out.println("文件上传已保存");
				} catch (IOException e) {
					e.printStackTrace();
				}
			}).start();
		}
	}
}

模拟B/S服务器分析

在这里插入图片描述
服务端实现:

/*
    创建BS版本TCP服务器
 */
public class TCPServerThread {
    public static void main(String[] args) throws IOException {
        //创建一个服务器ServerSocket,和系统要指定的端口号
        ServerSocket server = new ServerSocket(8080);

        /*
            浏览器解析服务器回写的html页面,页面中如果有图片,那么浏览器就会单独的开启一个线程,读取服务器的图片
            我们就的让服务器一直处于监听状态,客户端请求一次,服务器就回写一次
         */
        while(true){
            //使用accept方法获取到请求的客户端对象(浏览器)
            Socket socket = server.accept();

            new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        //使用Socket对象中的方法getInputStream,获取到网络字节输入流InputStream对象
                        InputStream is = socket.getInputStream();
                        //使用网络字节输入流InputStream对象中的方法read读取客户端的请求信息
                        /*byte[] bytes = new byte[1024];
                        int len = 0;
                        while((len = is.read(bytes))!=-1){
                            System.out.println(new String(bytes,0,len));
                        }*/

                        //把is网络字节输入流对象,转换为字符缓冲输入流
                        BufferedReader br = new BufferedReader(new InputStreamReader(is));
                        //把客户端请求信息的第一行读取出来 GET /11_Net/web/index.html HTTP/1.1
                        String line = br.readLine();
                        System.out.println(line);
                        //把读取的信息进行切割,只要中间部分 /11_Net/web/index.html
                        String[] arr = line.split(" ");
                        //把路径前边的/去掉,进行截取 11_Net/web/index.html
                        String htmlpath = arr[1].substring(1);

                        //创建一个本地字节输入流,构造方法中绑定要读取的html路径
                        FileInputStream fis = new FileInputStream(htmlpath);
                        //使用Socket中的方法getOutputStream获取网络字节输出流OutputStream对象
                        OutputStream os = socket.getOutputStream();

                        // 写入HTTP协议响应头,固定写法
                        os.write("HTTP/1.1 200 OK\r\n".getBytes());
                        os.write("Content-Type:text/html\r\n".getBytes());
                        // 必须要写入空行,否则浏览器不解析
                        os.write("\r\n".getBytes());

                        //一读一写复制文件,把服务读取的html文件回写到客户端
                        int len = 0;
                        byte[] bytes = new byte[1024];
                        while((len = fis.read(bytes))!=-1){
                            os.write(bytes,0,len);
                        }

                        //释放资源
                        fis.close();
                        socket.close();
                    }catch (IOException e){
                        e.printStackTrace();
                    }
                }
            }).start();


        }


        //server.close();
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值