java网络编程之Socket

1. 建立Socket连接

1.1. 基本知识

TCP编程:
 1. 客户端/服务端(Client/Server)
 2. C/S架构

我们开发一种产品时,需要编写客户端程序和服务端程序,服务端程序放在服务器上,客户端程序给用户使用。

对于网络编程:一台电脑,代码也认为是多台电脑

Socket(网络套接字):可以认为是一条网络连接

ServerSocket:在指定端口建立监听,可以接收socket

Socket对象中可以得到输入流和输出流,直接使用即可,
 1. 输入流可以从网络对端接收数据,
 2. 输出流可以把数据发送到网络对端,

在使用过滤流的时候需要注意,C/S双方创建过滤流的顺序应该不同,否则会使程序发生阻塞。

DEMO
 1. DEMO1:服务器和客户端互相发送字符串
 2. DEMO2:服务器给客户端发送一个文件
 3. DEMO3:发送文件时支持断点续传

flush()方法
 1. 刷新缓冲区,强制缓冲区的数据流出

Flushable接口中定义了flush,OutputStream继承了这个接口
FileOutputStream究其根源,flush什么也没写

  BufferedOutputStream是一个过滤流,之所以可以缓冲,是因为内部定义了一个byte[],当byte[]达到一定大小的时候,统一写出去。
这个流中的flush有作用,强行把byte[]中的数据写出去。

ObjectOutputStream中也有缓冲区

close()会执行flush()

1.2. 代码示例

1.2.1. 客户端

package demo;

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

public class MyClient {

	public static void main(String[] args) throws UnknownHostException, IOException {
		/**
		 * 现在编写客户端,我们不需要关系自己客户端的地址
		 * 但是一定要知道服务器的地址,这样才能连接服务器
		 * 服务器开放的端口也是必须要知道的
		 */
		Socket ss = new Socket("192.168.1.100", 6767);
		System.out.println("服务器连接成功");
	}

}

1.2.2. 服务端

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

public class MyServer {
	
	public  static void main(String[] arg) throws IOException {
		//建立网络服务端对象,监听6767端口
		ServerSocket serv = new ServerSocket(6767);
		System.out.println("服务器启动成功,等待用户的接入");
		//等待用户接入,直到用户接入为止
		Socket sc = serv.accept();
		System.out.println("有客户端接入,客户IP:"+sc.getInetAddress());
	}
}

1.2.3. 运行

然后先运行服务端(服务器端)的程序,再运行客户端程序,效果如下:
 1. 服务端运行效果

在这里插入图片描述

 2. 客户端运行效果

在这里插入图片描述

这也验证了一句话: 对于网络编程:一台电脑,代码也认为是多台电脑。

2. 、Socket基本流数据传输

2.1. 示意图

在这里插入图片描述

2.2. 基本流程序示例

服务端工程程序

public class MyServer {
	public  static void main(String[] arg) throws IOException {
		//建立网络服务端对象,监听6767端口
		ServerSocket serv = new ServerSocket(6767);
		System.out.println("服务器启动成功,等待用户的接入");
		//等待用户接入,直到用户接入为止
		Socket sc = serv.accept();
		System.out.println("有客户端接入,客户IP:"+sc.getInetAddress());
		
		//从socket中得到网络输入流,接收来自网络的数据
		InputStream in = sc.getInputStream();
		//从socket中得到网络输出流,把数据发送到网络上
		OutputStream out = sc.getOutputStream();
		
		//接收缓冲区
		byte[] b = new byte[1024];
		//接收数据
		int len = in.read(b);
		String str = new String(b, 0, len);
		System.out.println("来自客户端的消息是: "+ str);
		
		//往客户端发送消息
		out.write("你好,我是服务端,欢迎光临".getBytes());
		
		sc.close();
	}
}

客户端工程程序

public class MyClient {
	public static void main(String[] args) throws UnknownHostException, IOException {
		/**
		 * 现在编写客户端,我们不需要关系自己客户端的地址
		 * 但是一定要知道服务器的地址,这样才能连接服务器
		 * 服务器开放的端口也是必须要知道的
		 */
		Socket ss = new Socket("192.168.1.100", 6767);
		System.out.println("服务器连接成功");
		
		//从socket中得到网络输入流,接收来自网络的数据
		InputStream in = ss.getInputStream();
		//从socket中得到网络输出流,把数据发送到网络上
		OutputStream out = ss.getOutputStream();
		
		out.write("你好,我是客户端".getBytes());
		
		//接收来自服务端的信息
		//接收缓冲区
		byte[] b = new byte[1024];
		//接收数据
		int len = in.read(b);
		String str = new String(b, 0, len);
		System.out.println("来自服务端的消息是: "+ str);
		
		ss.close();
	}
}

程序运行结果
 1. 服务端程序的运行结果:

在这里插入图片描述

客户端程序的运行结果:

在这里插入图片描述

3. Socket过滤流数据传输

过滤流属于高级流,过滤流可以写对象,万物皆对象

3.1. 高级流程序示例

3.1.1. 服务端程序:

public class MyServer {
    public  static void main(String[] arg) throws IOException, ClassNotFoundException {
        //建立网络服务端对象,监听6767端口
        ServerSocket serv = new ServerSocket(6767);
        System.out.println("服务器启动成功,等待用户的接入");
        //等待用户接入,直到用户接入为止
        Socket sc = serv.accept();
        System.out.println("有客户端接入,客户IP:"+sc.getInetAddress());

        //从socket中得到网络输入流,接收来自网络的数据
        InputStream in = sc.getInputStream();
        //从socket中得到网络输出流,把数据发送到网络上
        OutputStream out = sc.getOutputStream();

        //创建过滤流,注意服务端和客户端的顺序不同
        ObjectInputStream ois = new ObjectInputStream(in);
        ObjectOutputStream oos = new ObjectOutputStream(out);

        //接收来自客户端的消息
        String s1 = (String)ois.readObject();
        System.out.println(s1);

        //往客户端发送消息
        oos.writeObject("你好,我是服务端,很霸气的!");

        sc.close();
    }
}

3.1.2. 客户端程序

public class MyClient {
	public static void main(String[] args) throws UnknownHostException, IOException, ClassNotFoundException {
    /**
     * 现在编写客户端,我们不需要关系自己客户端的地址
     * 但是一定要知道服务器的地址,这样才能连接服务器
     * 服务器开放的端口也是必须要知道的
     */
    Socket ss = new Socket("192.168.1.100", 6767);
    System.out.println("服务器连接成功");

    //从socket中得到网络输入流,接收来自网络的数据
    InputStream in = ss.getInputStream();
    //从socket中得到网络输出流,把数据发送到网络上
    OutputStream out = ss.getOutputStream();

    //创建过滤流,注意服务端和客户端的顺序不同
    ObjectOutputStream oos = new ObjectOutputStream(out);
    ObjectInputStream ois = new ObjectInputStream(in);

    //往服务端发送消息
    oos.writeObject("你好,我是客户端,很帅很帅");

    //接收来自服务端的消息
    String s1 = (String)ois.readObject();
    System.out.println(s1);
    ss.close();
    }
}

程序运行结果:
 1. 服务端程序运行结果

在这里插入图片描述

 2. 客户端程序运行结果

在这里插入图片描述

3.2. 高级流注意事项

创建过滤流(高级流)时,服务端和发送端的顺序应该不同,如果相同,数据将不能互相发送。

在这里插入图片描述

4. Socket传文件

4.1. 文件传输流程

示意图

在这里插入图片描述

  本示意图只是演示了文件从服务端发送到客户端过程,从客户端到服务端的原理一样,电脑A本地文件<—>JVM<—>网络通道<—>电脑B本地文件<—>JVM<—>网络通道。

4.2. 发送端

服务器给客户端发送一个文件。

网络服务器工程程序示例:

public class FileServer {
	public static void main(String[] args) throws IOException {
		//监听12345端口
		ServerSocket ser = new ServerSocket(12345);
		System.out.println("等待用户接入服务器!!!");
		
		//等待用户接入,知道用户接入为止
		Socket sc = ser.accept();
		//获取sc的IO流
		InputStream in = sc.getInputStream();
		OutputStream out = sc.getOutputStream();
		//为了简化操作,创建过滤流
		ObjectInputStream ois = new ObjectInputStream(in);
		ObjectOutputStream oos = new ObjectOutputStream(out);
		
		//获取服务器端的本地文件信息
		File f1 = new File("F:\\abc\\Xshell_Plus_v6.0.0095.7z");
		//获取文件的IO流
		//创建输入流,把文件读取到java的jvm中
		InputStream FileIn = new FileInputStream(f1);
		//发送文件名
		oos.writeObject(f1.getName());
		
		/*发送文件*/
		//创建128k缓冲区,用于缓冲本地文件
		byte[] b = new byte[1024*128];
		
		for(int len = 0;(len = FileIn.read(b)) != -1;) {
			//将数据写入到网络中
			out.write(b, 0, len); 
		}
		
		System.out.println("文件发送完成!!!");
		//关闭网络流
		sc.close();
		//关闭文件流
		FileIn.close();
	}
}

4.3. 接收端

客户端接收服务端发送的一个文件。

网络客户端工程程序示例:

public class FileClient {
	public static void main(String[] args) throws UnknownHostException, IOException, ClassNotFoundException {

		//通过socket(网络通道),连接服务器
		Socket sc = new Socket("192.168.1.100", 12345);
		System.out.println("成功连接服务器.......");
		
		//从网络套字节socket获取,网络IO流
		OutputStream out = sc.getOutputStream();
		InputStream in = sc.getInputStream();
		
		//创建过滤流
		//注意,服务端与客户端的顺序相反
		ObjectOutputStream oos = new ObjectOutputStream(out);
		ObjectInputStream ois = new ObjectInputStream(in);
		
		//接收文件名
		String FileName = (String)ois.readObject();
		//获取本地存放的目录信息
		File f1 = new File("F:\\download\\"+FileName);
		//创建文件输出流
		OutputStream FileOut = new FileOutputStream(f1);
		
		System.out.println("开始接收文件"+ FileName);
		//从网络接收文件的缓冲区
		byte[] b= new byte[1024*128];
		//将文件写入到本地中
		for(int len = 0; (len = in.read(b)) != -1 ;) {
			//从JVM中读取数据
			FileOut.write(b, 0, len);
		}
		//关闭网络流
		sc.close();
		//关闭文件流
		FileOut.close();
		System.out.println("接收文件完成 ");
	}
}

4.4. 断点续传

  需要服务端和客户端进行协商,首先客户端要判断文件的大小,将文件的大小传给服务器,然后服务器进行响应,xc代表续传,xin重头开始传,bu不传。

其实重头开始传和续传对于客户端是一样的,只不过重传的跳过文件比特大小为0。

  本程序如果想要演示断点续传,可以利用360安全软件(功能大全->网络优化->360流量防火墙)限制java的虚拟机速度(前提是要运行java程序,才会启用虚拟机javaw),当传一部分数据时,关闭java程序的运行,再次运行程序,查看是否可以断点续传。

在这里插入图片描述

客户端程序

public class FileClient {
	public static void main(String[] args) throws UnknownHostException, IOException, ClassNotFoundException {
		//通过socket(网络通道),连接服务器
		Socket sc = new Socket("192.168.1.100", 12345);
		System.out.println("成功连接服务器.......");
		
		//从网络套字节socket获取,网络IO流
		OutputStream out = sc.getOutputStream();
		InputStream in = sc.getInputStream();
		
		//创建过滤流
		//注意,服务端与客户端的顺序相反
		ObjectOutputStream oos = new ObjectOutputStream(out);
		ObjectInputStream ois = new ObjectInputStream(in);
		
		//接收文件名
		String FileName = (String)ois.readObject();
		//获取本地存放的目录信息
		File f1 = new File("F:\\download\\"+FileName);
		
		//将客户端本地文件的大小,传送给服务端
		//服务端进行响应,"xc"续传和重头开始传,"bc"不传
		oos.writeLong(f1.length());
		oos.flush();//刷新缓冲区,强制缓冲区数据流出
		
		System.out.println("本地文件大小:"+ f1.length());
		//获取服务器的响应
		String str = (String)ois.readObject();
		//根据服务器的响应,开始进行接收数据
		if("xc".equals(str)) {//断点续传和重传
			//创建文件输出流,从jvm输出到本地
			//true表示输出的数据追加到所接收的文件
			OutputStream FileOut = new FileOutputStream(f1, true);
			
			System.out.println("开始接收文件"+ FileName);
			//从网络接收文件的缓冲区
			byte[] b= new byte[1024*128];
			//将文件写入到本地中
			for(int len = 0; (len = in.read(b)) != -1 ;) {
				//从JVM中读取数据
				FileOut.write(b, 0, len);
			}
			//关闭网络流
			sc.close();
			//关闭文件流
			FileOut.close();
			System.out.println("接收文件完成 ");
		}else if("bu".equals(str)) {
			System.out.println("本地存在将下载文件");
		}
		
	}
}

服务端程序

public class FileServer {
	public static void main(String[] args) throws IOException {
		//监听12345端口
		ServerSocket ser = new ServerSocket(12345);
		System.out.println("等待用户接入服务器!!!");
		
		//等待用户接入,知道用户接入为止
		Socket sc = ser.accept();
		//获取sc的IO流
		InputStream in = sc.getInputStream();
		OutputStream out = sc.getOutputStream();
		//为了简化操作,创建过滤流
		ObjectInputStream ois = new ObjectInputStream(in);
		ObjectOutputStream oos = new ObjectOutputStream(out);
		
		//获取服务器端的本地文件信息
		File f1 = new File("F:\\abc\\Xshell_Plus_v6.0.0095.7z");
		//获取文件的IO流
		//创建输入流,把文件读取到java的jvm中
		InputStream FileIn = new FileInputStream(f1);
		//发送文件名
		oos.writeObject(f1.getName());
		
		//接收客户端的文件大小
		long overSize = ois.readLong();
		System.out.println("客户端的文件大小: " + overSize);
		
		if( f1.length() == overSize) {
			oos.writeObject("bu");
			System.out.println("客户端文件存在无需再传");
			
		}else if( overSize >= 0) { //断点续传和重传
			oos.writeObject("xc");
			System.out.println("服务端开始发送文件给客户端......");
			//跳过文件的大小为overSize 比特开始读取文件
			FileIn.skip(overSize);
			System.out.println("文件的起始位置: " + overSize);
			
			/*发送文件*/
			//创建128k缓冲区,用于缓冲本地文件
			byte[] b = new byte[1024*128];
			
			for(int len = 0;(len = FileIn.read(b)) != -1;) {
				//将数据写入到网络中
				out.write(b, 0, len); 
			}
			System.out.println("文件发送完成!!!");
			
			//关闭网络流
			sc.close();
			//关闭文件流
			FileIn.close();
		}
		
	}
}

5. 输出流中的flush方法

//通过socket(网络通道),连接服务器
Socket sc = new Socket("192.168.1.100", 12345);
//从网络套字节socket获取,网络IO流
OutputStream out = sc.getOutputStream();
//创建过滤流
//注意,服务端与客户端的顺序相反
ObjectOutputStream oos = new ObjectOutputStream(out);
oos.writeLong(f1.length());
oos.flush();

flush():刷新缓冲区,强制缓冲区数据流出

  当我们把数据写入到jvm的缓冲区中后,如果没有达到缓冲区大小数据不会从缓冲区中流出,只有达到缓冲区大小数据才会从缓冲区中流出,而flush()方法就是不管你的缓冲区是否满了,我都将强制缓冲区的数据流出。

Flushable接口中定义了flush(),OutputStream继承了这个接口。
 1. FileOutputStream中的flush什么也没写。

  BufferedOutputStream是一个过滤流,之所以缓冲,是因为内部定义了一个byte数组,当byte[]达到一定大小的时候,统一写出去。
 1. ObjectOutputStream中也有一个缓冲区。

执行close()方法也会执行flush()方法。

基本流没有缓冲区。
 1. 这个流中的flush有作用,强行把byte[]中的数据写出去。

已标记关键词 清除标记
相关推荐
©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页