目的:
1.对数组,循环输入输出流,线程等方法的综合应用
2.巩固socket的概念和使用
3.学习网络编程,实现数据交互
4..锻炼编程思维
具体实现:
定义接口类,里面定义常量:
public interface ChatProtocol {
//登录
String LOGIN_FLAG ="u+";
//私聊
String PRIVATE_FLAG = "p+";
//群聊
String PUBLIC_FLAG = "a+";
//分隔符
String SPLIT_FLAG = "♥";
String SUCCESS = "1";
String FAILURE = "-1";
}
定义客户端及客户端子线程类
public class Client {
public static void main(String[] args) {
//链接服务器
BufferedReader br = null;
PrintStream ps = null;
BufferedReader brServer = null;
try (Socket socket= new Socket("10.129.0.138",8080)){
//登录
//接收终端收入流
br =new BufferedReader(new InputStreamReader(System.in));
//发给服务器端的输出流
ps=new PrintStream(socket.getOutputStream());
//接收服务器端的输入流
brServer =new BufferedReader(new InputStreamReader(socket.getInputStream()));
while(true){
//接收终端输入信息
//BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
//String line =br.readLine();
String line = JOptionPane.showInputDialog("请输入用户名:");
//拼接登录格式
String loginStr = ChatProtocol.LOGIN_FLAG+line+ChatProtocol.LOGIN_FLAG;
//发送给服务器端
//ps = new PrintStream(socket.getOutputStream());
ps.println(loginStr);
//接收服务器端返回的结果
//brServer = new BufferedReader(new InputStreamReader(socket.getInputStream()));
String result = brServer.readLine();
//判断登录结果
if (result.equals(ChatProtocol.SUCCESS)){
System.out.println("登录成功!");
break;}
else{System.out.println("用户名已存在,请重新登录!");
}
}
//登录成功
//开启线程
new ClientThread(socket).start();
//接收终端输入 发送给服务器端
String line = null;
while((line= br.readLine())!=null){
//发送给服务器
ps.println(line);
}
} catch (IOException e) {
}
}
}
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();
}
}
}
定义服务器端及服务器子线程类:
public class Server {
//用于保存每个用户对应的姓名和Socket
public static UserManager manager = new UserManager();
public static void main(String[] args){
//创建ServerSocket
try (ServerSocket ss = new ServerSocket(8080)){
//监听所有来连接的客户端
while(true){
Socket socket =ss.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 br=null;
PrintStream ps = null;
try {
//1.得到对应的输入流对象
br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
//得到对应的输出流
ps = new PrintStream(socket.getOutputStream());
String line = null;
while ((line=br.readLine())!=null) {
if(line.startsWith(ChatProtocol.LOGIN_FLAG)&&line.endsWith(ChatProtocol.LOGIN_FLAG))
{
//String[]items=line.substring(2).split("u+");
//String name = items[0];
//u+jacku+
//获取名字
String name= line.substring(2,line.length()-2);
//判断这个用户是否已经登录
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)){
//p+jack♥hellop+
//获取信息 jack♥hello
String msg = line.substring(2,line.length()-3);
//分割
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);
//发送私聊消息
desPs.println(currentName+"向你发来私聊"+message);
}else {
//群聊
//处理数据
String msg = line.substring(2,line.length()-2);
//获取当前用户的名称
String currentName = Server.manager.nameBySocket(socket);
//遍历所有的用户信息
Collection sockets = Server.manager.allusers();
for (Socket s:sockets) {
PrintStream tempps = new PrintStream(s.getOutputStream());
tempps.println(currentName+"发来群聊:"+msg);
}
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
用户管理:
public class UserManager {
//保存所有用户信息
private Map 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对象找到对应的mingc
*/
public String nameBySocket(Socket socket){
for (String key:users.keySet()){
//取出这个key对应的名称
if(socket==users.get(key)){
return key;
}
}
return null;
}
/**
* 获取所有人的socket对象
*/
public synchronized Collection allusers(){
return users.values();
}
}
结果:
小结:
上课感觉就是听了思路就跟不上操作,打了代码就丢了思路,心累。所以课后练习和理解很重要,多看视频。