因为有多线程的存在,所以当连接请求到达时,可以在服务端创建多个socket,分别和不同的客户端进行连接通信。
服务端
SocketServerRelease 用于不断接收客户端的连接,并创建socket
public class SocketServerRelease extends Thread{
public static ServerSocket server = null;
static{
try {
server = new ServerSocket(5209);
} catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
System.out.println("******服务器已启动,等待客户端连接*****");
Socket socket = null;
int i=1;
try {
while(true){
//循环监听客户端的连接
socket = server.accept();
//新建一个线程ServerSocket,并开启
System.out.println("开启第"+i+"个socket");
new SocketServerThread(socket,"第"+i+"个socket").start();
i++;
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
SocketServerThread 接收和发送消息处理
public class SocketServerThread extends Thread{
private Socket socket;
private String name;
public SocketServerThread(Socket socket,String name) {
this.socket = socket;
this.name = name;
}
@Override
public void run() {
Accepter accepter = new Accepter(socket,name);
Sender sender = new Sender(socket,name);
accepter.start();
sender.start();
}
class Accepter extends Thread{
private Socket socket;
private String name;
public Accepter(Socket socket,String name) {
this.socket = socket;
this.name = name;
}
@Override
public void run() {
String message="";
try {
BufferedReader in =new BufferedReader(new InputStreamReader(socket.getInputStream()));;
int i=1;
do{
message = in.readLine();
System.out.println("服务端第"+i+"次接收到来自<<"+name+">>信息为:"+message);
i++;
}while (!"exit".equals(message));
//当客户端输入exit退出后,关闭socket输入流
//in.close(); //这种关闭流的方法会导致socket关闭
socket.shutdownInput();
System.out.println("服务端已停止接收<<"+name+">>的数据");
} catch (IOException e) {
e.printStackTrace();
}
}
}
class Sender extends Thread{
private Socket socket;
private String name;
public Sender(Socket socket,String name) {
this.socket = socket;
this.name = name;
}
@Override
public void run() {
int i= 1;
String readLine = "";
try {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
PrintWriter writer = new PrintWriter(socket.getOutputStream());
do{
readLine = br.readLine();
if(null != readLine){
writer.println(readLine);
writer.flush();
}
System.out.println("服务端第"+i+"次发送信息给<<"+name+">>,信息为:"+readLine);
i++;
}while (!"exit".equals(readLine));
//br.close(); //注意这里的控制台输入流不能关闭,在多线程的情况下有可能出现A线程正在读,而B线程已经关闭了该流的情况
//writer.close(); //采用流关闭方式会导致socket关闭
socket.shutdownOutput();
System.out.println("服务端停止发送数据给<<"+name+">>");
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
客户端
SocketClientA 多个客户端可以将此类复制多个
public class SocketClientA {
static Socket socket;
static BufferedReader br; //控制台输入
static BufferedReader in; //读取服务端信息输入流
static PrintWriter writer; //发送给服务端信息的输出流
class Accepter extends Thread{
@Override
public void run() {
int i= 1;
String readLine="";
try {
do{
readLine = in.readLine();
if(null != readLine){
System.out.println(readLine);
}
System.out.println("客户端第"+i+"次接收信息为:"+readLine);
i++;
}while(!"exit".equals(readLine));
//关闭socket输入流
//in.close(); 采用关闭流的方式会导致socket被关闭
socket.shutdownInput();
System.out.println("客户端停止接收数据");
} catch (IOException e) {
e.printStackTrace();
}
}
}
class Sender extends Thread{
@Override
public void run() {
int i= 1;
String readLine ="";
try {
do{
readLine = br.readLine();
if(null != readLine){
writer.println(readLine);
writer.flush();
System.out.println("客户端第"+i+"次发送信息为:"+readLine);
i++;
}
}while (!"exit".equals(readLine));
//当客户端输入exit后关闭socket输出流
br.close();
//writer.close(); 采用流的关闭方式会导致socket关闭
socket.shutdownOutput();
System.out.println("客户端停止发送数据");
} catch (IOException e) {
e.printStackTrace();
}
}
}
// 搭建客户端
public static void main(String[] args) throws IOException {
try {
// 1、创建客户端Socket,指定服务器地址和端口
socket = new Socket("127.0.0.1",5209);
System.out.println("客户端启动成功");
SocketClientA socketClient = new SocketClientA();
Sender sender = socketClient.new Sender();
Accepter accepter = socketClient.new Accepter();
br = new BufferedReader(new InputStreamReader(System.in));
in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
writer = new PrintWriter(socket.getOutputStream());
sender.start();
accepter.start();
} catch (Exception e) {
System.out.println("can not listen to:" + e);// 出错,打印出错信息
}
}
}
运行结果和分析
- 服务端控制台输出为
******服务器已启动,等待客户端连接*****
开启第1个socket
开启第2个socket
服务端第1次接收到来自<<第1个socket>>信息为:111111
服务端第1次接收到来自<<第2个socket>>信息为:222222
333333
服务端第1次发送信息给<<第1个socket>>,信息为:333333
444444
服务端第1次发送信息给<<第2个socket>>,信息为:444444
exit
服务端第2次发送信息给<<第1个socket>>,信息为:exit
服务端停止发送数据给<<第1个socket>>
服务端第2次接收到来自<<第1个socket>>信息为:exit
服务端已停止接收<<第1个socket>>的数据
服务端第2次接收到来自<<第2个socket>>信息为:exit
服务端已停止接收<<第2个socket>>的数据
2.客户端A控制台输出为
客户端启动成功
111111
客户端第1次发送信息为:111111
客户端第1次接收信息为:333333
客户端第2次接收信息为:exit
客户端停止接收数据
exit
客户端第2次发送信息为:exit
客户端停止发送数据
3.客户端B控制台输出为
客户端启动成功
222222
客户端第1次发送信息为:222222
客户端第1次接收信息为:444444
exit
客户端第2次发送信息为:exit
客户端停止发送数据
客户端第2次接收信息为:exit
客户端停止接收数据
分析:由于服务端的控制台输入只有一个入口,多线程中如果服务端某个socket关闭了控制台的输入流,那有可能其他socket正在读取,这是会导致io异常,所以服务端的输入流没有关闭。
有时候会出现服务端需要输入多次才能发送到客户端的现象,这可能是因为多个socket同时需要输入导致的。
在输入之前socket已经确定,发送给哪个客户端已经确定。