简易TCP项目,实现小聊天室
项目结构:
TcpServer服务端代码:
package com.example.socket;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
/**
* 服务端代码
*/
public class TcpServer {
public void start(){
ServerSocket serverSocket = null;
try {
serverSocket = new ServerSocket(9009);
MsgPool.getsInstacne().start();//启动消息队列
while (true){
Socket socket = serverSocket.accept();//等待tcp连接到来
System.out.println("ip="+socket.getInetAddress().getHostAddress()
+",port="+socket.getPort()+" is Online...");
//避免读写操作阻塞线程,接收不到下一个连接,使用线程
// socket.getInputStream();//客户端数据
// socket.getOutputStream();//向客户端写数据
ClientTask clientTask = new ClientTask(socket);
MsgPool.getsInstacne().addMsgComingListner(clientTask);
clientTask.start();
}
} catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args){
new TcpServer().start();
}
}
消息队列类:
package com.example.scoket.tcp.server;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.LinkedBlockingDeque;
/**
* 消息队列,用来通知所有客户端
*/
public class MsgPool {
/**
* 阻塞队列
* 1.有消息发送给队列,启动一个线程专门从队列取消息
*
* 2.如果队列没有消息时会阻塞
*/
private LinkedBlockingDeque<String> mQueue = new LinkedBlockingDeque<>();
//单例
private MsgPool(){
}
private static MsgPool sInstacne = new MsgPool();
public static MsgPool getsInstacne(){
return sInstacne;
}
/**
* 开启线程去队列读数据
*/
public void start(){
new Thread(){
@Override
public void run() {
while (true){
try {
String msg = mQueue.take();
notifyMsgComing(msg);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}.start();
}
//观察者模式
public interface MsgComingListener{
void onMsgComing(String msg);
}
private List<MsgComingListener> msgComingListenerList = new ArrayList<>();
public void addMsgComingListner(MsgComingListener listener){
msgComingListenerList.add(listener);
}
private void notifyMsgComing(String msg) {
for (MsgComingListener listener:msgComingListenerList) {
listener.onMsgComing(msg);
}
}
public void sendMsg(String msg){
try {
mQueue.put(msg);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
异步任务类给客户端传消息:
package com.example.socket.tcp.server;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.Socket;
public class ClientTask extends Thread implements MsgPool.MsgComingListener{
private Socket mSocket;
private InputStream mIs;
private OutputStream mOs;
public ClientTask(Socket socket){
mSocket = socket;
try {
mIs = socket.getInputStream();
mOs = socket.getOutputStream();
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 此线程 客户端没有主动退出 是一直持续的
*/
@Override
public void run() {
BufferedReader br = new BufferedReader(new InputStreamReader(mIs));
String line = null;
try {
while ((line = br.readLine())!=null){//等待客户端发送消息,没有消息,readLine会阻塞
System.out.println("read---"+line);
//聊天室内,转发消息至其他客户端 并发处理 -->使用消息队列 只有一个线程去里面取数据分发数据
MsgPool.getsInstacne().sendMsg(mSocket.getPort()+" :"+line);
}
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void onMsgComing(String msg) {
try {
mOs.write(msg.getBytes());
mOs.write("\n".getBytes());
mOs.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
}
TCP业务类:
package com.example.socket.tcp.client;
import android.os.Handler;
import android.os.Looper;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.Socket;
public class TcpClientBiz {
private Socket mSocket;
private InputStream mIs;
private OutputStream mOs;
private Handler mUiHandler = new Handler(Looper.getMainLooper());
public void onDestroy() {
//多个try catch防止第一个出异常后面 的资源无法关闭
try {
if(mSocket!=null) {
mSocket.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if(mIs!=null){
mIs.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if(mOs!=null){
mOs.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
public interface OnMsgComingListener{
void onMsgComing(String msg);
void onError(Exception ex);
}
private OnMsgComingListener mListener;
public void setOnMsgComingListener(OnMsgComingListener listener){
mListener = listener;
}
public TcpClientBiz() {
new Thread(){//防止在ui线程作网络操作
@Override
public void run() {
try {
mSocket = new Socket("192.168.207.2", 9009);
mIs = mSocket.getInputStream();
mOs = mSocket.getOutputStream();
final BufferedReader br = new BufferedReader(new InputStreamReader(mIs));
//循环读取服务端数据
String line = null;
while ((line = br.readLine())!=null){
final String finalLine = line;
mUiHandler.post(new Runnable() {
@Override
public void run() {
if(mListener!=null){
mListener.onMsgComing(finalLine);
}
}
});
}
} catch (final IOException e) {
mUiHandler.post(new Runnable() {
@Override
public void run() {
if(mListener!=null){
mListener.onError(e);
}
}
});
}
}
}.start();
}
public void sendMsg(final String msg){
new Thread(){
@Override
public void run() {
try {
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(mOs));
bw.write(msg);
bw.newLine();
bw.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
}.start();
}
}
TCPActivity:
package com.example.socket;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.text.TextUtils;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import com.example.socket.Biz.UdpBiz;
import com.example.socket.tcp.client.TcpClientBiz;
public class TcpActivity extends AppCompatActivity {
private TextView tv_result;
private Button btn_send;
private EditText et_input;
private TcpClientBiz tcpBiz = new TcpClientBiz();//需优化 先loading建立连接初始化完成后再做交互
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
tcpBiz.setOnMsgComingListener(new TcpClientBiz.OnMsgComingListener() {
@Override
public void onMsgComing(String msg) {
tv_result.append(msg);
}
@Override
public void onError(Exception ex) {
ex.printStackTrace();
}
});
}
private void initView(){
tv_result = findViewById(R.id.tv_result);
btn_send = findViewById(R.id.btn_send);
et_input = findViewById(R.id.et_input);
btn_send.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
String msg = et_input.getText().toString();
if(TextUtils.isEmpty(msg)){
return;
}
tcpBiz.sendMsg(msg);
}
});
}
@Override
protected void onDestroy() {
super.onDestroy();
tcpBiz.onDestroy();//释放资源
}
}
实现效果:
服务端----
android----