今天下午学习了群聊服务器的编程,因为群聊服务器需要同步运行,所以理所当然的运用到了多线程。
把接受服务的客户端封装到一个客户端类里面去,就可以实现群聊了。
然后就是登陆操作-------> 客户端---服务器 交互
就是服务器的输出send()和接收get()。
这里再一次体现了类的封装性,可以把send()、get()方法封装到一个工具类里面去。
废话少说,上代码:
ServerSocket ss; try { //telnet localhost 9090 //创建一个服务器对象,传入一个端口号,输出建立服务完成提示 ss = new ServerSocket(9090); while(true){ System.out.println("已建立服务器"); //创建要接收服务的对象,获取套接字,获取服务,阻塞代码 Socket client = ss.accept(); //输出客户端地址 System.out.println("IP地址是:"+client.getRemoteSocketAddress()); ServerThread st = new ServerThread(client); st.start(); }这是重新编写服务器类的代码
public void run() { try{ //创建输入输出流 OutputStream out = client.getOutputStream(); InputStream ins = client.getInputStream(); //登录用户名 String msg = "Please input username:"; Tool.send(out, msg); //接收用户名输入 String username = Tool.get(ins); //请输入密码 msg = "Please input password:"; Tool.send(out,msg); //接收密码输入 String password = Tool.get(ins); //检测用户名和密码,如果正确就进入聊天 if(Tool.check(username, password)){ //发送消息 new Thread(){ public void run() { String str = "welcom !"; byte[] b = str.getBytes(); try{ out.write(b); out.flush(); //可以接收客户端发出的信息,直到客户端发出bye字符串 while(!"bye".equals(str)){ int value = ins.read(); str = ""; while(value!=13){ str = str + (char)value; value = ins.read(); } str = str.trim();//去掉字符串前后的空格和回车符 System.out.println(username+":"+str); } }catch(Exception e){ e.printStackTrace(); } }; }.start(); //接收消息 Scanner scanner = new Scanner(System.in); while(true){ String scanMsg = scanner.nextLine(); Tool.send(out, scanMsg+"\r\n"); } }else{//登录失败 String msgs = "your username or password is wrong!"; Tool.send(out,msgs); } }catch(Exception e){ e.printStackTrace(); } }
public class Tool { /** * 服务器向客户端发送数据 * @param out * @param msg */ public static void send(OutputStream out,String msg){ byte[] b = msg.getBytes(); try { out.write(b); out.flush(); } catch (IOException e) { e.printStackTrace(); } } /** * 服务器获取客户端输入的数据 * @param ins * @return */ public static String get(InputStream ins){ String msg = ""; try{ int v = ins.read(); while(v!=13){ msg = msg + (char)v; v = ins.read(); } msg = msg.trim(); }catch(Exception e){ e.printStackTrace(); } return msg; } //检测用户名和密码 /** * 检测用户名和密码是否正确 * @param username * @param password * @return */ public static boolean check(String username,String password){ if(MyServer.data.containsKey(username)){ if(password.equals(MyServer.data.get(username))){ return true; } } return false; } }
至于用户名密码的检测,则是用HashMap键值对容器来充当数据库的角色存放用户名密码,用static静态变量定义,像工具类的工具方法一样,这样就可以直接用类名来引用了。
为了方便检测,我先不编写注册的代码,而是用static{}代码块来初始化用户名和密码。
static{}代码块是在加载类的时候运行的,而代码块{}则是在创建对象的时候运行的。(如果我没记错)
代码如下:
public static HashMap<String,String> data = new HashMap<String,String>(); static{ for(int i=0;i<10;i++){ data.put("user"+i, "psw"+i); } }好了,这就是今天学到的服务器群聊。
上图:
洗洗睡,明天早上得去上课。