TCP通信——支持多个客户端同时通信
Client类
import java.io.DataOutputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.util.Scanner;
public class Client {
public static void main(String[] args) throws Exception {
// 创建Socket对象,并同时请求与服务端程序的连接
Socket socket = new Socket("127.0.0.1",8888);
// 从socket通信管道中得到一个字节输出流,用来发送数据给服务端程序
OutputStream os = socket.getOutputStream();
// 把低级的字节输出流包装成数据输出流
DataOutputStream dos = new DataOutputStream(os);
Scanner sc = new Scanner(System.in);
while (true) {
System.out.println("请说:");
String msg = sc.nextLine();
// 一旦发现用户输入了exit命令,就退出客户端
if (msg.equals("exit")){
System.out.println("退出成功");
dos.close();
socket.close();
break;
}
// 开始写数据出去
dos.writeUTF(msg);
dos.flush();
}
}
}
Server类
import java.net.ServerSocket;
import java.net.Socket;
public class Server {
public static void main(String[] args) throws Exception {
System.out.println("------服务端启动成功------");
// 创建ServerSocket的对象,同时为服务端注册端口
ServerSocket serverSocket = new ServerSocket(8888);
while (true) {
// 使用serverSocket对象,调用一个accept方法,等待客户端连接请求
Socket socket = serverSocket.accept();
System.out.println("有人上线了:" + socket.getRemoteSocketAddress());
// 把这个客户端对应的socket通信管道,交给一个独立的线程负责处理
new ServerReaderThread(socket).start();
}
}
}
ServerReaderThread类
import java.io.DataInputStream;
import java.io.InputStream;
import java.net.Socket;
public class ServerReaderThread extends Thread{
private Socket socket;
public ServerReaderThread(Socket socket){
this.socket = socket;
}
@Override
public void run() {
try {
InputStream is = socket.getInputStream();
DataInputStream dis = new DataInputStream(is);
while (true){
try {
String msg = dis.readUTF();
System.out.println(msg);
} catch (Exception e) {
System.out.println("有人下线了:" + socket.getRemoteSocketAddress());
dis.close();
socket.close();
break;
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
TCP通信—综合案例
即时通信-群聊
Client类
import java.io.DataOutputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.util.Scanner;
public class Client {
public static void main(String[] args) throws Exception {
// 创建Socket对象,并同时请求与服务端程序的连接
Socket socket = new Socket("127.0.0.1",8888);
// 创建一个独立的线程,负责随机从socket中接收服务端发送过来的消息
new ClientReaderTherad(socket).start();
// 从socket通信管道中得到一个字节输出流,用来发送数据给服务端程序
OutputStream os = socket.getOutputStream();
// 把低级的字节输出流包装成数据输出流
DataOutputStream dos = new DataOutputStream(os);
Scanner sc = new Scanner(System.in);
while (true) {
System.out.println("请说:");
String msg = sc.nextLine();
// 一旦发现用户输入了exit命令,就退出客户端
if (msg.equals("exit")){
System.out.println("退出成功");
dos.close();
socket.close();
break;
}
// 开始写数据出去
dos.writeUTF(msg);
dos.flush();
}
}
}
Server类
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;
public class Server {
public static List<Socket> onLineSockets = new ArrayList<>();
public static void main(String[] args) throws Exception {
System.out.println("------服务端启动成功------");
// 创建ServerSocket的对象,同时为服务端注册端口
ServerSocket serverSocket = new ServerSocket(8888);
while (true) {
// 使用serverSocket对象,调用一个accept方法,等待客户端连接请求
Socket socket = serverSocket.accept();
onLineSockets.add(socket);
System.out.println("有人上线了:" + socket.getRemoteSocketAddress());
// 把这个客户端对应的socket通信管道,交给一个独立的线程负责处理
new ServerReaderThread(socket).start();
}
}
}
ServerReaderThread类
import java.io.*;
import java.net.Socket;
public class ServerReaderThread extends Thread{
private Socket socket;
public ServerReaderThread(Socket socket){
this.socket = socket;
}
@Override
public void run() {
try {
InputStream is = socket.getInputStream();
DataInputStream dis = new DataInputStream(is);
while (true){
try {
String msg = dis.readUTF();
System.out.println(msg);
// 把这个消息分发给全部客户端进行接收
sendMsgToAll(msg);
} catch (Exception e) {
System.out.println("有人下线了:" + socket.getRemoteSocketAddress());
Server.onLineSockets.remove(socket);
dis.close();
socket.close();
break;
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
private void sendMsgToAll(String msg) throws IOException {
//发送给全部在线的socket管道接收
for (Socket onLineSocket : Server.onLineSockets) {
OutputStream os = onLineSocket.getOutputStream();
DataOutputStream dos = new DataOutputStream(os);
dos.writeUTF(msg);
dos.flush();
}
}
}
ClientReaderThread类
import java.io.DataInputStream;
import java.io.InputStream;
import java.net.Socket;
public class ClientReaderTherad extends Thread{
private Socket socket;
public ClientReaderTherad(Socket socket){
this.socket = socket;
}
@Override
public void run() {
try {
InputStream is = socket.getInputStream();
DataInputStream dis = new DataInputStream(is);
while (true){
try {
String msg = dis.readUTF();
System.out.println(msg);
// 把这个消息分发给全部客户端进行接收
} catch (Exception e) {
System.out.println("自己下线了:" + socket.getRemoteSocketAddress());
dis.close();
socket.close();
break;
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
实现一个简易版的BS架构
BS架构的基本原理
注意:服务器必须给浏览器响应HTTP协议规定的数据格式,否则浏览器不识别返回的数据
- HTTP协议规定:响应给浏览器的数据格式必须满足如下格式
Server类
import java.net.ServerSocket;
import java.net.Socket;
public class Server {
public static void main(String[] args) throws Exception {
System.out.println("------服务端启动成功------");
// 创建ServerSocket的对象,同时为服务端注册端口
ServerSocket serverSocket = new ServerSocket(8080);
while (true) {
// 使用serverSocket对象,调用一个accept方法,等待客户端连接请求
Socket socket = serverSocket.accept();
System.out.println("有人上线了:" + socket.getRemoteSocketAddress());
// 把这个客户端对应的socket通信管道,交给一个独立的线程负责处理
new ServerReaderThread(socket).start();
}
}
}
ServerReaderThread类
import java.io.*;
import java.net.Socket;
public class ServerReaderThread extends Thread{
private Socket socket;
public ServerReaderThread(Socket socket){
this.socket = socket;
}
@Override
public void run() {
try {
OutputStream os = socket.getOutputStream();
PrintStream ps = new PrintStream(os);
ps.println("HTTP/1.1 200 OK");
ps.println("Content-Type:text/html;charest=UTF-8");
ps.println();
ps.println("<div style='color:red;font-size:120px;text=align:center'>666<div>");
ps.close();
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
扩展
每次请求都开一个新的线程,到底好不好?
使用线程池优化
Server类
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class Server {
public static List<Socket> onLineSockets = new ArrayList<>();
public static void main(String[] args) throws Exception {
System.out.println("------服务端启动成功------");
// 创建ServerSocket的对象,同时为服务端注册端口
ServerSocket serverSocket = new ServerSocket(8080);
// 创建一个线程池,负责处理通信管道的任务
ThreadPoolExecutor pool = new ThreadPoolExecutor(8*2,8*2,0, TimeUnit.SECONDS,
new ArrayBlockingQueue<>(8), Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy());
while (true) {
// 使用serverSocket对象,调用一个accept方法,等待客户端连接请求
Socket socket = serverSocket.accept();
System.out.println("有人上线了:" + socket.getRemoteSocketAddress());
pool.execute(new ServerReaderRunnable(socket));
}
}
}
ServerReaderRunnable类
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import java.net.Socket;
public class ServerReaderRunnable implements Runnable{
private Socket socket;
public ServerReaderRunnable(Socket socket){
this.socket = socket;
}
@Override
public void run() {
try {
OutputStream os = socket.getOutputStream();
PrintStream ps = new PrintStream(os);
ps.println("HTTP/1.1 200 OK");
ps.println("Content-Type:text/html;charest=UTF-8");
ps.println();
ps.println("<div style='color:red;font-size:120px;text=align:center'>666<div>");
ps.close();
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}