上学期学校开设了创新项目的课程,我选择了《基于手机定位的Android考勤系统》,在整个开发过程中,总的来说,真的是学到了很多,尤其是客户端和服务器端通信这一块。对Socket通信,多线程等有了一定的认识,所以在此记录一下,一起学习,我的认识还是很浅的,如有错误,欢迎指出。
服务器端
(我这里是把自己的电脑当做服务器,也可以申请云服务器)
主要步骤:
- 1、在服务器端,用一个端口来实例化一个ServerSocket对象。当服务器端开始运行时,就可以用这个端口时刻监听从客户端发来的连接请求。
- 2、调用ServerSocket的accept方法,接收从端口上发送来的连接请求,返回客户端的socket对象,用来进行读写IO的操作。
- 3、利用客户端socket的isConnected()方法,来获取客户端连接的状态,连接成功可以做相应的操作,比如支持多用户并发访问的时候,可以将客户端的socket添加到线程池中。(isConnected()方法获取的并不是实时的客户端的连接状态,可以通过心跳包机制来获取实时的连接状态)。
- 4、通讯完成后,关闭打开的流和Socket对象。
服务器代码(Server.java)
public class Server {
private ExecutorService executorService;// 线程池
private ServerSocket serverSocket = null;
private Socket socket = null;
private boolean isStarted = true;//判断服务是否启动
public Server() {
try {
// 创建线程池,池中具有(cpu个数*50)条线程
executorService = Executors.newFixedThreadPool(Runtime.getRuntime()
.availableProcessors() * 50);
//实例化ServerSocket对象 注意客户端端口号需要和服务器端端口号保持一致
serverSocket = new ServerSocket(8090);
} catch (IOException e) {
e.printStackTrace();
//quit();
}
}
public void start() {
try {
while (isStarted) {
System.out.println("等待连接");
socket = serverSocket.accept();
String ip = socket.getInetAddress().toString();
System.out.println("客户端已连接");
// 为支持多用户并发访问,采用线程池管理每一个用户的连接请求
if (socket.isConnected())
{
//new Thread(new HeartBeatMonitor(socket)).start();
executorService.execute(new SocketTask(socket));// 添加到线程池
}
}
if (socket != null)
socket.close();
if (serverSocket != null)
serverSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 退出
*/
public void quit() {
try {
this.isStarted = false;
serverSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
new Server().start();//启动服务
// new Server().quit();
}
//单个Socket线程
final class SocketTask implements Runnable {
private Socket socket = null;
private ServerThread in;
private OutputThread out;
private OutputThreadMap outputThreadMap;
public SocketTask(Socket socket) {
this.socket = socket;
outputThreadMap = OutputThreadMap.getInstance();
//socketThreadMap = SocketThreadMap.getInstance();
}
@Override
public void run() {
out = new OutputThread(socket, outputThreadMap);// 先实例化写消息线程,(把对应用户的写线程存入map缓存器中)
in = new ServerThread(socket, out, outputThreadMap);// 再实例化读消息线程
out.setStart(true);
in.setStart(true);
in.start();
out.start();
}
}
}
服务器读消息线程代码
private Socket socket;
private Gson gson;
private OutputThread out;// 传递进来的写消息线程,因为我们要给用户回复消息啊
private OutputThreadMap map;// 写消息线程缓存器
private SocketThreadMap socketThreadMap;// 写消息线程缓存器
private DataInputStream inputStream;// 对象输入流
private InputStreamReader iReader;
private boolean isStart = true;// 是否循环读消息
public long lastReceiveHeart;//上次接收心跳包时间
public ServerThread(Socket socket, OutputThread out, OutputThreadMap map) {
// TODO Auto-generated constructor stub
this.socket = socket;
this.out = out;
this.map = map;
try {
inputStream = new DataInputStream(socket.getInputStream());// 实例化对象输入流
} catch (IOException e) {
e.printStackTrace();
}
}
public void setStart(boolean isStart) {// 提供接口给外部关闭读消息线程
this.isStart = isStart;
}
@Override
public void run() {
// TODO Auto-generated method stub
try {
while (isStart) {
iReader = new InputStreamReader(inputStream, "UTF-8");
char[] buffer = new char[1024];
int count = 0;
String phone = null;
StringBuffer sBuilder = new StringBuffer();
while ((count = iReader.read(buffer, 0, buffer.length)) > -1) {
sBuilder.append(buffer, 0, count);
if (count < 1024 && count != 0) {
break;
}
}
gson = new GsonBuilder().setPrettyPrinting() // 格式化输出(序列化)
.setDateFormat("yyyy-MM-dd HH:mm:ss") // 日期格式化输出
.create();
JsonReader jsonReader = new JsonReader(new StringReader(sBuilder.toString()));// 其中jsonContext为String类型的Json数据
jsonReader.setLenient(true);
TranObject readObject = gson.fromJson(jsonReader, TranObject.class);
if (readObject != null )
{
lastReceiveHeart = System.currentTimeMillis();
phone = readObject.getFromUser();//手机号作为用户的标识
}
//如果距离接收心跳包的时间超过5分钟 说明用户掉线
if(System.currentTimeMillis() - lastReceiveHeart > 300000) {
try {
if (phone != null) {//更新用户状态
new UserDao().updateStatus(0, phone);
}
socket.close();
} catch (IOException e) {
// TODO: handle exception
e.printStackTrace();
}
}
TranObject serverResult = execute(readObject);
pushMessage(readObject);// 执行推送的消息
if (serverResult != null) {
out.setMessage(serverResult);
}
}
if (iReader != null) {
iReader.close();
}
if (inputStream != null) {
inputStream.close();
}
if (socket != null) {
socket.close();
}
} catch (IOException e) {
// TODO: handle exception
}
}
// 处理客户端发送过来的消息
private TranObject execute(TranObject readObject) {
//...省略代码...
}
/**
* 处理需要互相推送的消息
*
* @param readObject
*/
private void pushMessage(TranObject readObject) {
// ...省略代码...
}
服务器写消息线程
public class OutputThread extends Thread{
@SuppressWarnings("unused")
private OutputThreadMap map;
private SocketThreadMap socketThreadMap;
//private ObjectOutputStream oos;
private OutputStreamWriter oStreamWriter;
private DataOutputStream dataOutputStream;
private TranObject object;
private boolean isStart = true;// 循环标志位
private Socket socket;
public OutputThread(Socket socket, OutputThreadMap map) {
this.socket = socket;
this.map = map;
try {
dataOutputStream = new DataOutputStream(socket.getOutputStream());// 在构造器里面实例化对象输出流
} catch (IOException e) {
e.printStackTrace();
}
}
public void setStart(boolean isStart) {
this.isStart = isStart;
}
// 调用写消息线程,设置了消息之后,唤醒run方法,可以节约资源
public void setMessage(TranObject object) {
this.object = object;
synchronized (this) {
notify();
}
}
@Override
public void run() {
try {
while (isStart) {
// 没有消息写出的时候,线程等待
synchronized (this) {
wait();
}
if (object != null) {
Gson gson = new GsonBuilder()
.setPrettyPrinting() //格式化输出(序列化)
.setDateFormat("yyyy-MM-dd HH:mm:ss") //日期格式化输出
.create();
oStreamWriter = new OutputStreamWriter(dataOutputStream, "UTF-8");
String outputString = gson.toJson(object);
//dataOutputStream.writeInt(outputString.length());
//dataOutputStream.write(outputString.getBytes());
//dataOutputStream.flush();
StringBuffer sBuilder = new StringBuffer();
sBuilder.append(outputString);
oStreamWriter.write(sBuilder.toString());
oStreamWriter.flush();
if(object != null && object.getType()!=TranObjectType.HEART_TEST)
{
System.out.println(outputString);
}
}
}
if(oStreamWriter != null)
{
oStreamWriter.close();
}
if (dataOutputStream != null)// 循环结束后,关闭流,释放资源
dataOutputStream.close();
if (socket != null)
socket.close();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}