网络通信–你画我猜
1. 服务器和客户端的连接
首先在服务器生成一个ServerSocket类型的对象(包括端口编号),等待socket对象与之连接(ServerSocket的accept()方法从连接请求队列中取出一个客户的连接请求,然后创建与客户连接的Socket对象,并将它返回。如果队列中没有连接请求 accept() 方法就会一直等待,直到接收到了连接请求才返回。);在客户端生成一个Socket对象(包括网络IP地址和端口号)。二者连接成功则可进行后续操作。
//服务器类中
private ServerSocket serverSocket;
public Socket connect(int port){
Socket socket = null;
try {
serverSocket = new ServerSocket(port);
//进入阻塞态等待连接
socket = serverSocket.accept();
} catch (IOException e) {
System.out.println("连接异常!!!");
e.printStackTrace();
}
return socket;
}
private Socket socket;
try {
socket = new Socket(ip,port);
} catch (IOException e) {
e.printStackTrace();
}
2. 设置界面及监听器
设置界面
private JFrame initui(Socket socket){
String[] btnstr = {"直线","矩形","圆形"};
//创建窗体对象并配置属性
JFrame jFrame = new JFrame("服务器");
jFrame.setSize(400,400);
jFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jFrame.setLocation(500,100);
jFrame.setLayout(new FlowLayout());
//创建监听器对象
ServerListener serverListener = null;
jFrame.setVisible(true);
try {
serverListener = new ServerListener(jFrame.getGraphics(),socket.getOutputStream());
//给窗体添加鼠标监听器
jFrame.addMouseListener(serverListener);
} catch (IOException e) {
e.printStackTrace();
}
for (int i = 0; i < btnstr.length; i++) {
JButton jButton = new JButton(btnstr[i]);
//给窗体添加按钮
jFrame.add(jButton);
//给按钮添加动作监听器
jButton.addActionListener(serverListener);
}
jFrame.setVisible(true);
//返回窗体对象
return jFrame;
}
设置监听器
public class ServerListener implements MouseListener, ActionListener {//包含鼠标监听器和动作监听器
int x1,x2,y1,y2,x11,x22,y11,y22;
Graphics graphics;
OutputStream outputStream;
String shapetype = "";
ArrayList<Integer> pointlist = new ArrayList();
//构造函数:传入画笔和输出流
public ServerListener(Graphics graphics, OutputStream outputStream){
this.graphics = graphics;
this.outputStream = outputStream;
}
@Override
public void mouseClicked(MouseEvent e) {}
@Override
public void mousePressed(MouseEvent e) {
x1 = e.getX();
y1 = e.getY();
}
@Override
public void mouseReleased(MouseEvent e) {
x2 = e.getX();
y2 = e.getY();
//由于是一个个byte的传输,再次防止信息内容大于一个byte的范围。例如x1=300,则传输时值会变为44(300-256);
//因此需传输x1=x1+x11*256=44+1*256=300
x11=x1/256;
y11=y1/256;
x22=x2/256;
y22=y2/256;
//根据命令绘制图形
if (shapetype.equals("直线")){
graphics.drawLine(x1,y1,x2,y2);
}else if (shapetype.equals("矩形")){
graphics.drawRect(Math.min(x1,x2),Math.min(y1,y2),Math.abs(x2-x1),Math.abs(y2-y1));
}else if (shapetype.equals("圆形")){
graphics.drawOval(Math.min(x1,x2),Math.min(y1,y2),Math.abs(x2-x1),Math.abs(y2-y1));
}
//网络协议:[图形类型长度][图形类型][图形坐标长度][图形坐标]
try {
//传输图形的类型的长度
outputStream.write(shapetype.length());
//传输图形的类型
outputStream.write(shapetype.getBytes(StandardCharsets.UTF_8));
//将图形的坐标信息添加到链表中
pointlist.add(x1);
pointlist.add(y1);
pointlist.add(x2);
pointlist.add(y2);
pointlist.add(x11);
pointlist.add(y11);
pointlist.add(x22);
pointlist.add(y22);
//传输链表的长度
outputStream.write(pointlist.size());
for (int i = 0; i < pointlist.size(); i++) {
//依次传输链表内的数据信息
outputStream.write(pointlist.get(i));
}
//清空链表,供下次使用
pointlist.clear();
} catch (IOException ex) {
ex.printStackTrace();
}
}
@Override
public void mouseEntered(MouseEvent e) {}
@Override
public void mouseExited(MouseEvent e) {}
@Override
public void actionPerformed(ActionEvent e) {
//获取按钮上命令文字
String btnstr = e.getActionCommand();
shapetype = btnstr;
}
}
3. 设置客户端
客户端接收数据
private void startListenMsg(JFrame jFrame, Socket socket) {
InputStream inputStream = null;
Graphics g = jFrame.getGraphics();
try {
inputStream = socket.getInputStream();
while (true){
int x1, y1, x2, y2;
//网络协议:[图形类型长度][图形类型][图形坐标长度][图形坐标]
//读取图形类型的长度
int length = inputStream.read();
//读取图形类型
byte[] shapetypear = new byte[length*3];
inputStream.read(shapetypear);
String typestr = new String(shapetypear);
System.out.println("已接收 "+typestr);
//读取图形坐标信息个数
int datasize = inputStream.read();
System.out.println("坐标个数 "+datasize);
//读取图形坐标
int[] dataarray = new int[datasize];
for (int i = 0; i < dataarray.length; i++) {
dataarray[i] = inputStream.read();
}
//坐标的转化
x1 = dataarray[0]+dataarray[4]*256;
y1 = dataarray[1]+dataarray[5]*256;
x2 = dataarray[2]+dataarray[6]*256;
y2 = dataarray[3]+dataarray[7]*256;
//绘制图形
if (typestr.equals("直线")){
g.drawLine(x1,y1,x2,y2);
}else if (typestr.equals("矩形")){
g.drawRect(Math.min(x1,x2),Math.min(y1,y2),Math.abs(x2-x1),Math.abs(y2-y1));
}else if (typestr.equals("圆形")){
g.drawOval(Math.min(x1,x2),Math.min(y1,y2),Math.abs(x2-x1),Math.abs(y2-y1));
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
综上,服务器按照网络协议要求将信息依次发送至客户端,客户端依次接收信息,从而实现网络通信。
public class Msgserver {
private ServerSocket serverSocket;
//服务器构造函数
public Msgserver(int port){
Socket socket = connect(port);
JFrame drawui = initui(socket);
}
}
public class Client {
private Socket socket;
//客户端构造函数
public Client(String ip,int port) {
try {
socket = new Socket(ip,port);
JFrame drawui = initui(socket);
startListenMsg(drawui,socket);
} catch (IOException e) {
e.printStackTrace();
}
}
}
//先运行服务器main方法,在运行客户端main方法即可
public class ServerStart {
public static void main(String[] args) {
new Msgserver(20000);
}
}
public class ClientStart {
public static void main(String[] args) {
new Client("127.0.0.1",20000);
}
}