一、Socket和Client
1、Socket是客户端创建的通信端点,比较官方的翻译是:套接字,但是这个翻译对于初学者来说,比较抽象,不易理解。为了方便理解,我这里举个简单的例子。
我们可以把客户端比作电话,服务器端比作某移动公司的客服电话,当你拨打该客服电话时,需要输入电话号码,此时这个号码我们就可以理解为服务器的地址(IP),但是由于客服业务众多,就算电话接通后,也并不知道你要办理或咨询什么业务,此时会提示你按某数字键来确定你想要办理的业务,此时的数字键我们就可以理解为服务器的端口号(PORT),只有当IP和PORT都确定了,服务器才能知道你想要的是具体的哪一个服务。
所以,当我们创建客户端与服务器的通信端点(套接字对象)时,我们需要告诉它服务器的IP和端口号。
2、下面代码是使用Socket + IO流实现的简易版Client
package com.xiao.socket;
import java.io.*;
import .Socket;
/**
* @description Socket 简易版客户端
* @auther: 笑笑是一个码农
* @date: 22:34 2020/11/20
*/
public class Client {
// 默认服务器主机地址
final static String DEFAULT_SERVER_HOST = "127.0.0.1";
// 默认服务器端口
final static int DEFAULT_SERVER_PORT = 8888;
/**
* 默认客户端退出指令定义
*/
final static String QUIT = "quit";
public static void main(String[] args) {
Socket socket = null;
BufferedReader reader = null;
BufferedWriter writer = null;
try {
// 创建Socket
socket = new Socket(DEFAULT_SERVER_HOST, DEFAULT_SERVER_PORT);
System.out.println("[客户端日志] 已连接服务器");
// 创建输入流,读取服务器发送的数据
reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
// 创建输出流,向服务器发送数据
writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
// 等待用户输入信息(TODO 思考:有几种方式实现? 这里为什么用这种方式?)
BufferedReader consoleReader = new BufferedReader(new InputStreamReader(System.in));
while (true){
String input = consoleReader.readLine();
// 发送消息给服务器
writer.write(input +"\n");
writer.flush();
System.out.println("[客户端日志] 客户端发送消息:" + input);
// 读取服务器返回的消息
String serverMessage = reader.readLine();
System.out.println("[客户端日志] 接收到服务器消息:["+serverMessage+"]");
if (input.equals(QUIT)){
System.out.println("[客户端日志] 退出客户端");
break;
}
}
} catch (IOException e) {
e.printStackTrace();
}finally {
close(writer);
}
}
/**
* 关闭方法抽取
* @param writer
*/
private static void close(BufferedWriter writer) {
if (writer != null){
try {
writer.close(); // 会自动关闭socket
System.out.println("[客户端日志] 关闭客户端的socket");
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
二、ServerSocket和Server
1、ServerSocket是服务端监听通信的端点,其accept方法,会返回一个与客户端建立通信端点的Socket对象,通过上面的例子可知,我们在创建服务器端监听的通信端点时,需要指定监听的端口号,告诉服务器,我们是在哪个端口办理业务。
2、下面代码是使用ServerSocket+IO流实现的简易版Server
package com.xiao.socket;
import java.io.*;
import .ServerSocket;
import .Socket;
/**
* @description ServerSocket 简易服务器
* @auther: 笑笑是一个码农
* @date: 22:12 2020/11/20
*/
public class Server {
// 默认端口
final static int DEFAULT_PORT = 8888;
/**
* 默认客户端退出指令定义
*/
final static String QUIT = "quit";
public static void main(String[] args) {
ServerSocket serverSocket = null;
try {
// 绑定监听端口
serverSocket = new ServerSocket(DEFAULT_PORT);
System.out.println("[服务器日志] 服务器已启动,当前监听端口:" + DEFAULT_PORT);
while (true){
// accept方法,等待客户端连接,会阻塞当前线程,有客户端建立连接时,会返回一个Socket对象,使用该对象,就可以跟客户端通信
Socket socket = serverSocket.accept();
System.out.println("[服务器日志] 客户端["+socket.getInetAddress().getHostAddress()+":"+socket.getPort()+"]已连接");
// 获取客户端的输入数据reader对象,读取客户端发送过来的数据
BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
// 获取向客户端发送数据writer对象,向客户端发送数据
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
String message;
// 循环读取某个客户端发的消息
// null判断,客户端的输入流关闭或不可用,读取的为null
while ((message = reader.readLine()) != null) {
System.out.println("[服务器日志] 接收到客户端["+socket.getPort()+"]消息:" + message);
// 回复客户端消息
writer.write("服务器回复消息:" + message + "\n");
writer.flush();
// 客户端是否退出
if (QUIT.equals(message)){
System.out.println("[服务器日志] 客户端["+socket.getPort()+"]已断开连接");
break;
}
}
}
} catch (IOException e) {
e.printStackTrace();
}finally {
close(serverSocket);
}
}
/**
* 抽取关闭方法
* @param serverSocket
*/
private static void close(ServerSocket serverSocket) {
if (serverSocket != null){
try {
serverSocket.close();
System.out.println("[服务器日志] 关闭serverSocket");
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
三、源码地址
https://gitee.com/smile-coding/my-code 在io模型(BIO/NIO/AIO)目录下
如果对您有帮助,点个小心心(Star)支持一下哈~