一、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();
}
}
}
备注:概念及方法的使用方法大多从网上各博客搜集得来,总结简化留下本次任务的相关内容。