java socket 编程实现网络交谈和同步操作

java socket 编程实现网络交谈和同步操作


目标

    用java socket编程和多线程机制实现网络交谈和同步操作。

1、程序网络结构图

这里写图片描述

2、流程图

这里写图片描述

3、原理

①Java Swing编程

    该网络交谈小程序使用了Java Swing编程技术,实现画图、编辑消息等功能;

②多线程

    该程序使用了多线程技术,在程序运行过程中产生了多个线程。在创建客户端时,为每个客户端开启一个线程,这样客户端之间可以看成是相互独立的。

③Socket编程

    该程序使用了socket编程技术,主进程(即服务器)创建一个服务器端的socket,指定绑定的端口,并监听该端口。当有客户端socket向该端口发送消息时,服务器端接收消息并将该客户端放入一个队列中,该队列中存放当前访问该端口的所有客户端。当服务器向客户端发送消息时,遍历该队列中的所有客户端,同时向所有客户端发送消息。当客户端有消息发送到服务器时,服务器接收该消息,并将该消息转发给客户端队列中的所有客户端,实现消息共享。
    注意:
    ConnectionException : 对于客户进程, 如果它发出的连接请求被加入到服务器的请求连接队列中, 就意味着客户与服务器的连接建立成功, 客户进程从 Socket 构造方法中正常返回. 如果客户进程发出的连接请求被服务器拒绝, Socket 构造方法就会抛出 ConnectionException.
     必须在服务器进程通过 ServerSocket 的 accept() 方法从请求连接队列中取出连接请求, 并返回一个Socket 对象后, 服务器进程这个Socket 对象才与客户端的 Socket 对象形成一条通信线路.
相关博客:http://blog.csdn.net/qq_23473123/article/details/51461894

4、程序的输入与输出

    输入:在“画图”窗口中绘图,在“聊天”窗口中发送消息。
    输出:服务器和各客户端同步共享窗口内容。

5、程序运行截图

    a. 如下图所示,运行程序,可看到名为“ycc的通信小程序”的窗口,该窗口包含一个下拉框,一个画图面板和一个聊天面板;
这里写图片描述

    b. 如下图所示,点击下拉框上的“文件”按钮,弹出“添加客户端”按钮,单击该按钮可添加客户端;
这里写图片描述

    c. 如下图所示,这里添加了三个客户端,在服务器或任意客户端上画图,其它窗口可同步显示该图片;
这里写图片描述

    d. 如下图所示,点击“聊天”面板,在服务器或任意客户端窗口的编辑框中编辑消息并点击发送按钮,其它窗口均可显示该消息;
这里写图片描述

6、代码实现

①程序结构

这里写图片描述

②客户端Client.java

package networkTalk;

import java.awt.BorderLayout;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionListener;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.Socket;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTabbedPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;

import networkTalk.Client;

public class Client extends JFrame{
        public JTextArea area; //聊天窗口的输入框
        JTextField field; //聊天窗口的显示框
        JButton button;
        //这里不能用static,每个客户端必须是独立的
        public PrintWriter writer;
        public Graphics g;  //用来画图的面板
        public JPanel contentPanel1;
        public Client() {
            //使用java swing的一些控件
            this.setTitle("客户端");
            this.setSize(500, 500);

            JMenu jMenu = new JMenu("文件"); //菜单对象
            JMenuItem t = new JMenuItem("添加客户端");//菜单项
            jMenu.add(t);
            JMenuBar jMenuBar = new JMenuBar();  //创建菜单工具栏
            jMenuBar.add(jMenu);
            this.setJMenuBar(jMenuBar);  

            JTabbedPane jTabbedPane = new JTabbedPane(JTabbedPane.LEFT);//设置选项卡坐标
            contentPanel1 = new JPanel();//创建多个容器
            JPanel contentPanel2 = new JPanel();
            jTabbedPane.add("画图",contentPanel1);
            jTabbedPane.add("聊天",contentPanel2);
            this.add(jTabbedPane,BorderLayout.CENTER);

            area= new JTextArea(20,30);
            field = new JTextField(20);
            button=new JButton("提交");
            JScrollPane sp =new JScrollPane(area);
            contentPanel2.add(sp,BorderLayout.CENTER);
            contentPanel2.add(field);
            contentPanel2.add(button);
            this.setVisible(true);
            this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            //聊天界面中消息发送按钮的监听事件
            button.addActionListener(new ActionListener() {

                @Override
                public void actionPerformed(ActionEvent arg0) {
                    //获取编辑框中的内容
                    String text = field.getText();
                    //输出流,传递给服务器
                    writer.println(text);
                    //显示框中显示内容
                    area.append("我:"+text+"\n");
                    //编辑设为空
                    field.setText("");
                }
            });
            //“添加客户端”按钮的监听事件,点击该按钮添加一个客户端
            t.addActionListener(new ActionListener() {

                @Override
                public void actionPerformed(ActionEvent arg0) {
                    // TODO Auto-generated method stub
                    Runnable myRunnable = new Runnable(){
                        @Override
                        public void run() {
                            // TODO Auto-generated method stub
                            try {
                                Client.main();
                            } catch (Exception e) {
                                // TODO Auto-generated catch block
                                e.printStackTrace();
                            }
                        }
                    };
                    Thread thread = new Thread(myRunnable);
                    thread.start();
                }
            });

            //在画图面板中监听鼠标消息      
            contentPanel1.addMouseMotionListener(new MouseMotionListener() {
            int x=0;
            int y=0;
            @Override
            public void mouseMoved(MouseEvent e) {
                // TODO Auto-generated method stub
            }
            @Override
            public void mouseDragged(MouseEvent e) {
                // TODO Auto-generated method stub
                // 获取鼠标坐标
                x = e.getX();
                y = e.getY();
                //将消息传给服务器
                writer.println(x + "*" + y);

                g = contentPanel1.getGraphics(); //这里也用到了
                g.fillOval(x, y, 1, 1);
            }
            });

        }

        public static void main() throws Exception{
            Client c =new Client();
            //创建客户端socket,指定服务器地址和端口
            Socket socket =new Socket("127.0.0.1",9995);
            //获取输出流,向服务器发送消息
            OutputStream out = socket.getOutputStream();  //输出流
            c.writer=new PrintWriter(out,true); //将输出流包装成打印流

            System.out.println("成功连接服务器!");
            //获取输入流,并读取服务器端的响应信息
            BufferedReader reader=new BufferedReader(new InputStreamReader(socket.getInputStream()));
            while(true){
                //获取客户端消息
                String line = reader.readLine();
                String a[] = new String[2];
                //画图时传回来的值是x*y,调用split方法后a的长度为2,聊天时没有*符号,a的长度为1
                a = line.split("\\*"); 
                if(a.length == 1){
                    //聊天
                    c.area.append("客户端:"+a[0]+"\n");
                }else {
                    //画图
                    int x = Integer.parseInt(a[0]);
                    int y = Integer.parseInt(a[1]);
                    c.g = c.contentPanel1.getGraphics(); 
                    c.g.fillOval(x, y, 1, 1);
                }
            }
        }
}

①服务器端Server.java

package networkTalk;

import java.awt.BorderLayout;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionListener;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTabbedPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;

import networkTalk.Server;

public class Server extends JFrame{
    static JTextArea area;  //聊天窗口的输入框
    JTextField field;  //聊天窗口的显示框
    JButton button; 
    static PrintStream writer;  //打印流,设置为全局变量
    static Graphics g; //画图
    static JPanel contentPanel1; //用来画图的面板
    static ArrayList<Socket> sList; //socket请求队列

    public Server(){

        //使用java swing的一些控件
        this.setTitle("ycc的通信小程序");  
        this.setSize(500,500);

        JMenu jMenu = new JMenu("文件"); //菜单对象
        JMenuItem t = new JMenuItem("添加客户端");
        jMenu.add(t);
        JMenuBar jMenuBar = new JMenuBar();  //创建菜单工具栏
        jMenuBar.add(jMenu);
        this.setJMenuBar(jMenuBar);  

        JTabbedPane jTabbedPane = new JTabbedPane(JTabbedPane.LEFT);//设置选项卡坐标
        contentPanel1 = new JPanel(); //创建多个容器
        JPanel contentPanel2 = new JPanel();
        jTabbedPane.add("画图",contentPanel1);
        jTabbedPane.add("聊天",contentPanel2);
        this.add(jTabbedPane,BorderLayout.CENTER);

        area = new JTextArea(20,30);  
        area.setEditable(false);  
        field = new JTextField(20);  
        button = new JButton("提交");  
        JScrollPane sp = new JScrollPane(area);  
        contentPanel2.add(sp,BorderLayout.CENTER);  
        contentPanel2.add(field);  
        contentPanel2.add(button);  
        this.setVisible(true);  
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 

        //在画图面板中监听鼠标消息      
        contentPanel1.addMouseMotionListener(new MouseMotionListener() {
        //X,Y表示鼠标当前坐标
        int x=0;
        int y=0;
        @Override
        public void mouseMoved(MouseEvent e) {
            // TODO Auto-generated method stub
        }
        @Override
        public void mouseDragged(MouseEvent e) {
            // 获取鼠标坐标
            x = e.getX();
            y = e.getY();
            //writer.println(x + "*" + y);
            //将消息广播给各个客户端
            for (Socket s : sList) {
                try {
                    //System.out.println(s);
                    PrintStream writer1;
                    //获取输出流
                    writer1 = new PrintStream(s.getOutputStream(),true);
                    //传给客户端
                    writer1.println(x + "*" + y);

                } catch (IOException e1) {
                    // TODO Auto-generated catch block
                    e1.printStackTrace();
                }   
            }
            //调用graphics画图
            g = contentPanel1.getGraphics();
            g.fillOval(x, y, 1, 1);
        }
        });
        //“添加客户端”按钮的监听事件,点击该按钮添加一个客户端
        t.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent arg0) {
                //为客户端开启一个线程
                Runnable myRunnable = new Runnable(){
                    @Override
                    public void run() {
                        // TODO Auto-generated method stub
                        try {
                            Client.main();
                        } catch (Exception e) {
                            // TODO Auto-generated catch block
                            e.printStackTrace();
                        }
                    }
                };
                //启动线程
                Thread thread = new Thread(myRunnable);
                thread.start();
            }
        });
        //聊天界面中消息发送按钮的监听事件
        button.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                String text =field.getText();
               // writer.println(text);
                //广播
                for (Socket s : sList) {
                    try {
                        System.out.println(s);
                        PrintStream writer1;
                        writer1 = new PrintStream(s.getOutputStream(),true);

                        writer1.println(text);

                    } catch (IOException e1) {
                        // TODO Auto-generated catch block
                        e1.printStackTrace();
                    }   
                }
                area.append("我:"+text+"\n");
                field.setText("");
            }
        });

    }
/*=================================================================================*/
    public static void main(String[] args) throws Exception{
        Server s = new Server();
        //创建一个服务器端Socket,即ServerSocket,指定绑定的端口,并监听此端口
        ServerSocket server =new ServerSocket(9995);
        System.out.println("服务端准备完毕!开始监听请求!");

        Server.sList = new ArrayList<Socket>();

        for(;;)
        {
            //调用accept()方法开始监听,等待客户端的连接
            Socket socket = server.accept();
            //这个socket传给下面开启的线程
            //这里接收到的socket要在下面开启的线程中用到
            //添加到socket队列中
             sList.add(socket);
            //开启多线程
            class MyThread  implements  Runnable
            {
                private Socket m_Socket;
                public void setSocket(Socket s)
                {
                    m_Socket = s;
                }
                @Override
                public void run() {
                    // TODO Auto-generated method stub
                    try {
                        //下面的代码放到一个单独的线程中,每次accept到新的socke都开一个线程
                        InetAddress address =m_Socket.getInetAddress();
                        String name = address.getLocalHost().getHostName();
                        System.out.println(name+"已成功连接");
                        //getOutputStream方法使连接的另一端得到输入
                        writer=new PrintStream(m_Socket.getOutputStream(),true);
                        //获取从客户端传回来的值
                        BufferedReader reader = new BufferedReader(new InputStreamReader(m_Socket.getInputStream()));

                        while(true){
                            String line=null;
                            line=reader.readLine();
                            String a[] = new String[2];
                            //画图时传回来的值是x*y,调用split方法后a的长度为2,聊天时没有*符号,a的长度为1
                            a = line.split("\\*"); 
                            //收到的数据发送给slist里面的其它客户端,广播
                            for (Socket s : sList) {
                                System.out.println(s);
                                if (m_Socket != s) {
                                    PrintStream writer1;//新的 writer,发给其他客户端
                                    writer1 = new PrintStream(s.getOutputStream(),true);
                                    writer1.println(line);
                                }
                            }
                            if(a.length == 1){
                                //聊天
                                area.append("客户端:"+a[0]+"\n");

                            }else {
                                //画图
                                int x = Integer.parseInt(a[0]);
                                int y = Integer.parseInt(a[1]);
                                g = contentPanel1.getGraphics();
                                g.fillOval(x, y, 1, 1);
                            }

                        }
                    } catch (Exception e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }

                }
            }

            MyThread myRunnable = new MyThread();
            myRunnable.setSocket(socket);
            Thread thread = new Thread(myRunnable);
            thread.start();

        }

    }
}

猜你喜欢

转载自blog.csdn.net/cherry_yu08/article/details/79257902