java Socket 简单实现客户端与服务器间通信(仿聊天室)
java Socket TCP协议简单实现客户端与服务器间的通信
执行效果:
- 启动服务器和3个客户端
- 进行群聊和私聊
执行过程:
-
服务端:
首先创建服务器套接字ServerSocket对象并绑定端口(启动服务器),然后ServerSocket调用accept方法阻塞式等待接收一个客户端的请求,每接收一个请求就返回一个该客户端的Socket对象(有了Socket对象就可以获得该客户端的输入输出流然后进行消息传送),开启 新一个线程去处理该客户端的消息,比如客户端上线消息,客户端下线消息,群发消息,私聊消息再进行相应的处理。
注意:Socket对象获得该客户端的输入输出流也是阻塞式的,比如readline方法读取 到“\n”才会算读取结束否则程序一直阻塞在那,但read方法却不知道什么时候读取结束因为它不知道发送方什么时候发送消息完毕,除非发送方关闭输出流,然后read读取到-1它才会读取结束,但发送方关闭输出流后无法再向接收方发送消息了,这里没有涉及但就接收方什么时候读取消息结束是个值得考虑的问题。
-
客户端:
首先根据服务器地址和端口创建客户端套接字Socket对象,同上有了Socket对象就可以获得对服务器的输入输出流然后进行消息读写,然后再创建一个线程监听处理服务器的消息,之后获取用户输入再发送给服务器即可(这里输入exit消息代表退出服务器,输入@xxx:message格式消息代表私聊发送message给xxx客户端,其他消息统一为群聊消息,服务器接收后直接转发给其他所有客户端)
服务端代码
public class Server {
private int port = 5210;
private ServerSocket server_socket;
private Map<String,Socket> clients; //存放所有访问服务器的客户端和其用户名
public Server() throws Exception {
//1-初始化
server_socket = new ServerSocket(this.port); //创建服务器
clients = new HashMap<>();
//2-每次接收一个客户端请求连接时都启用一个线程处理
while(true) {
//accept方法一直阻塞直到接收到一个客户端的请求 并返回该客户端的套接字socket
Socket client_socket = server_socket.accept();
//开启新线程处理客户端的请求
new HandleClientThread(client_socket).start();
}
}
/** ----------------------------------------------------------------------------------------------------
* 启用一个线程处理一个客户端的请求
*/
private class HandleClientThread extends Thread{
private Socket client_socket; //
private BufferedReader server_in;
private PrintWriter server_out;
private String client_Name;
public HandleClientThread(Socket client_socket) {
try {
//初始化
this.client_socket = client_socket;
server_in = new BufferedReader(new InputStreamReader(this.client_socket.getInputStream()));
server_out = new PrintWriter(this.client_socket.getOutputStream(),true);
//通知刚连上的客户端输入用户名
server_out.println("您已成功连接上聊天服务器!请输入你的用户名");
} catch (IOException e) {
e.printStackTrace();
}
}
//处理客户端消息
public void run(){
try {
int falg = 0; //判断该客户端是否第一次访问
String fromClientData;
//循环接收该客户端发送的数据
while((fromClientData = server_in.readLine()) != null){
//该客户端请求关闭服务器的连接
if("exit".equals(fromClientData)){
Server.this.offline(this.client_Name);
break;
}
//判断该客户端是否第一次访问
if(falg++ == 0){
this.client_Name = fromClientData;
clients.put(this.client_Name,this.client_socket);
sendtoAllClient("欢迎:"+this.client_Name+"进入聊天室");
Server.this.showUserList();
continue;
}
//处理私聊 格式 @接收客户端的名字:对其说的话
if(fromClientData.startsWith("@")){
String receiveName = fromClientData.substring(1,fromClientData.indexOf(":"));
String message = fromClientData.substring(fromClientData.indexOf(":")+1,fromClientData.length());
sendtoUser(this.client_Name,receiveName,message);
}else {
//处理群发群发
sendtoAllClient(this.client_Name+"说:"+fromClientData);
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
//显示所有用户列表
private synchronized void showUserList(){
System.out.println("\n*****用户列表****");
System.out.println("在线人数:"+clients.size());
for (Map.Entry<String,Socket> user:clients.entrySet()){
System.out.println("---"+user.getKey());
}
}
//广播消息message给所有客户端
private synchronized void sendtoAllClient(String message){
try {
//获取所有客户端套接字socket的输出流
for (Map.Entry<String,Socket> user:clients.entrySet()) {
PrintWriter server_out = new PrintWriter(user.getValue().getOutputStream(),true);
server_out.println(message);
}
} catch (IOException e) {
e.printStackTrace();
}
}
//转发消息给某个特定的客户端
private synchronized void sendtoUser(String senduser,String receiveuser,String message){
try {
PrintWriter out = new PrintWriter( clients.get(receiveuser).getOutputStream(),true);
out.println(senduser+"对你说:"+message);
} catch (IOException e) {
e.printStackTrace();
}
}
//某个客户端退出服务器
private synchronized void offline(String username){
clients.remove(username);
if(username != null)
sendtoAllClient(username+"已下线!");
Server.this.showUserList();
}
public static void main(String[] args) throws Exception {
new Server();
}
}
客户端
public class Client {
private String ip_adress = "127.0.0.1";
private final int port = 5210;
private PrintWriter client_out; //发送消息给服务器的输出流
private BufferedReader client_in; //接收服务器消息的输入流
private Socket client_socket; //客户端套接字
public Client() throws IOException {
//1-初始化
client_socket = new Socket(ip_adress,port); //根据ip,端口连接服务器
client_in = new BufferedReader(new InputStreamReader(client_socket.getInputStream()));
client_out = new PrintWriter(client_socket.getOutputStream(),true);
//2-开启新线程处理监听服务器端发来的消息
new ClientThread().start();
//3-客户端循环发送群聊,私聊消息
while(true){
//获取客户端的输入
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String input = br.readLine();
client_out.println(input); //发送消息给服务器
//4-客户端退出服务器
if(input.equals("exit")){
client_out.close();
client_in.close();
client_socket.close();
System.exit(0);
}
}
}
//关闭客户端
private void quitClient(){
try {
client_out.close();
client_in.close();
client_socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
//-----------------------------------------------------------------
/**
* 监听服务器端发来的消息
*/
private class ClientThread extends Thread{
public void run(){
try {
//接收服务器消息
String fromServer_data;
while((fromServer_data=client_in.readLine()) != null){
System.out.println(fromServer_data);
}
} catch (IOException e) {
}
}
}
//启动客户端
public static void main(String[] args) throws IOException {
new Client();
}
}