关于回显服务器的概念在上篇博客中有解释
(1)TCP是面向字节流的,UDP是面向数据报的;
(2)TCP的服务器需要与操作系统内核建立连接,UDP不需要
(3)TCP是可靠传输,UDP是不可靠传输
(4)UDP效率较高,TCP相对UDP来说效率稍微逊;
代码如下:
public class TCPEchoServer {
private ServerSocket serverSocket = null;
//初始化serverSocket时需要绑定端口号
public TCPEchoServer(int port) throws IOException {
serverSocket = new ServerSocket(port);
}
}
进入主循环需要完成的事情:
- 与TCP建立连接
- 处理TCP连接
- 获取请求并解析
- 根据请求计算响应
- 将响应写回客户端
代码如下:
public void start() throws IOException {
System.out.println("服务器启动");
while(true){
//负责与客户端交互
//与TCP建立连接
Socket clientSocket = serverSocket.accept();
//处理连接
processConnection(clientSocket);
}
}
private void processConnection(Socket clientSocket) {
System.out.printf("客户端上线 [%s:%d]",clientSocket.getInetAddress().toString(),
clientSocket.getPort());
//TCP是面向字节流的,由于是回显服务器主要针对字符流文件,因此这里需要将字节流文件字符流化
try(BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(clientSocket.getOutputStream()))
){
while(true){
//获取请求并解析
//注意此处readLine()规定了读取是按照行读取的,同样写数据必须按行写,相当于自定义协议
String request = bufferedReader.readLine();
//根据请求计算响应
String response = process(request);
//将响应写回客户端
//这里之所以加上换行符的原因是因为要按行写数据,需要遵循自定义协议
bufferedWriter.write(response+"\n");
//由于 bufferedWriter是带有缓冲区的,没有刷新的话数据就在缓冲区中,没有真正写到socket中
//需要手动刷新
bufferedWriter.flush();
//打印日志
System.out.printf("[%s:%d] req:%s resp:%s\n",clientSocket.getInetAddress().toString(),
clientSocket.getPort(),request,response);
}
} catch (IOException e) {
e.printStackTrace();
}
}
private String process(String request) {
return request;
}
完整代码如下:
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
/**
* Author:ZouDouble
* Description:
* 天气:晴天
* 目标:Good Offer
* Date 2021-01-05 17:20
*/
public class TCPEchoServer {
private ServerSocket serverSocket = null;
//初始化serverSocket时需要绑定端口号
//serverSocket负责与客户端进行连接
public TCPEchoServer(int port) throws IOException {
serverSocket = new ServerSocket(port);
}
public void start() throws IOException {
System.out.println("服务器启动");
while(true){
//负责与客户端交互
//与TCP建立连接
Socket clientSocket = serverSocket.accept();
//处理连接
processConnection(clientSocket);
}
}
private void processConnection(Socket clientSocket) {
System.out.printf("客户端上线 [%s:%d]",clientSocket.getInetAddress().toString(),
clientSocket.getPort());
//TCP是面向字节流的,由于是回显服务器主要针对字符流文件,因此这里需要将字节流文件字符流化
try(BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(clientSocket.getOutputStream()))
){
while(true){
//获取请求并解析
//注意此处readLine()规定了读取是按照行读取的,同样写数据必须按行写,相当于自定义协议
String request = bufferedReader.readLine();
//根据请求计算响应
String response = process(request);
//将响应写回客户端
//这里之所以加上换行符的原因是因为要按行写数据,需要遵循自定义协议
bufferedWriter.write(response+"\n");
//由于 bufferedWriter是带有缓冲区的,没有刷新的话数据就在缓冲区中,没有真正写到socket中
//需要手动刷新
bufferedWriter.flush();
//打印日志
System.out.printf("[%s:%d] req:%s resp:%s\n",clientSocket.getInetAddress().toString(),
clientSocket.getPort(),request,response);
}
} catch (IOException e) {
e.printStackTrace();
}
}
private String process(String request) {
return request;
}
public static void main(String[] args) throws IOException {
TCPEchoServer server = new TCPEchoServer(9090);
server.start();
}
}
代码如下:
public class TCPEchoClientServer {
private Socket socket = null;
//注意:这里用的是Socket类,这里的serverIp和serverPort仅仅只是属性,初始化的时候并没有绑定端口号
//客户端初始化的时候不需要绑定端口号
public TCPEchoClientServer(String serverIp,int serverPort) throws IOException {
socket = new Socket(serverIp,serverPort);
}
进入主循环需要完成的事情:
- 读取客户请求并解析
- 构造请求发给服务器
- 从服务器读取响应
- 将响应写回客户端
代码如下:
public void start(){
System.out.println("客户端来咯");
Scanner scanner = new Scanner(System.in);
try(BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()))
){
while(true){
//读取用户数据
System.out.println("输入请求->");
String request = scanner.nextLine();
if ("exit".equals(request)){
System.out.println("退出");
break;
}
//构造请求数据
bufferedWriter.write(request+"\n");
bufferedWriter.flush();
//从服务器读取响应
String response = bufferedReader.readLine();
//将响应写回客户端
System.out.println(response);
}
} catch (IOException e) {
e.printStackTrace();
}
}
完整代码如下:
import java.io.*;
import java.net.Socket;
import java.util.Scanner;
/**
* Author:ZouDouble
* Description:
* 天气:晴天
* 目标:Good Offer
* Date 2021-01-05 17:42
*/
public class TCPEchoClientServer {
private Socket socket = null;
//注意:这里用的是Socket类,这里的serverIp和serverPort仅仅只是属性,初始化的时候并没有绑定端口号
//客户端初始化的时候不需要绑定端口号
public TCPEchoClientServer(String serverIp,int serverPort) throws IOException {
socket = new Socket(serverIp,serverPort);
}
public void start(){
System.out.println("客户端来咯");
Scanner scanner = new Scanner(System.in);
try(BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()))
){
while(true){
//读取用户数据
System.out.println("输入请求->");
String request = scanner.nextLine();
if ("exit".equals(request)){
System.out.println("退出");
break;
}
//构造请求数据
bufferedWriter.write(request+"\n");
bufferedWriter.flush();
//从服务器读取响应
String response = bufferedReader.readLine();
//将响应写回客户端
System.out.println(response);
}
} catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args) throws IOException {
TCPEchoClientServer client = new TCPEchoClientServer("127.0.0.1",9090);
client.start();
}
}
这样写出的代码有一个bug,它的建立连接和处理连接是串行执行的,代码中的while()循环是客户端下线才会触发,因此这个代码的局限性就是仅支持单用户使用,改进方法就是让它并发执行!如果使用多线程的话,由于多线程中也涉及到线程的创建与销毁,也是一些开销~最直接一步到位的方法就是利用线程池。接下来,我把多线程版本的和线程池版本的代码都放在下面,有需要的朋友可以看看!
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
/**
* Author:ZouDouble
* Description:
* 天气:晴天
* 目标:Good Offer
* Date 2021-01-05 18:02
*/
public class TCPThreadEchoServer {
private ServerSocket serverSocket = null;
public TCPThreadEchoServer(int port) throws IOException {
serverSocket = new ServerSocket(port);
}
public void start() throws IOException {
System.out.println("服务器启动");
while(true){
Socket clientSocket = serverSocket.accept();
Thread thread = new Thread(){
@Override
public void run() {
processConnection(clientSocket);
}
};
thread.start();
}
}
private void processConnection(Socket clientSocket){
System.out.printf("[%s:%d]客户端上线~\n",clientSocket.getInetAddress().toString(),
clientSocket.getPort());
try(BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(clientSocket.getOutputStream()))
) {
while(true){
//1)读取请求并建立连接
String request = bufferedReader.readLine();
//2)根据请求计算响应
String response = process(request);
//3)将响应写回客户端
bufferedWriter.write(response+"\n");
bufferedWriter.flush();
System.out.printf("[%s:%d] req:%s resp:%s\n",clientSocket.getInetAddress().toString(),
clientSocket.getPort(),request,response);
}
} catch (IOException e) {
System.out.printf("[%s:%d]客户端下线~\n",clientSocket.getInetAddress().toString(),
clientSocket.getPort());
}
}
private String process(String request) {
return request;
}
public static void main(String[] args) throws IOException {
TCPThreadEchoServer server = new TCPThreadEchoServer(9090);
server.start();
}
}
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* Author:ZouDouble
* Description:
* 天气:晴天
* 目标:Good Offer
* Date 2021-01-05 19:58
*/
public class TCPEThreadPoolEchoServer {
private ServerSocket serverSocket = null;
public TCPEThreadPoolEchoServer(int port) throws IOException {
serverSocket = new ServerSocket(port);
}
public void start() throws IOException {
System.out.println("服务器启动");
ExecutorService executorService = Executors.newCachedThreadPool();
while(true){
Socket clientSocket = serverSocket.accept();
executorService.execute(new Runnable() {
@Override
public void run() {
processConnection(clientSocket);
}
});
}
}
private void processConnection(Socket clientSocket){
System.out.printf("客户端上线[%s:%d]",clientSocket.getInetAddress().toString(),
clientSocket.getPort());
try(BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(clientSocket.getOutputStream()))
) {
while(true){
//1)接收请求并解析
String request = bufferedReader.readLine();
//2)根据请求计算响应
String response = process(request);
//3)将响应写回客户端
bufferedWriter.write(response+"\n");
bufferedWriter.flush();
//打印日志
System.out.printf("[%s:%d] req:%s resp:%s\n",clientSocket.getInetAddress().toString(),
clientSocket.getPort(),request,response);
}
} catch (IOException e) {
System.out.printf("客户端下线[%s:%d]",clientSocket.getInetAddress().toString(),
clientSocket.getPort());
}
}
private String process(String request){
return request;
}
public static void main(String[] args) throws IOException {
TCPEThreadPoolEchoServer server = new TCPEThreadPoolEchoServer(9090);
server.start();
}
}