Java基础23 Socket手撸应用服务器

超详细的Java知识点路线图


前言

本文带大家了解TCP协议概念,实现Socket的基本通信,文件上传,最后会用Socket实现模拟的服务器。

TCP协议

TCP(Transmission Control Protocol)传输控制协议,是一种面向连接的、可靠的、基于字节流的传输层通信协议。

TCP协议的特点:

  1. 基于字节流
  2. 面向连接
  3. 可靠
  4. 支持点对点通信

三次握手和四次挥手

TCP协议的可靠性主要基于三次握手和四次挥手机制
三次握手:
在这里插入图片描述

  1. 第一次握手:客户端发送标志位SYN=1和随机值seq=J给服务器,客户端进入SYN_SENT状态,等待服务器确定
  2. 第二次握手:服务端收到SYN=1后,将标志位SYN和ACK都置为1,ack设置为J+1,产生随机值seq=K发送给客户端,服务器状态为SYN_RCVD
  3. 第三次握手:客户端收到后检查如果ACK为1,ack为J+1,就将ACK标志位设置为1,ack设置为k+1发送给服务器,服务器检查ACK为1,ack为K+1则连接成功建立,客户端和服务器都进入ESTABLISHED状态,开始通信

简单来说:类似某男追某女

  1. 第一次见面,某男给某女发消息:XX,做我女朋友吧,这是我给你的礼物
  2. 第二次见面,某女说:XX,我收到你的消息和礼物了,你要对人家负责啊,也给你个礼物
  3. 第三次见面,某男说:XX,我收到你的礼物了,太好了,我会对你负责的。

四次挥手:
在这里插入图片描述

  1. 第一次挥手: 客户端发送FIN=M给服务器,客户端进入FIN_WAIT_1状态
  2. 第二次挥手: 服务器收到FIN=M后,发送ack=M+1给客户端,服务端进入CLOSE_WAIT状态
  3. 第三次挥手: 服务器发送FIN=N给客户端,服务端进入LAST_ACK状态
  4. 第四次挥手: 客户端收到FIN=N后进入TIME_WAIT状态,发送标志位ACK=1,确认序号ack=K+1,服务器收到后进入CLOSED状态,连接关闭。

简单来说:类似某男和某女分手

  1. 第一次挥手,某男给某女发消息:XX,我们不合适,分手吧
  2. 第二次挥手,某女说:XX,我知道了,你是个渣男,我早就想和你分手了,等着我去收拾东西
  3. 第三次挥手,某女说:XX,东西我收拾好了,拜拜吧
  4. 第三次挥手,某男说:XX,知道了,我们再也不见!

TCP/UDP

TCP和UDP同属于传输层协议,对比TCP和UDP:

UDPTCP
是否连接无连接面向连接
是否可靠不可靠传输可靠传输
连接个数支持一对一,一对多,多对多通信只能是一对一通信
传输方式面向报文面向字节流
适用场景适用于实时应用(IP电话、视频会议、直播等)适用于要求可靠传输的应用,例如文件传输

Socket编程

Socket基于TCP/IP协议,用于客户端和服务器端通信。

Socket

网络套接字,用于连接另一台计算机

创建方法:

new Socket("IP地址",端口号)

注意:一旦创建了Socket对象,就自动连接对方计算机

常用方法:

  • getInputStream() 获得输入流,读取对方发来的数据
  • getOutputStream() 获得输出流,给对方发数据
  • close() 关闭

注意:一旦输入流或输出流关闭,Socket连接会自动关闭。

ServerSocket

服务器端Socket,Socket的子类,用于接受客户端并和客户端通信

创建:

new ServerSocket(端口号)

注意:一旦创建ServerSocket对象,会不断侦听该端口,判断是否有客户端连接

主要方法:

  • Socket accept() 用于获得连接过来的客户端Socket对象

服务器端和客户端的通信

服务器端实现步骤:

  1. 创建ServerSocket对象
  2. 循环调用accept方法获得连接
  3. 调用Socket对象的IO流来读取、发送数据。
public class Server {

	public static final int PORT = 8888;

	public void start(){
    	System.out.println("启动服务器。。。");
		//创建ServerSocket对象
		try {
			ServerSocket server = new ServerSocket(PORT);
			//循环获得客户端连接
			while(true){
				Socket client = server.accept();
				System.out.println(client.getInetAddress()+"连接了");
				//获得客户端的输入流和输出流
				try(DataInputStream dis = new DataInputStream(client.getInputStream());
					DataOutputStream dos = new DataOutputStream(client.getOutputStream())){
					//读取客户端的消息
					System.out.println("客户端"+client.getInetAddress()+"说:"+dis.readUTF());
					//给客户端发消息
					dos.writeUTF("我是服务器端,客户端你好啊~~~~~~~~~~~~~~~~");
				}catch(IOException ex){
					ex.printStackTrace();
				}
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
	}


	public static void main(String[] args) {
		new Server().start();
	}
}

客户端实现步骤:

  1. 创建Socket对象
  2. 调用Socket对象的getInputStream来读取数据。
  3. 调用Socket对象的getOutputStream来发送数据。
public class Client {

	public void sendMessage(String ip,int port,String msg){
		//创建Socket对象,连接服务器端
		try {
			Socket socket = new Socket(ip,port);
			//获得输出流和输入流
			try(DataOutputStream dos = new DataOutputStream(socket.getOutputStream());
					DataInputStream dis = new DataInputStream(socket.getInputStream())){
				//发送数据给服务器端
				dos.writeUTF(msg);
				//读取服务器端的消息
				System.out.println("服务器端说:"+dis.readUTF());
			}catch(Exception ex){
				ex.printStackTrace();
			}
		} catch (Exception e) {
			e.printStackTrace();
		} 
	}

	public static void main(String[] args) {
		new Client().sendMessage("192.168.53.5", 8888, 
				"你好!!!服务器端");
	}
}

Socket实现文件上传

文件的上传的步骤

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IimwXFK3-1611022265375)(Socket.assets/clipboard-1610675065080.png)]

服务器端:

  1. 创建ServerSocket
  2. 调用accept获得客户端Socket
  3. 定义字节数组
  4. 创建文件输出流,获得客户端输入流
  5. 循环读取输入流的字节,写入到文件输出流

客户端:

  1. 创建Socket
  2. 获得socket对象输出流
  3. 创建文件输入流
  4. 循环读取文件输入流字节,写入到输出流
public class FileServer {

	public static final int PORT = 8888;
	public static final String DIR = "C:\\upload\\";
	
	public void start(){
		System.out.println("start...");
		//创建服务器端对象
		try (ServerSocket server = new ServerSocket(PORT);){
			//调用accept接受客户端连接
			while(true){
				Socket socket = server.accept();
				//创建文件输出流和网络输入流
				try(ObjectInputStream in = new ObjectInputStream(
								socket.getInputStream());
						//读取客户端发来的文件名,创建文件输出流
						OutputStream out = 
								new FileOutputStream(DIR+in.readUTF());){
					//从网络中读取数据,写入到本地磁盘
					int len = 0;
					byte[] buffer = new byte[1024];
					while((len = in.read(buffer)) != -1){
						out.write(buffer, 0, len);
					}
					System.out.println("服务器保存完毕");
				}catch(IOException ex){
					ex.printStackTrace();
				}
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	public static void main(String[] args) {
		new FileServer().start();
	}
}
public class FileClient {

	/**
	 * 发送文件
	 */

	public void sendFile(String ip,int port,String path){
		File file = new File(path);
		//创建连接,创建文件输入流,网络输出流
		try(Socket socket = new Socket(ip,port);
				InputStream in = new FileInputStream(path);
					ObjectOutputStream out = new ObjectOutputStream(
							socket.getOutputStream())){
			//先发送文件名给服务器
			out.writeUTF(file.getName());
			out.flush();
			//读取本地文件数据,写入到网络输出流中
			int len = 0;
			byte[] buffer = new byte[1024];
			while((len = in.read(buffer)) != -1){
				out.write(buffer, 0, len);
			}
			System.out.println("客户端发送完毕");
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	public static void main(String[] args) {
		new FileClient().sendFile("127.0.0.1", 8888, 
					"D:\\java_code\\Test10.java");
	}
}

Socket实现模拟服务器

在这里插入图片描述

HTML服务器的简单工作原理:

  1. 用户在浏览器输入URL地址
  2. 浏览器发送请求给服务器
  3. 服务器从请求头中解析信息:请求方法、URL等
  4. 从URL中解析到资源名称,就到服务器上查找HTML文件
  5. 服务器发送响应状态给浏览器
  6. 服务器发送响应头给浏览器
  7. 服务器开始发送HTML文件的内容
  8. 浏览器接收到HTML内容渲染出来
/**
 * 模拟服务器
 */
public class MyTomcat {

	private static final int PORT = 8888;
	private static final String WEB_DIR = "D:/webapps";
	
	public void start(){
		//创建服务端Socket
		try(ServerSocket server = new ServerSocket(PORT)){
			System.out.println("服务器启动了。。。。");
			while(true){
				//接受客户端连接
				Socket client = server.accept();
				//读取浏览器的请求
				service(client);
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	//服务客户端
	public void service(Socket client){
		//获得客户端的输入流和输出流
		try(BufferedReader reader = new BufferedReader(
				new InputStreamReader(client.getInputStream()));
			BufferedOutputStream writer = new BufferedOutputStream(
					client.getOutputStream())){
			//读取客户端的请求信息
			String request = reader.readLine();
			if(request == null){
				return;
			}
			System.out.println(request);
			//将请求进行分割
			String[] requests = request.split("\\ ");
			if(requests.length < 2){
				return;
			}
			//通过请求头中的路径查找文件
			String path = requests[1];
			File file = new File(WEB_DIR+path);
			if(!file.exists()){
				//如果不存在,就发送404错误
				writer.write("HTTP/1.1 404 NOT FOUND\r\n".getBytes());
			}else{
				//如果存在,就发送200
				writer.write("HTTP/1.1 200 OK\r\n".getBytes());
				//读取本地HTML文件的内容
				BufferedReader br = new BufferedReader(new FileReader(file));
				StringBuilder strb = new StringBuilder();
				String line = null;
				while((line = br.readLine()) != null){
					strb.append(line);
				}
				br.close();
				String html = strb.toString();
				System.out.println("html-->"+html);
				//发送响应头给客户端
				String header = "Content-Type:text/html;charset=utf-8\r\n"
						+ "Content-Length:"+html.getBytes("UTF-8").length+"\r\n";
				writer.write(header.getBytes());
				//正文和响应头之间发送分割符号
				writer.write(new byte[]{10,13},0,2);
				//发送正文
				writer.write(html.getBytes("UTF-8"));
				System.out.println("发送完毕!");
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	public static void main(String[] args) {
		MyTomcat myTomcat = new MyTomcat();
		myTomcat.start();
	}
}

在D:/webapps下添加hello.html文件,启动服务器
在浏览器输入测试:http://localhost:8888/hello.html
在这里插入图片描述

结束


大家如果需要学习其他Java知识点,戳这里 超详细的Java知识点汇总

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

恒哥~Bingo

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值