通信小结
最近好多天都在写通信的程序,刚开始讲时,服务器和客户端都没用到自己设计的界面,觉得很简单,服务器端有一个ServerSocket,客户端有一个Socket,简简单单的建立一个通道,后来给服务器和客户端加界面时,才发现自己并没有真正的掌握这些知识,胡哥刚讲完同步画板时,吕旭当天上午就实现了,而我觉得好高深啊,怎么实现的???虽然有些交流可总觉得自己还没入门。那两天真是不知道天天在写什么,有时听得懂被人讲的,看得懂别人的代码,就是自己不会写。。。(用某人的话说这些没有意义)
周二早上怀着惴惴不安的心情来到公司,因为没写完作业,周一休息,不会写也不怎么想写,(不会俩字很敏感)书上代码敲了一遍,敲完基本什么也没记住。。。周二上午没讲课,大神李伟给我仔仔细细分析了框架,说了大略思路,唉,觉得自己明白了,开悟了。。。
为了给设计程序一个良好的框架,胡哥给我们讲了UML作图,这个的话,如老师所说,框架很详细的话,只往框架中填代码就好了,“初中生培训三个月就可以完成”。。。可是我觉得大道至简,其实书设计思路不够熟练于是框架就变成了这样
服务器框架
客户端框架
服务器客户端界面通信
大概讲,服务器要有3个方法
ServerSocket so=new ServerSocket(port);
//读取数据 private String readString(InputStream ins) throws IOException{ //创建一个字符串缓冲区 StringBuffer stb=new StringBuffer(); char c=0; while(c!=35){ //遇到#号算一个字符串 int i=ins.read();//读取客户机发过来的一个字节 c=(char)i;//将输入的字节转换为一个char stb.append(c); } //将读到的字节转化为字符串,并调用trim去掉尾部的空格 String inputS=stb.toString().trim(); return inputS; } 区分客户端发给服务器,还是群发,加了个标志值(c) int c=ins.read(); if(c==1){//只发给服务器 String inputS=readString(ins); while(!inputS.equals("bye")){ System.out.println("客户机说"+inputS); //加到空白区域 are.setText(are.getText()+"\r\n"+inputS); inputS=readString(ins);//读取客户机下一次输入 } }else if(c==2){//发送给系统 String inputS=readString(ins); while(!inputS.equals("bye")){ System.out.println("客户机说"+inputS); //加到空白区域 are.setText(are.getText()+"\r\n"+inputS); //群发给其他客户端 StartListener.sendMsg(inputS); inputS=readString(ins);//读取客户机下一次输入 } }
//发送消息给连接我的客户端 public void sendMsg2Me(String msg){ try{ msg+="#"; out.write(msg.getBytes()); }catch(Exception ef){ ef.printStackTrace(); } }
这3个方法可能分布在不同的类中,类与类之间也因此相互连接
客户端与服务器端相对应,方法也相对应
Socket client=new Socket("IP",port);
public void Connect(){ try{ ous=so.getOutputStream();//获取输出流 System.out.println("服务器连接成功!"); while(true){ //读取服务器发来的消息 ins=so.getInputStream();//获取输入流 String message=""; int b = ins.read(); while(b!=35){ //#号结尾 message +=(char)b; b = ins.read(); } //显示在客户端的area上 are.setText(are.getText()+"\r\n"+message); System.out.println("服务器传过来的是="+message); } }catch(Exception e){ e.printStackTrace(); } }
//发送消息 public void sendMsg(String s){ byte [] by=s.getBytes(); try { ous.write(by); } catch (Exception e) { e.printStackTrace(); } }
最后效果
1.客户端发给服务器
2.客户端群发
3.一行代码解决汉字乱码
//读取汉字
message=new String(message.getBytes("ISO-8859-1"),"GB2312");
其实关于服务器与客户端的读写,在做同步画板时更能深刻体现,一边写进去什么dou.writeInt(),另一边就即读取什么dis.readInt();
x2=e.getX(); y2=e.getY(); if(s.equals("画线")){ g.drawLine(x1, y1, x2, y2); try{ dos.writeInt(1); dos.writeInt(x1); dos.writeInt(y1); dos.writeInt(x2); dos.writeInt(y2); System.out.println("x1="+x1+" y1="+y1+" x2="+x2+" y2="+y2); }catch(Exception e1){ e1.printStackTrace(); } }else if(s.equals("画圆")){ int r1=Math.abs(x2-x1); int r2=Math.abs(y2-y1); g.drawOval(x1, y1, r1, r2); try{ dos.writeInt(2); dos.writeInt(x1); dos.writeInt(y1); dos.writeInt(r1); dos.writeInt(r2); System.out.println("x1="+x1+" y1="+y1+" x2="+x2+" y2="+y2); }catch(Exception e1){ e1.printStackTrace(); } }else if(s.equals("画图")){ ImageIcon con=new ImageIcon("images/psb.jpg"); //缓冲图纸,实为窗体性质 image01=new BufferedImage(con.getIconWidth(),con.getIconHeight(),BufferedImage.TYPE_INT_RGB); Graphics g2=image01.getGraphics(); g2.drawImage(con.getImage(), 0, 0, null); //白纸划到窗体上 g.drawImage(image01, 0, 0, null); try{ int width=image01.getWidth(); int height=image01.getHeight(); dos.writeInt(3); dos.writeInt(width); dos.writeInt(height); //把每个像素点的颜色写进去 for(int i=0;i<width;i++){ for(int j=0;j<height;j++){ dos.writeInt(image01.getRGB(i, j)); } } }catch(Exception e1){ e1.printStackTrace(); } }
//读出数据的方法 public void readMsg(InputStream ins){ try { while(true){ DataInputStream dis=new DataInputStream(ins); int type=dis.readInt(); if(1==type){ //读取客户端画线的数据 int x1=dis.readInt(); int y1=dis.readInt(); int x2=dis.readInt(); int y2=dis.readInt(); System.out.println("x1="+x1+" y1="+y1+" x2="+x2+" y2="+y2); g.drawLine(x1,y1,x2,y2); }else if(2==type){ //读取客户端画圆的数据 int x1=dis.readInt(); int y1=dis.readInt(); int r1=dis.readInt(); int r2=dis.readInt(); g.drawOval(x1, y1, r1, r2); }else if(3==type){ //读取客户端画图的数据 int width=dis.readInt(); int height=dis.readInt(); for(int i=0;i<width;i++){ for(int j=0;j<height;j++){ g.setColor(new Color(dis.readInt())); g.drawLine(i, j, i, j); } } } } } catch (Exception e) { e.printStackTrace(); }
最终同步画板的效果
在写客户端时用了鼠标监听器和事件监听器都为内部类,内部类最大的好处是基本上省略了传参过程,节省很多代码,最大的缺点是不清晰,在编程语言类往往讲究清晰第一,在写服务器端是鼠标监听器和时间监听器均是外部类,尝试了一下,过程中出现过空指针,但都不是什么大问题,容易解决。在写的过程中还犯了个错误,用了static的一个getter方法,导致同步时服务器只能同步客户端的一条线,原因是第二个调用getter方法覆盖了前一次的数据,static用时需谨慎和思路很清晰啊。。。。。