一个小的Demo, 可以运用监视器模式扩展成带广播与私聊的聊天小程序,这里不做演示。
记录下自己学socket 的过程。
Server 端:
package com.test.socket;
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;
/**
* Created by szh on 2017/9/10.
*/
public class SocketSingleServer extends Thread {
public static Boolean exitSignal = false;
InputStream inputStream = null;
SocketSingleServer(InputStream stream) {
this.inputStream = stream;
this.start();
}
@Override
public void run() {
try {
RandomAccessFile accessFile = new RandomAccessFile("out/socketClientMessage.txt", "rw");
accessFile.seek(accessFile.length());
BufferedReader reader = new BufferedReader(new InputStreamReader(this.inputStream));
String tmpLine = null;
while(true){
synchronized (SocketSingleServer.exitSignal){
if(SocketSingleServer.exitSignal) break;
while(reader.ready()){
tmpLine = reader.readLine();
accessFile.write((tmpLine+"\r\n").getBytes());
System.out.println(tmpLine);
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
ServerSocket serverSocket = null;
Socket s = null;
InputStream inputStream = null;
OutputStream outputStream = null;
PrintWriter writer = null;
SocketSingleServer serverThread = null;
try {
serverSocket = new ServerSocket(8080);
//会阻塞进程
s = serverSocket.accept();
if (s != null) {
System.out.println("建立连接");
inputStream = s.getInputStream();
outputStream = s.getOutputStream();
writer = new PrintWriter(new BufferedWriter(new OutputStreamWriter(outputStream)));
serverThread = new SocketSingleServer(inputStream);
Scanner scanner = new Scanner(System.in);
String tmpLine = null;
while (!(tmpLine = scanner.nextLine()).equals("exit")) {
writer.println("Server Say : " + tmpLine);
writer.flush();
}
// synchronized (SocketSingleServer.exitSignal){
// SocketSingleServer.exitSignal = true;
// }
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
synchronized (SocketSingleServer.exitSignal){
SocketSingleServer.exitSignal = true;
s.shutdownInput();
writer.close();
outputStream.close();
inputStream.close();
s.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
Client 端:
package com.test.socket;
import java.io.*;
import java.net.InetAddress;
import java.net.Socket;
import java.util.Scanner;
/**
* Created by szh on 2017/9/10.
*/
public class SocketSingleClient extends Thread {
public static Boolean exitSignal = false;
InputStream inputStream = null;
SocketSingleClient(InputStream stream) {
this.inputStream = stream;
this.start();
}
@Override
public void run() {
RandomAccessFile accessFile = null;
BufferedReader reader = null;
try {
accessFile = new RandomAccessFile("out/socketServerMessage.txt", "rw");
accessFile.seek(accessFile.length());
reader = new BufferedReader(new InputStreamReader(this.inputStream));
String tmpLine = null;
while (true) {
synchronized (SocketSingleClient.exitSignal) {
if (SocketSingleClient.exitSignal) break;
while (reader.ready()) {
tmpLine = reader.readLine();
accessFile.write((tmpLine+"\r\n").getBytes());
System.out.println(tmpLine);
}
}
}
System.out.println("step2");
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
reader.close();
accessFile.close();
this.inputStream.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) throws Exception {
Socket clientSocket = null;
InputStream inputStream = null;
OutputStream outputStream = null;
PrintWriter writer = null;
try {
clientSocket = new Socket(InetAddress.getByAddress(new byte[]{127, 0, 0, 1}), 8080);
inputStream = clientSocket.getInputStream();
outputStream = clientSocket.getOutputStream();
writer = new PrintWriter(new BufferedWriter(new OutputStreamWriter(outputStream)));
//创建一个线程用于输出服务端的输入
new SocketSingleClient(inputStream);
Scanner scanner = new Scanner(System.in);
String tmpLine = null;
while (!(tmpLine = scanner.nextLine()).equals("exit")) {
writer.println("Client Say : " + tmpLine);
writer.flush();
}
synchronized (SocketSingleClient.exitSignal) {
SocketSingleClient.exitSignal = true;
}
System.out.println("step1");
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
clientSocket.shutdownInput();
writer.close();
inputStream.close();
outputStream.close();
clientSocket.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
程序的主要思路是
聊天程序中 读取对方的发送内容 / 写给对方内容 应该是可以并行执行的操作,故用了2个线程分别执行 读 与 写
这里主要记录下为什么用到了 同步监视器
由于最终的写入的socket 输入 exit, 线程退出并释放资源. 此时子线程传入的 InputStream 并不知道主线程结束了,再去调用readLine 会引发异常,崩溃退出,程序不够优雅。
(readLine 会阻塞进程) 故加了一个状态位标识 socket 的状态,
读取流 与 释放资源分别是 两块原子操作!!!!
效果:
1
2
3