[心得] java socket短连接实现客户端和服务器的文件发送与存储

 一、java socket短连接的基本概念

1.socket套接字

TCP用主机的IP地址加上主机上的端口号作为TCP连接的端点,这种端点就叫做套接字(socket)或插口。

即TCP/IP实现的套接字,是应用层调用下层服务的接口。

备注:若接口被占用,打开cmd窗口,输入netstat -ano,查找端口号对应的PID;打开任务管理器,详细信息,结束对应PID的进程,即可。

 

2.短连接:连接->传输数据->关闭连接

短连接(short connnection)是相对于长连接而言的概念,指的是在数据传送过程中,只在需要发送数据时,才去建立一个连接,数据发送完成后,则断开此连接,即每次连接只完成一项业务的发送。

优点:不需要长期占用通道,对于业务频率不高的场合,能节省通道的使用。

缺点:需要在每次发送业务时,都要建立一次连接,连接建立的过程开销大。

最常见的短连接例子就是HTTP协议。


长连接 :连接->传输数据->保持连接 -> 传输数据-> 。。。 ->关闭连接 
长连接指建立socket连接后不管是否使用都保持连接,但安全性较差。 

 

JAVA如何实现短连接:(我理解为socket相当于一个快递点或可以说是中转站)

①发送的方法:直接获得socket的OutputStream流,然后用write方法,flush方法即可。

②接收的方法:常用的两个类是InputStream和BufferedInputStream。

 

二、实现文件发送与存储的步骤

1.要求: 传输文件:即将客户端的文件传输到服务器 

2.步骤: 

客户端

①创建客户端Socket对象,指定服务器地址和端口;

②创建socket输出流(方便往socket写数据): 

    os = socket.getOutputStream(); 

③读取文件读到byte[]数组中:byte[] bs = new byte[1024];

    is = new  FileInputStream(new File("D:\\新建文本文档1.txt"));

    再将数组中的数据通过os写入socket:

    os.write(bs,0,num);

服务器端

①创建服务器端ServerSocket对象,指定端口:

    serverSocket = new ServerSocket(9999);

②调用accept()监听(即等待客户端的连接)来接收一个socket:

    socket = serverSocket.accept();

③创建socket输入流(读取socket中的数据):

 is = socket.getInputStream();

 创建文件输出流(方便把从socket读取的数据写入文件):

 fos = new FileOutputStream(new File("D:\\新建文本文档2.txt"));

 把从socket读取的数据写入文件:

 fos.write(bs, 0, num);

 

三、Socket类的getInputStream方法与getOutputStream方法的使用

客户端上的使用:

①getInputStream方法可以得到一个输入流,客户端的Socket对象上的getInputStream方法得到输入流其实就是从服务器端发回的数据;

getOutputStream方法得到的是一个输出流,客户端的Socket对象上的getOutputStream方法得到的输出流其实就是发送给服务器端的数据;

服务器端上的使用:

getInputStream方法得到的是一个输入流,服务端的Socket对象上的getInputStream方法得到的输入流其实就是从客户端发送给服务器端的数据流;

②getOutputStream方法得到的是一个输出流,服务端的Socket对象上的getOutputStream方法得到的输出流其实就是发送给客户端的数据; 

 

四、代码

客户端

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.net.UnknownHostException;

public class Client {
    public static void main(String[] args) {
        // 1 创建客户端Socket对象,指定服务器地址和端口
        Socket socket   = null;
        OutputStream os = null;
        InputStream is  = null;
        try {
             socket = new Socket("localhost", 9999);
            // 2 创建socket输出流(方便往socket写数据)
             os = socket.getOutputStream();
            // 3 读取文件读到byte[]数组中:    byte[] bs = new byte[1024];
             is =new  FileInputStream(new File("D:\\新建文本文档1.txt"));
            byte[] bs = new byte[1024];
            int num = 0;
            while((num = is.read(bs))!=-1){
                // 再将数组中的数据通过os写入socket
                os.write(bs,0,num);
                os.flush();
            }
            System.out.println("客户端发送完毕!");
        } catch (UnknownHostException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
        	try {
				is.close();
				os.close();
				socket.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
        }
    }
}

服务器端

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;

public class Server {

    public static void main(String[] args) {
        // 1 创建服务器端ServerSocket对象,指定端口
        ServerSocket serverSocket =null;
        Socket socket = null;
        InputStream is  = null;
        FileOutputStream fos = null;
        try {
             serverSocket = new ServerSocket(9999);
            // 2 调用accept()监听(即等待客户端的连接)
             socket = serverSocket.accept();
            // 3 创建socket输入流(读取socket中的数据)
             is  = socket.getInputStream();
             //  创建文件输出流,方便把从socket读取的数据写入文件
             fos = new FileOutputStream(new File("D:\\新建文本文档2.txt"));
            byte[] bs = new byte[1024];
            int num = 0;
            while((num = is.read(bs))!=-1){
            	//把从socket读取的数据写入文件
                fos.write(bs, 0, num);
                fos.flush();
            }
            System.out.println("服务端接收完毕!");
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
        	try {
				is.close();
				fos.close();
				socket.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
        }
    }
}

五、多线程

关于进程与线程的讲解 最最最生动的理解
       进程(process)和线程(thread)是操作系统的基本概念,但是它们比较抽象,不容易掌握。我发现有一个很好的类比,可以把它们解释地清晰易懂。
       计算机的核心是CPU,它承担了所有的计算任务。它就像一座工厂,时刻在运行。
假定工厂的电力有限,一次只能供给一个车间使用。也就是说,一个车间开工的时候,其他车间都必须停工。背后的含义就是,单个CPU一次只能运行一个任务。
       进程就好比工厂的车间,它代表CPU所能处理的单个任务。任一时刻,CPU总是运行一个进程其他进程处于非运行状态。
       一个车间里,可以有很多工人。他们协同完成一个任务。
       线程就好比车间里的工人。一个进程可以包括多个线程。
       车间的空间是工人们共享的,比如许多房间是每个工人都可以进出的。这象征一个进程的内存空间是共享的,每个线程都可以使用这些共享内存。
       可是,每间房间的大小不同,有些房间最多只能容纳一个人,比如厕所。里面有人的时候,其他人就不能进去了。这代表一个线程使用某些共享内存时,其他线程必须等它结束,才能使用这一块内存。
       一个防止他人进入的简单方法,就是门口加一把锁。先到的人锁上门,后到的人看到上锁,就在门口排队,等锁打开再进去。这就叫"互斥锁"(Mutual exclusion,缩写 Mutex),防止多个线程同时读写某一块内存区域。
       还有些房间,可以同时容纳n个人,比如厨房。也就是说,如果人数大于n,多出来的人只能在外面等着。这好比某些内存区域,只能供给固定数目的线程使用。
       这时的解决方法,就是在门口挂n把钥匙。进去的人就取一把钥匙,出来时再把钥匙挂回原处。后到的人发现钥匙架空了,就知道必须在门口排队等着了。这种做法叫做"信号量"(Semaphore),用来保证多个线程不会互相冲突。不难看出,mutex是semaphore的一种特殊情况(n=1时)。也就是说,完全可以用后者替代前者。但是,因为mutex较为简单,且效率高,所以在必须保证资源独占的情况下,还是采用这种设计。


操作系统的设计,因此可以归结为三点:
(1)以多进程形式,允许多个任务同时运行;
(2)以多线程形式,允许单个任务分成不同的部分运行;
(3)提供协调机制,一方面防止进程之间和线程之间产生冲突,另一方面允许进程之间和线程之间共享资源。

服务器端多线程实现代码(客户端不变)

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

public class Server {
	
	public static void main(String[] args) {
		// TODO Auto-generated method stub
	    class  ServerThread extends Thread{
			Socket socket =null;
			public ServerThread(Socket socket) {
				this.socket = socket;
			}
		    public  void run()
		    {
		    	//ServerSocket serverSocket = null;
		    	InputStream is = null;
		    	FileOutputStream fos = null;
			
		    	try {
		    		//3.创建socket输入流(读取socket中的数据)
		    		is = socket.getInputStream();
		    		//  创建文件输出流,方便从socket读取的数据写入文件
		    		fos = new FileOutputStream("D:\\新建文本文档2.txt");
				
		    		byte[] bs = new byte[1024];
		    		int num = 0;
		    		while((num = is.read(bs))!=-1)
		    		{
		    			fos.write(bs,0,num);
		    			fos.flush();
		    		}
		    		System.out.println("服务器端接受完毕!");
		    	}catch(IOException e) {
		    		e.printStackTrace();
		    	}finally {
		    		try {
		    			is.close();
		    			fos.close();
		    			socket.close();
		    		}catch(IOException e) {
		    			e.printStackTrace();
		    		}
		    	}
		    }
		}
		
		
		try {
			//1.创建服务器端ServerSocket对象,指定端口
			ServerSocket serverSocket = new ServerSocket(9999);
			Socket socket =null;
			
			System.out.println("服务器等待客户端的连接");
			//2.调用accept()监听(即等待客户端的连接)
			while(true) {
				socket = serverSocket.accept();
				//创建一个新线程
				ServerThread serverThread = null;
				serverThread = new ServerThread(socket);
				serverThread.start();
			}
		}catch(IOException e) {
			e.printStackTrace();
		}
    }
}

备注:概念及方法的使用方法大多从网上各博客搜集得来,总结简化留下本次任务的相关内容。

 

 

 

  • 1
    点赞
  • 0
    评论
  • 8
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

©️2021 CSDN 皮肤主题: 编程工作室 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值