一、目的
实现下面效果:
1.每个客户端一个名称
2.私聊
3.群聊
4.发文件
客户端只能向服务器端发送文件或者文字 服务器端只能得到客户端发来的数据,所以 必须服务器端和客户端有一个规范
客户的意图可以在其发送的字符里面体现
- 1.登录 u+姓名u+
- 2.私聊 p+姓名!聊天内容p+
- 3.群聊 a+ 聊天内容 a+
- 4.发文件 f+
- 5.发语音 v+
服务器的返回体现意思
- 登录 成功 1 失败 -1
二、代码
1.服务器端
主线程
public class Server {
//保存每个用户的姓名和socket
public static UserManager manager = new UserManager();
public static void main(String[] args){
try(ServerSocket serverSocket = new ServerSocket(8000)){
//监听所有来链接的客户端
while(true){
Socket socket = serverSocket.accept();
//让子线程处理socket
new ServerThread(socket).start();
}
}catch (IOException e){
//e.printStackTrace();
}
}
}
子线程
class ServerThread extends Thread{
private Socket socket;
public ServerThread(Socket socket){
this.socket = socket;
}
@Override
public void run() {
BufferedReader bufferedReader = null;
PrintStream ps = null;
try {
//登录
//得到对应的输入流
bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
//得到对应的输出流
ps = new PrintStream(socket.getOutputStream());
String line = null;
while((line = bufferedReader.readLine()) != null){
if(line.startsWith(ChatProtocol.LOGIN_FLAG)&&line.endsWith(ChatProtocol.LOGIN_FLAG)) {
//获取名字
int endIndox = line.length()-2;
String name = line.substring(2, endIndox);
判断这个用户是否已经登录
if (Server.manager.isLogined(name)) {
//已登录
//发送结果给客户端
ps.println(ChatProtocol.FAILURE);
} else {
//未登录
//保存当前登录的用户信息
Server.manager.save(name, socket);
//发送结果给客户端
ps.println(ChatProtocol.SUCCESS);
}
}
判断是不是私聊
else if(line.startsWith(ChatProtocol.PRIVATE_FLAG)&&line.endsWith(ChatProtocol.PRIVATE_FLAG)){
//获取信息
int endIndox = line.length()-2;
String msg = line.substring(2,endIndox);
//分割
String[] items = msg.split(ChatProtocol.SPLIT_FLAG);
//用户名
String name = items[0];
//聊天内容
String message = items[1];
//通过用户名找到对应的socket
Socket desSocket = Server.manager.socketByName(name);
PrintStream desPs = new PrintStream(desSocket.getOutputStream());
//获取当前用户的名称
String currentName = Server.manager.nameBySocket(socket);
//System.out.println(currentName);
//发送私聊
desPs.println(currentName+"发来私聊:"+message);
}else if(line.startsWith(ChatProtocol.PUBLIC_FLAG)&&line.endsWith(ChatProtocol.PUBLIC_FLAG)){
群聊
//信息处理
int endIndox = line.length()-2;
String msg = line.substring(2,endIndox);
//获取当前用户的名称
String currentName = Server.manager.nameBySocket(socket);
//遍历所有用户信息
Collection<Socket> sockets = Server.manager.allUsers();
for(Socket s:sockets){
PrintStream temps = new PrintStream(s.getOutputStream());
temps.println("群消息 "+currentName+":"+msg); }
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
2.客户端
Client1
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.Socket;
import javax.swing.JOptionPane;
public class Client {
public static void main(String[] args){
BufferedReader br = null;
PrintStream ps = null;
BufferedReader brServer = null;
//连接服务器 IP:可以是自己的电脑IP,也可以是一起编程的伙伴的电脑IP 8000是端口,如果该端口被占用,可改为其他的
try(Socket socket = new Socket("102.19.1.205",8000)){
//登录
//接收终端输入信息
br = new BufferedReader(new InputStreamReader(System.in));
//发送给服务器端
ps = new PrintStream(socket.getOutputStream());
//接收服务器端的返回结果
brServer = new BufferedReader(new InputStreamReader(socket.getInputStream()));
while(true){
String line = JOptionPane.showInputDialog("请输入用户名");
//拼接登录格式
String loginStr = ChatProtocol.LOGIN_FLAG+line+ChatProtocol.LOGIN_FLAG;
//发送给服务器端
ps.println(loginStr);
//接收服务器端的返回结果
String result = brServer.readLine();
//判断登录结果
if(result.equals(ChatProtocol.SUCCESS)){
System.out.println("登录成功");
break;
}else{
System.out.println("用户名已存在, 请重新登录");
}
}
//登录成功
//开启线程处理服务器端的输入
new ClientThread(socket).start();//子线程接收服务器端输入
//接收终端输入 发送给服务器端
String line ;
while ((line = br.readLine()) != null){
//发送给服务器
ps.println(line);
}
} catch (IOException e) {
// e.printStackTrace();
}
}
}
Client2
class Client2 {
public static void main(String[] args){
BufferedReader br = null;
PrintStream ps = null;
BufferedReader brServer = null;
//连接服务器
try(Socket socket = new Socket("102.19.1.205",8000)){
//登录
//接收终端输入信息
br = new BufferedReader(new InputStreamReader(System.in));
//发送给服务器端
ps = new PrintStream(socket.getOutputStream());
//接收服务器端的返回结果
brServer = new BufferedReader(new InputStreamReader(socket.getInputStream()));
while(true){
String line = JOptionPane.showInputDialog("请输入用户名");
//拼接登录格式
String loginStr = ChatProtocol.LOGIN_FLAG+line+ChatProtocol.LOGIN_FLAG;
//发送给服务器端
ps.println(loginStr);
//接收服务器端的返回结果
String result = brServer.readLine();
//判断登录结果
if(result.equals(ChatProtocol.SUCCESS)){
System.out.println("登录成功");
break;
}else{
System.out.println("用户名已存在, 请重新登录");
}
}
//登录成功
//开启线程处理服务器端的输入
new ClientThread(socket).start();//子线程接收服务器端输入
//接收终端输入 发送给服务器端
String line ;
while ((line = br.readLine()) != null){
//发送给服务器
ps.println(line);
}
} catch (IOException e) {
// e.printStackTrace();
}
}
}
子线程
class ClientThread extends Thread{
private Socket socket ;
public ClientThread(Socket socket){
this.socket = socket;
}
@Override
public void run() {
BufferedReader br = null;
try {
br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
String line = null;
while ((line = br.readLine()) != null){
System.out.println(line);
}
} catch (IOException e) {
System.out.println("网络出错!");
}finally {
try {
if(br != null) {
br.close();
}
if( socket!= null) {
socket.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
3.UserManager类:管理所有的登录的用户Map<String,Socket>和判断某个用户是否已经登录
public class UserManager {
//保存所有用户信息
private Map<String, Socket> users = new HashMap<>();
/**
* 判断用户是否登录
*/
public boolean isLogined(String name){
//遍历数组
for(String key:users.keySet()){
if (key.equals(name)){
return true;
}
}
return false;
}
/**
* 保存当前登录的用户信息
*/
public void save(String name,Socket socket){
users.put(name, socket);
}
/**
* 通过用户名找到对应的socket
*/
public Socket socketByName(String name){
return users.get(name);
}
/**
* 通过socket对象找到对应的名称
*/
public String nameBySocket(Socket socket){
for(String key:users.keySet()){
//取出这个key对应的socket
if(socket == users.get(key)){
return key;
}
}
return null;
}
/**
* 获取所有人的socket对象
*/
public Collection<Socket> allUsers(){
return users.values();
}
}
4.ChatProtocol接口:用于制定一个规则
public interface ChatProtocol {
//登录
String LOGIN_FLAG = "u+";
//私聊
String PRIVATE_FLAG = "p+";
//群聊
String PUBLIC_FLAG = "a+";
//分隔符
String SPLIT_FLAG = "*";
//成功的状态
String SUCCESS = "1";
String FAILURE = "-1";
}
三、运行效果
登录
私聊
群聊
四、心得
"旁观者的名字永远比赛的记分板上"
现在的参与,是为了一天能够站在赛场上,并且有赢得漂亮的能力,好好学吧。