进入了通信阶段,感觉和之前学的内容差异很大,对这方面也没有之前那么感兴趣……有点缺乏热情进
度上不来……同时还在改进之前的线程游戏,很久没更新了。做了可以连接多个客户端,群发消息的服务
器,和可以同步画图的画板,一起总结一下。
1.思路:
需要实现的功能:
服务器:
1.创建一个连接
2.被客户端连接,将线程加入一个队列中。
3.打开客户端的输入输出流
4.将消息写入客户端的输出流
5.群发,遍历整个队列,将数据写入每个客户端的输出流。
6.读入客户端写入自身输出流的内容,再按照协议表达。
客户端:
1.连接服务器
2.打开服务器的输入输出流
3.读取服务器写入自身输入流的数据,按照协议表达。
4.把需要传递的内容写入服务器的输入流中,再由服务器调用群发方法,实现客户端与客户端之间的交流
。
感觉通信和之前学习的内容有个很大的区别,就是程序的每个部分都是紧密关联的,环环相扣,哪一个部
分出了问题都出不了效果。需要在思绪清晰的时候写。之前几天原理不太明确走了很多弯路。
服务器和客户端的写法都可以简化为几步:
服务器:
1.创建服务器
2.进入等待状态 accept
3.获取输入输出流
客户端:
1.new socket
2.获取输入输出流
其他功能的实现,只需定义不同的协议,就能实现。
2.服务器和客户端一起所需要的类比较多,一开始写因为类名规定不合理给自己添加了许多麻烦。这里稍
微提一下:
服务器:
服务器界面类:ServerUI;
服务器创建线程:CreatServer
服务器读写线程:ServerThread
服务器群发类:ChatConnect
客户端:
客户端界面:ClientUI
客户端读写线程:ClientThread
还有各种监听器,和辅助类,那就根据需求来。
姑且服务器相关的都以Server打头,客户端的都以Client打头好了。
服务器界面1:
客户端界面1:
3.新涉及的类
1.类 Socket
public class Socketextends Object此类实现客户端套接字(也可以就叫“套接字”)。套接字是两
台机器间通信的端点。
套接字的实际工作由 SocketImpl 类的实例执行。应用程序通过更改创建套接字实现的套接字工厂可以
配置它自身,以创建适合本地防火墙的套接字。
涉及的用法:
while(true){
//阻塞
Socket client=s.accept();
System.out.println("进入连接:"+client.getRemoteSocketAddress());
方法:
getRemoteSocketAddress()
返回此套接字连接的端点的地址,如果未连接则返回 null。
2.类 ServerSocket
此类实现服务器套接字。服务器套接字等待请求通过网络传入。它基于该请求执行某些操作,然后可能向
请求者返回结果。
服务器套接字的实际工作由 SocketImpl 类的实例执行。应用程序可以更改创建套接字实现的套接字工
厂来配置它自身,从而创建适合本地防火墙的套接字。
涉及的用法:
java.net.ServerSocket s=new java.net.ServerSocket(this.port);
System.out.println("服务器创建成功!"+port);
方法:
accept()
侦听并接受到此套接字的连接。
3.telnet
在最开始调试服务器时,使用了一个WINDOWS自带的简易客户端,telnet。
连接服务器的格式:
telnet IP 端口号。
另外在CMD中使用指令:ipconfig 可以获取主机的IP地址
3.在网络画板部分实现的功能:
1.切换画笔类型(划线画圆画方框)
2.多客户端连接服务器
3.群画群聊
服务器界面2:
客户端界面2:
源代码:
创建线程:
import java.awt.Graphics;
import java.net.Socket;
import javax.swing.JTextArea;
public class creatThread extends Thread{
private int port;
private int count;
private Graphics g;
private JTextArea JA;
public chatThread ct;
public creatThread(int port,JTextArea JA,int count,Graphics g){
this.port=port;
this.JA=JA;
this.count=count;
this.count++;
this.g=g;
}
public void run(){
creatchat();
}
private void creatchat() {
try{
java.net.ServerSocket s=new java.net.ServerSocket(this.port);
System.out.println("服务器创建成功!"+port);
//让服务器进入循环等待状态
while(true){
//阻塞
Socket client=s.accept();
System.out.println("进入连接:"+client.getRemoteSocketAddress());
//创建线程
ct=new chatThread(client,JA,g);
this.setchatThread(ct);
//保存至队列
Chatconnect.add(ct);
ct.start();
//
}
}catch(Exception ef){
ef.printStackTrace();
}
}
public void setchatThread(chatThread ct){
this.ct = ct;
}
public chatThread getchatThread(){
return this.ct;
}
}
服务器线程:
public class chatThread extends Thread{
private Socket client;
private OutputStream ous;
private InputStream ins;
private JTextArea JA;
int count;
static Graphics g;
DataInputStream dins;
DataOutputStream dous;
ServerMouse sm;
public chatThread(Socket client,JTextArea JA,Graphics g){
this.client=client;
this.JA=JA;
this.g=g;
}
public void run(){
processChat();
}
public void processChat() {
try{
//从通话对象上获取输入输出流
ins=this.client.getInputStream();
ous=this.client.getOutputStream();
dins=new DataInputStream(ins);
dous=new DataOutputStream(ous);
} catch(Exception ef){
ef.printStackTrace();
}
//服务器读入客户端发来的数据
while(true){
try {
// String msg=("welcom!\r\n");
// byte[] bb=msg.getBytes();
// ous.write(bb);
byte b = dins.readByte();
System.out.println("服务器读入,type="+b);
if(b==1){
int x1 = dins.readInt();
int y1 = dins.readInt();
int x2 = dins.readInt();
int y2 = dins.readInt();
System.out.println("画线 "+"x1="+x1+",y1="+y1+",x2="+x2+",y2="+y2);
g.drawLine(x1, y1, x2, y2);
Chatconnect.sendmsg(1,x1,y1,x2,y2);
}
if(b==2){
int x1 = dins.readInt();
int y1 = dins.readInt();
int x2 = dins.readInt();
int y2 = dins.readInt();
System.out.println("画圆 "+"x1="+x1+",y1="+y1+",x2="+x2+",y2="+y2);
g.drawOval(x1, y1,Math.abs(x1-x2), Math.abs(y1-y2));
Chatconnect.sendmsg(2,x1,y1,x2,y2);
}
if(b==3){
int x1 = dins.readInt();
int y1 = dins.readInt();
int x2 = dins.readInt();
int y2 = dins.readInt();
System.out.println("画方框 "+"x1="+x1+",y1="+y1+",x2="+x2+",y2="+y2);
g.drawRect(x1, y1,Math.abs(x1-x2), Math.abs(y1-y2));
Chatconnect.sendmsg(3,x1,y1,x2,y2);
}
// //发送消息
// if(b==4){
// System.out.println("服务器发送消息");
// String s=readString(ins);
// this.JA.append("客户端"+count+":"+s+"\r\n");
// System.out.println("服务器写入运行!");
// System.out.println("user: "+s);
// Chatconnect.sendmsg(s);
// msg="host:"+s+"\r\n";
// bb=msg.getBytes();
ous.write(b);
// s=readString(ins);
// }
} catch (Exception e) {
e.printStackTrace();
}
}
}
// public static void writeLine(int x1,int y1,int x2,int y2){
// try{
// dous.writeInt(1);
// dous.writeInt(x1);
// dous.writeInt(y1);
// dous.writeInt(x2);
// dous.writeInt(y2);
// }catch(Exception ef){
// ef.printStackTrace();
// }
// }
//
// //字符读入
// private String readString(InputStream ins) {
// try{
// StringBuffer sb=new StringBuffer();
// int t=ins.read();
// while(t!='#'){
// sb.append((char)t);
// t=ins.read();
// }
// String s=sb.toString();
// return s;
// }catch(Exception ef){
// ef.printStackTrace();
// }
// return "error";
// }
//
// public void sendmsg(String msg) {
// try{
// ous.write(4);
// msg+="#";
// ous.write(msg.getBytes());
// }catch(Exception ef){
// ef.printStackTrace();
// }
//
// }
public void sendmsg(int type,int x1,int y1,int x2,int y2) {
try {
System.out.println("服务器输出,TYPE="+type);
if(type==1){
//画线
dous.writeByte(1);
dous.writeInt(x1);
dous.writeInt(y1);
dous.writeInt(x2);
dous.writeInt(y2);
}
if(type==2){
//画圆
dous.writeByte(2);
dous.writeInt(x1);
dous.writeInt(y1);
dous.writeInt(x2);
dous.writeInt(y2);
}
if(type==3){
//画方框
dous.writeByte(3);
dous.writeInt(x1);
dous.writeInt(y1);
dous.writeInt(x2);
dous.writeInt(y2);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
4.调试心得
关于参数的传输:这里加入了鼠标监听器,当初在传送数据的时候纠结了一下。最后使用的方法是在监听
器里画好图后,然
后再调用线程中的方法,传入线程中,写入输出流中。
关于群发的实现思路:在实现客户端之间的交流时,思路上也走了弯路。虽然想法比较奇特,但是应该也
能实现,不知道为什么出了错误。最后换成了更加正常的思路:在监听器里画完图后,调用线程中的方法
写入服务器的输出流中。服务器的输入流获取信息之后,立即调用群发方法。
关于不同类型数据的传输:这里就关于协议了。一般的方式是定一个方式,先输出一个类型码,代表数据
类型,读取时再通过数据类型辨别,分别处理。