一、服务器
思路:
首先创建服务器,用一个死循环等候若干个客户端的连接。一旦有客户端连接,就把客户端添加到集合当中,并且启动一个新的线程来保持长连接,监控客户端发来的信息。一旦接收到有客户端发来的信息,就进行包装后遍历集合,把包装好的信息发送给每一个客户端。
代码如下:
/**
* Tcp通信服务器
* @author Devin Chen
*
*/
public class CSServer {
private static final int PORT = 8888;
private List<Socket> mClientList = new ArrayList<Socket>();
private ServerSocket server = null;
private ExecutorService mExecutors = null; // 线程池对象
public static void main(String[] args) {
new CSServer();
}
/**
* 构造方法:任务是启动服务器,等待客户端连接
*/
public CSServer() {
try {
server = new ServerSocket(PORT);
mExecutors = Executors.newCachedThreadPool(); // 创建线程池
System.out.println("服务器已启动,等待客户端连接...");
Socket client = null;
/*
* 用死循环等待多个客户端的连接,连接一个就启动一个线程进行管理
*/
while (true) {
client = server.accept();
// 把客户端放入集合中
mClientList.add(client);
mExecutors.execute(new Service(client)); // 启动一个线程,用以守候从客户端发来的消息
}
} catch (Exception e) {
e.printStackTrace();
}
}
class Service implements Runnable {
private Socket socket;
private BufferedReader in = null;
private String message = "";
public Service(Socket socket) {
this.socket = socket;
try {
in = new BufferedReader(new InputStreamReader(
socket.getInputStream()));// 获得输入流对象
// 客户端只要一连到服务器,便发送连接成功的信息
message = "服务器地址:" + this.socket.getInetAddress();
this.sendMessage(message);
message = "当前连接总数:" + mClientList.size();
this.sendMessage(message);
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void run() {
try {
while (true) {
if ((message = in.readLine()) != null) {
// 当客户端发送的信息为:exit时,关闭连接
if (message.equals("exit")) {
closeSocket();
break;
} else {
// 接收客户端发过来的信息message,然后转发给客户端。
message = socket.getInetAddress() + ":" + message;
this.sendMessage(message);
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 关闭客户端
*
* @throws IOException
*/
public void closeSocket() throws IOException {
mClientList.remove(socket);
in.close();
message = "主机:" + socket.getInetAddress() + "关闭连接\n目前在线:"
+ mClientList.size();
socket.close();
this.sendMessage(message);
}
/**
* 将接收的消息转发给每一个客户端
*
* @param msg
*/
public void sendMessage(String msg) {
System.out.println(msg);// 先在控制台输出
int count = mClientList.size();
// 遍历客户端集合
for (int i = 0; i < count; i++) {
Socket mSocket = mClientList.get(i);
PrintWriter out = null;
try {
out = new PrintWriter(new BufferedWriter(
new OutputStreamWriter(mSocket.getOutputStream())),
true);// 创建输出流对象
out.println(msg);// 转发
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
二、Android客户端
思路:
设置权限。
首先开辟线程,在线程中连接服务器,并获取输入流和输出流,并用一个死循环接收来自服务器的消息,输入流收到消息进行处理,用handler通知给UI线程更新文本框。
编辑框填好消息,直接点击发送。用获得的输出流发送消息。
代码:
/**
* Android Tcp即时通讯客户端
*/
public class MainActivity extends Activity implements Runnable{
private TextView tv_msg = null;
private EditText ed_msg = null;
private Button btn_send = null;
private static final String HOST = "192.168.0.100";//服务器地址
private static final int PORT = 8888;//连接端口号
private Socket socket = null;
private BufferedReader in = null;
private PrintWriter out = null;
//接收线程发送过来信息,并用TextView追加显示
public Handler mHandler = new Handler() {
public void handleMessage(Message msg) {
super.handleMessage(msg);
tv_msg.append((CharSequence) msg.obj);
}
};
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tv_msg = (TextView) findViewById(.txt_1);
ed_msg = (EditText) findViewById(.et_talk);
btn_send = (Button) findViewById(.btn_send);
btn_send.setOnClickListener(new Button.OnClickListener() {
@Override
public void onClick(View v) {
String msg = ed_msg.getText().toString();
if (socket.isConnected()) {//如果服务器连接
if (!socket.isOutputShutdown()) {//如果输出流没有断开
out.println(msg);//点击按钮发送消息
ed_msg.setText("");//清空编辑框
}
}
}
});
//启动线程,连接服务器,并用死循环守候,接收服务器发送过来的数据
new Thread(this).start();
}
/**
* 连接服务器
*/
private void connection() {
try {
socket = new Socket(HOST, PORT);//连接服务器
in = new BufferedReader(new InputStreamReader(socket
.getInputStream()));//接收消息的流对象
out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(
socket.getOutputStream())), true);//发送消息的流对象
} catch (IOException ex) {
ex.printStackTrace();
ShowDialog("连接服务器失败:" + ex.getMessage());
}
}
/**
* 如果连接出现异常,弹出AlertDialog!
*/
public void ShowDialog(String msg) {
new AlertDialog.Builder(this).setTitle("通知").setMessage(msg)
.setPositiveButton("ok", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
}
}).show();
}
/**
* 读取服务器发来的信息,并通过Handler发给UI线程
*/
public void run() {
connection();// 连接到服务器
try {
while (true) {//死循环守护,监控服务器发来的消息
if (!socket.isClosed()) {//如果服务器没有关闭
if (socket.isConnected()) {//连接正常
if (!socket.isInputShutdown()) {//如果输入流没有断开
String getLine;
if ((getLine = in.readLine()) != null) {//读取接收的信息
getLine += "\n";
Message message=new Message();
message.obj=getLine;
mHandler.sendMessage(message);//通知UI更新
} else {
}
}
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
运行效果:
如果要扩展优化成一个有模有样的即时通讯工具。考虑发发送的消息用json进行格式化,包含用户昵称和图片地址等等,服务器收到后解析,在添加上时间等进行重新包装。客户端收到消息,同样进行解析,装载到美化好的listview当中。