您的位置:首页 > 理论基础 > 计算机网络

Java网络编程基础(四)---基于TCP的简单聊天系统

2016-05-23 16:55 906 查看
实现思路:
要实现消息的发送,客户端每次在连接服务器端时都需要告诉服务器自己的用户名,以便能够接收到发送给自己的消息。服务器端在接收到消息时,能够查到对应用户名的客户端,将消息发送给该客户端。
服务端需要实现多个客户端的同时请求,因此需要使用多线程来处理客户端的请求。同时,它还需要保存一份用户名列表,以便在转发消息时能够查找到对应的用户。
对于客户端来说,客户端需要能够随时收取服务器端转发来的消息,并能够随时通过键盘输入发送消息,因此它的读取消息和发送消息功能是并行的,需要独立的线程来实现。

实现过程:
根据以上思路,客户端和服务端都需要独立的线程,因此分别包含了一个主程序和一个子线程类,共需要实现4个类。
(1)、客户端主程序ChatClient.java
该程序根据启动时输入的用户名参数,负责启动客户端的子线程,由子线程建立客户端和服务器的连接。在主线程中,需要循环读取键盘的输入,将输入的信息通过子线程发送给服务器,交由服务器来转发该消息给客户端。
(2)、客户端子线程ClientThread.java
该线程封装了客户端和服务器的所有操作。
a、在构造函数中,根据用户名创建与服务端的连接,然后发送用户名给服务端进行注册
b、在线程函数中,循环读取服务端发送的消息,并显示的控制台
c、还应有一个发送消息的函数,外部的主线程在接收到键盘输入后,调用该函数给服务端发送消息
d、关闭函数:在客户端输入bye命令后,关闭客户端和服务端连接。
(3)、服务端主程序ChatServer.java
该类负责启动服务器,并负责监听客户端的请求,在接收到新的用户端后,交给子线程处理。
(4)、服务端子线程ServerThread.java
该线程负责处理客户端的输入\输出和消息转发任务,因此需要包含以下功能
a、在构造函数中,建立与客户端的输入\输出流,并在第一次建立连接时读取客户端发送的用户名,并将该用户添加到用户列表中。
b、在主线程函数中,读取客户端的输入数据,如果发送的消息时bye,则推出监听关闭该客户端,否则根据命令格式进行解析。冒号(:)前的部分为用户名,根据该用户名在客户端列表中找到对应的客户端处理线程,发送数据给客户端
c、发送数据函数:在收到用户消息进行转发时调用
参考样例
1、客户端

package org.test.example;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

/**
* @author kucs
* 客户端主程序
* 负责调用子线程创建客户端
* 监听键盘输入
* 根据系统启动参数取得用户名
* 调用子线程创建该用户的客户端线程并启动
*/
public class ChatClient {

public static void main(String[] args) {

try {
ClientThread client = new ClientThread(args[0]);
client.start();

//输入\输出流
BufferedReader sin = new BufferedReader(new InputStreamReader(System.in));
//循环读取键盘输入
String readLine;
while((readLine = sin.readLine()) != null){
if(readLine.equals("bye")){
client.close();
System.exit(0);
}
client.send(readLine);
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}

}
2、客户端线程
package org.test.example;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.net.UnknownHostException;

/**
* @author kucs
*
* 受主线程委托,来创建与服务端的连接
* 监听服务端的消息
* 负责发送消息给服务端
*/
public class ClientThread extends Thread {
Socket socket;        //socket对象
BufferedReader is;    //输入流
PrintWriter os;        //输出流

//启动客户端
public ClientThread(String userName) {
//连接服务器
try {
socket = new Socket("127.0.0.1",12345);

//输入输出流
is = new BufferedReader(new InputStreamReader(socket.getInputStream()));
os = new PrintWriter(socket.getOutputStream());

//发送用户名
os.println(userName);
os.flush();
} catch (UnknownHostException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}

@Override
public void run() {
try {
// TODO 循环读取服务端转发的消息
String readLine;
while((readLine = is.readLine()) != null){
System.out.println(readLine);
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}

public void close() {
try {
// TODO 关闭客户端
is.close();
os.close();

socket.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}

public void send(String readLine) {
// TODO 发送消息
os.print(readLine);
os.flush();
}

}
3、服务端
package org.test.example;

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Hashtable;

/**
* @author kucs
*
*/
public class ChatServer {
public static void main(String[] args) {
ServerSocket server = null;
try {
// TODO 启动服务端
server = new ServerSocket(12345);
System.out.println("服务端启动:"+
server.getInetAddress().getHostAddress()+":"+
server.getLocalPort());

//客户端列表
Hashtable<String, ServerThread> clientList = new Hashtable<String, ServerThread>();

//监听客户端
while(true){
Socket socket = server.accept();
//启动服务端处理线程
new ServerThread(socket,clientList).start();
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally{
try {
server.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}

}
4、服务端线程
package org.test.example;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Hashtable;

public class ServerThread extends Thread {

Socket socket;                                //Socket对象
BufferedReader is;                            //输入流
PrintWriter os;                                //输出流
Hashtable<String, ServerThread> clientList;    //客户端列表
String userName;                            //用户名
public ServerThread(Socket socket, Hashtable<String, ServerThread> clientList) {
this.socket = socket;
this.clientList = clientList;

try {
//输入输出流
is = new BufferedReader(new InputStreamReader(socket.getInputStream()));
os = new PrintWriter(socket.getOutputStream());

//读取用户名
this.userName = is.readLine();
clientList.put(userName, this);

//显示连接信息
System.out.println(userName+"连接:"
+ socket.getInetAddress().getHostAddress()+":"
+ socket.getLocalPort());
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
@Override
public void run() {
// TODO 循环处理客户端输入
try {
String line;
while((line = is.readLine()) != null){
//用户输入信息
System.out.println(userName + "发给"+line);
if(line.equals("bye")){
break;
}else{
//转发消息给客户端
String[] arr = line.split(":");
if(arr.length == 2){
if(clientList.containsKey(arr[0])){
clientList.get(arr[0]).send(userName+":"+arr[1]);
}
}
}
}
is.close();
os.close();

//关闭客户端
socket.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
private void send(String msg) {
// TODO 发送消息
System.out.println("发给" + userName +" "+msg);
os.println(msg);
os.flush();
}

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  java TCP 网络编程