目录
1、socket简介
socket 翻译为“套接字”,是计算机之间基于tcp协议的一种连接。两台存在socket连接的计算机可以发送/接收数据。在java中Socket有以下方法:
- socket.connect()用于远程连接服务器
- socket.setSoTimeout()用于设置超时时间
- socket.getOutputStream()用于写入数据,发送信息
- socket.getInputStream()用于读取数据
- socket.getRemoteSocketAddress()获取连接地址、端口
- socket.bind()为socket关联一个相应地址属性
2、架构图
3、服务器端详细过程
(1)服务器端启动一个ServerSocket占用并监视7000端口,无限循环调用accept方法来与客户端建立socket连接,每一个socket连接使用一个线程管理。每次创建一个线程,都需要将线程加入到list集合中。
ServerSocket serverSocket=new ServerSocket(7000);
System.out.println("服务器已启动!");
while (true){
Socket socket=serverSocket.accept();
System.out.println(String.format("客户端连接已经创建,客户端地址:"+socket.getRemoteSocketAddress()));
Handler talkThread=new Handler(socket);
list.add(talkThread); //加入线程数组中去
talkThread.start();
}
(2)服务器端对消息的处理:编写一个工具类Handler处理消息
static class Handler extends Thread{
Socket socket;
public Handler(Socket socket){
this.socket=socket;
}
}
(3)Handler类继承Thread类,重写run方法。
@Override
public void run() {
try {
InputStream is=this.socket.getInputStream();
OutputStream os=this.socket.getOutputStream();
handle(is,os);
} catch (IOException e) {
e.printStackTrace();
}finally {
removeSocket(list);//线程执行完毕,移除集合
}
System.out.println(socket.getRemoteSocketAddress()+"客户端连接已断开");
}
(4)获取socket中的数据/发送数据
BufferedWriter bw=new BufferedWriter(new OutputStreamWriter(socket.getOutputStream(), StandardCharsets.UTF_8));
BufferedReader br=new BufferedReader(new InputStreamReader(socket.getInputStream(),StandardCharsets.UTF_8));
(5)在Handler方法中定义一个handle方法,用于处理数据通信
private void handle(InputStream inputStream,OutputStream outputStream) throws IOException {
BufferedWriter bw=new BufferedWriter(new OutputStreamWriter(outputStream, StandardCharsets.UTF_8));
BufferedReader br=new BufferedReader(new InputStreamReader(inputStream,StandardCharsets.UTF_8));
bw.write("连接成功\n");
bw.flush();
while (true){
String message=br.readLine(); //读取客户端发送过来的消息
System.out.println(String.format("客户%s:%s",socket.getRemoteSocketAddress(),message));
sendTalk(list,message); //发送信息给所有客户端
if (message.equals("end")){ //用户结束通话,线程执行完毕
System.out.println(socket.getRemoteSocketAddress()+"退出");
br.close();
bw.close();
break;
}
}
}
(5)聊天过程中服务器接收到一个客户端发送的数据之后需要将信息发送给所有的客户,我们这里需要遍历一遍线程数组list。调用sendTalk方法,由于是多个客户端,这里涉及到线程并发安全问题,可以使用synchronized 关键字修饰sendTalk方法。
public synchronized void sendTalk(List<Handler> list,String tellInfo) throws IOException { //将信息全发送给所有socket连接
for (Handler v:list){
PrintWriter printStream=new PrintWriter(v.socket.getOutputStream(),true);
printStream.println(v.socket.getRemoteSocketAddress()+":"+tellInfo);
}
}
线程执行完毕需要移出线程数组list,仍然需要synchronized关键字修饰
public void removeSocket(List<Handler> list){ //移除socket连接
synchronized (list){ //使用synchronized关键字修饰list
list.remove(this);//this 表示当前实例对象,指当前线程
}
}
4、客户端详细过程
(1)创建Socket对象连接服务器端
Socket socket=new Socket("localhost",7000);
(2)编写ClientHandler工具类,用于接收和发送消息
class ClientHandler{
Socket socket=null;
boolean flag=true;
public ClientHandler(Socket socket) {//构造方法
this.socket = socket;
}
(3)工具类中包含两个线程,sendMessage和getMessage,一个用于发送信息,一个接收信息
Thread sendMessage=new Thread(new Runnable() {
@SneakyThrows
@Override
public void run() {
Scanner scanner=new Scanner(System.in);
PrintWriter printWriter=new PrintWriter(socket.getOutputStream(),true);
while (flag){
String message=scanner.nextLine();
if(message.equals("")) continue;//发送内容为空,则不发送
printWriter.println(message);
if (message.equals("end")||message==null) {
flag=false;//如果发送内容为end,则表示结束通信
break;
}
}
}
});
Thread getMessage=new Thread(new Runnable() {
@SneakyThrows
@Override
public void run() {
BufferedReader socketReader=new BufferedReader(new InputStreamReader(socket.getInputStream(),StandardCharsets.UTF_8));
while (flag){
String sr=socketReader.readLine();
System.out.println(sr);
}
}
});
5、完整代码
(1)服务器端
package com.example.kunpneg01.controller;
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
public class SocketTest {
private static List<Handler> list=new ArrayList<>();
public static void main(String[] args) throws IOException {
ServerSocket serverSocket=new ServerSocket(7000);
System.out.println("服务器已启动!");
while (true){
Socket socket=serverSocket.accept();
System.out.println(String.format("客户端连接已经创建,客户端地址:"+socket.getRemoteSocketAddress()));
Handler talkThread=new Handler(socket);
list.add(talkThread);
talkThread.start();
}
}
static class Handler extends Thread{
Socket socket;
public Handler(Socket socket){
this.socket=socket;
}
public synchronized void sendTalk(List<Handler> list,String tellInfo) throws IOException { //将信息全发送给所有socket连接
for (Handler v:list){
PrintWriter printStream=new PrintWriter(v.socket.getOutputStream(),true);
printStream.println(v.socket.getRemoteSocketAddress()+":"+tellInfo);
}
}
public synchronized void removeSocket(List<Handler> list){ //移除socket连接
synchronized (list){//
list.remove(this);
}
}
@Override
public void run() {
try {
InputStream is=this.socket.getInputStream();
OutputStream os=this.socket.getOutputStream();
handle(is,os);
} catch (IOException e) {
e.printStackTrace();
}finally {
removeSocket(list);//线程执行完毕,移除集合
}
System.out.println(socket.getRemoteSocketAddress()+"客户端连接已断开");
}
private void handle(InputStream inputStream,OutputStream outputStream) throws IOException {
BufferedWriter bw=new BufferedWriter(new OutputStreamWriter(outputStream, StandardCharsets.UTF_8));
BufferedReader br=new BufferedReader(new InputStreamReader(inputStream,StandardCharsets.UTF_8));
bw.write("连接成功\n");
bw.flush();
while (true){
String message=br.readLine(); //读取客户端发送过来的消息
System.out.println(String.format("客户%s:%s",socket.getRemoteSocketAddress(),message));
sendTalk(list,message);
if (message.equals("end")){ //用户结束通话,线程执行完毕
System.out.println(socket.getRemoteSocketAddress()+"退出");
br.close();
bw.close();
break;
}
}
}
}
}
(2)客户端
package com.example.retry.domain;
import lombok.SneakyThrows;
import org.apache.hadoop.hbase.client.Scan;
import java.io.*;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
import java.util.Scanner;
public class ClientTest {
public static void main(String[] args) throws IOException {
Socket socket=new Socket("localhost",7000);
ClientHandler clientHandler=new ClientHandler(socket);
clientHandler.start();
}
}
class ClientHandler{
Socket socket=null;
boolean flag=true;
public ClientHandler(Socket socket) {
this.socket = socket;
}
Thread sendMessage=new Thread(new Runnable() {
@SneakyThrows
@Override
public void run() {
Scanner scanner=new Scanner(System.in);
PrintWriter printWriter=new PrintWriter(socket.getOutputStream(),true);
while (flag){
String message=scanner.nextLine();
if(message.equals("")) continue;//发送内容为空,则不发送
printWriter.println(message);
if (message.equals("end")||message==null) {
flag=false;//如果发送内容为end,则表示结束通信
break;
}
}
}
});
Thread getMessage=new Thread(new Runnable() {
@SneakyThrows
@Override
public void run() {
BufferedReader socketReader=new BufferedReader(new InputStreamReader(socket.getInputStream(),StandardCharsets.UTF_8));
while (flag){
String sr=socketReader.readLine();
System.out.println(sr);
}
}
});
public void start(){
sendMessage.start();
getMessage.start();
}
}