0

    手机发送post请求工具 Socket实例

    2023.04.12 | admin | 265次围观

    Java编程Socket实现多个客户端连接同一个服务端代码

    Java socket(套接字)通常也称作"套接字",用于描述ip地址和端口,是一个通信链的句柄。应用程序通常通过"套接字"向网络发出请求或者应答网络请求。

    使用socket实现多个客户端和同一客户端通讯;首先客户端连接服务端发送一条消息,服务端接收到消息后进行处理,完成后再回复客户端一条消息。

    服务端代码

    public class server {
      
        public static void main(string[] args) throws ioexception {
            // 为了简单起见,所有的异常信息都往外抛
            int port = 8899;
            // 定义一个servicesocket监听在端口8899上
            serversocket server = new serversocket(port);
            system.out.println("等待与客户端建立连接...");
            while (true) {
                // server尝试接收其他socket的连接请求,server的accept方法是阻塞式的
                socket socket = server.accept();
                /**
           * 我们的服务端处理客户端的连接请求是同步进行的, 每次接收到来自客户端的连接请求后,
           * 都要先跟当前的客户端通信完之后才能再处理下一个连接请求。 这在并发比较多的情况下会严重影响程序的性能,
           * 为此,我们可以把它改为如下这种异步处理与客户端通信的方式
           */
                // 每接收到一个socket就建立一个新的线程来处理它
                new thread(new task(socket)).start();
            }
            // server.close();
        }
        /**
       * 处理socket请求的线程类
       */
        static class task implements runnable {
            private socket socket;
            /**
         * 构造函数
         */
            public task(socket socket) {
                this.socket = socket;
            }
            @override
                public void run() {
                try {
                    handlersocket();
                }
                catch (exception e) {
                    e.printstacktrace();
                }
            }
            /**
         * 跟客户端socket进行通信
         *
         * @throws ioexception
         */
            private void handlersocket() throws exception {
                // 跟客户端建立好连接之后,我们就可以获取socket的inputstream,并从中读取客户端发过来的信息了
                /**
           * 在从socket的inputstream中接收数据时,像上面那样一点点的读就太复杂了,
           * 有时候我们就会换成使用bufferedreader来一次读一行
           *
           * bufferedreader的readline方法是一次读一行的,这个方法是阻塞的,直到它读到了一行数据为止程序才会继续往下执行,
           * 那么readline什么时候才会读到一行呢?直到程序遇到了换行符或者是对应流的结束符readline方法才会认为读到了一行,
           * 才会结束其阻塞,让程序继续往下执行。
           * 所以我们在使用bufferedreader的readline读取数据的时候一定要记得在对应的输出流里面一定要写入换行符(
           * 流结束之后会自动标记为结束,readline可以识别),写入换行符之后一定记得如果输出流不是马上关闭的情况下记得flush一下,
           * 这样数据才会真正的从缓冲区里面写入。
           */
                bufferedreader br = new bufferedreader(
                          new inputstreamreader(socket.getinputstream(), "utf-8"));
                stringbuilder sb = new stringbuilder();
                string temp;
                int index;
                while ((temp = br.readline()) != null) {
                    if ((index = temp.indexof("eof")) != -1) {
                        // 遇到eof时就结束接收
                        sb.append(temp.substring(0, index));
                        break;
                    }
                    sb.append(temp);
                }
                system.out.println("form cliect[port:" + socket.getport()
                          + "] 消息内容:" + sb.tostring());
                // 回应一下客户端
                writer writer = new outputstreamwriter(socket.getoutputstream(),
                          "utf-8");
                writer.write(string.format("hi,%d.天朗气清,惠风和畅!", socket.getport()));
                writer.flush();
                writer.close();
                system.out.println(
                          "to cliect[port:" + socket.getport() + "] 回复客户端的消息发送成功");
                br.close();
                socket.close();
            }
        }
    }
    

    客户端代码

    import java.io.inputstreamreader;
    import java.io.outputstreamwriter;
    import java.io.reader;
    import java.io.writer;
    import java.net.socket;
    /** * socket客户端
    * 功能说明: * * @author 大智若愚的小懂 * @date 2016年8月30日 * @version 1.0 */
    public class client {
        /** * 入口 * @param args */
        public static void main(string[] args) {
            // 开启三个客户端,一个线程代表一个客户端
            for (int i = 0; i < 3; i++) {
                new thread(new runnable() {
                    @override public void run() {
                        try {
                            testclient client = testclientfactory.createclient();
                            client.send(string.format("hello,server!i'm %d.这周末天气如何。", client.client.getlocalport()));
                            client.receive();
                        }
                        catch (exception e) {
                            e.printstacktrace();
                        }
                    }
                }
                ).start();
            }
        }
        /** * 生产测试客户端的工厂 */
        static class testclientfactory {
            public static testclient createclient() throws exception {
                return new testclient("127.0.0.1", 8899);
            }
        }
        /** * 测试客户端 */
        static class testclient {
            /** * 构造函数 * @param host 要连接的服务端ip地址 * @param port 要连接的服务端对应的监听端口 * @throws exception */
            public testclient(string host, int port) throws exception {
                // 与服务端建立连接
                this.client = new socket(host, port);
                system.out.println("cliect[port:" + client.getlocalport() + "] 与服务端建立连接...");
            }
            private socket client;
            private writer writer;
            /** * 发送消息 * @param msg * @throws exception */
            public void send(string msg) throws exception {
                // 建立连接后就可以往服务端写数据了
                if(writer == null) {
                    writer = new outputstreamwriter(client.getoutputstream(), "utf-8");
                }
                writer.write(msg);
                writer.write("eof\n");
                writer.flush();
                // 写完后要记得flush
                system.out.println("cliect[port:" + client.getlocalport() + "] 消息发送成功");
            }
            /** * 接收消息 * @throws exception */
            public void receive() throws exception {
                // 写完以后进行读操作
                reader reader = new inputstreamreader(client.getinputstream(), "utf-8");
                // 设置接收数据超时间为10秒
                client.setsotimeout(10*1000);
                char[] chars = new char[64];
                int len;
                stringbuilder sb = new stringbuilder();
                while ((len = reader.read(chars)) != -1) {
                    sb.append(new string(chars, 0, len));
                }
                system.out.println("cliect[port:" + client.getlocalport() + "] 消息收到了,内容:" + sb.tostring());
                reader.close();
                // 关闭连接
                writer.close();
                client.close();
            }
        }
    }
    

    接下来模拟一下:

    1.首先运行服务端

    2.接着运行客户端(开三个客户端请求)

    为了演示有所区分,服务端我使用的是eclipse工具,客户端使用的是intellij idea工具。这时可以看到客户端在控制台打印出来的消息

    一个port端口号代表一个客户端,回过来看下服务端在控制台打印出来的消息

    TCP网络编程 ServerSocket和Socket实现多客户端聊天 目标

    使用SeverSocket 和 Socket 实现双人、多人聊天

    过程分析 思路

    大致说一下,整体思路

    手机发送post请求工具 Socket实例

    1.实现一个群发效果,且需要执行判断,当前的客户端发送的信息,并不需要通过Server端发送给自己。

    2.实现循环侦听手机发送post请求工具,因为有多个套接字请求,所以需要将它们“存起来”。

    3.根据循环侦听结果创建多个线程,每条线程都是平级关系,每条线程需要实现发送功能,读取功能(将读取到的信息发送出去)

    详细分析

    1.每条线程需要单独运行一遍,才能实现群发效果

    2.每条线程内的:发送方法、run方法、读取方法 都是不同的socket

    3.将每一条线程存到集合,使用当前的线程调取群发方法时,遍历判断,然后切换到其他线程进行发送操作。

    4.大概就是:循环(侦听、创建新线程)——存储线程——当前线程接收信息——发送信息——调取群发——切换线程完成发送

    5.服务器只有及时转发,和循环监听

    代码示例 Server端

    首先要侦听所有的套接字,侦听到一个,就启动一个线程,并将其存至集合

    子线程写了发送方法,run方法手机发送post请求工具,接收方法

    还有一个单例,需要保证该群发类只有一个实例对象

    import java.io.BufferedReader;
    import java.io.IOException;
    import java.io.InputStreamReader;
    import java.net.ServerSocket;
    import java.net.Socket;
    import java.util.ArrayList;
    import java.util.Hashtable;
    import java.util.Map.Entry;
    import java.util.Vector;
    /*服务器端:一个线程专门发送和接收消息  一个线程监听所有Socket。
     客户端:一个线程专门发送消息,一个线程专门接收消息。*/
    public class QunLiaoServer {
        public static void main(String[] args) throws IOException {
            ServerAccept sa = new ServerAccept();
            sa.start();
        }
    }
    //创建侦听类
    class ServerAccept extends Thread{
        @Override
        public void run() {
            try {
                System.out.println("等待客户端连接");
                ServerSocket ss = new ServerSocket(35555);
        //由于可能当有多个客户端连接时,accept方法就会产生多个Socket对象,需加一个while循环监听来自客户端的连接            
                while(true){
                    //侦听套接字的连接,accept是一个阻塞的方法,会阻塞当前的线程
                    Socket s = ss.accept();
                    //建立连接,表示serverSocket在监听,如果监听到有客户端连接则会调用accept方法,然后返回一个Socket,最后建立连接
                    System.out.println("连接成功"+s.getPort());
                    //传递当前连接的客户端套接字
                    SocketShouFa rs = new SocketShouFa(s);//会产生多条线程
                    rs.start();
                    //传递该线程
                    QunFa.getQunFa().add(rs,s);//只有一个实例
                    //当第一个socket被侦听到,且被传递下去,会死循环侦听下一个socket,并传递下去
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    //创建接收和发送类线程 (客户端信息接收,发送方法)
     class SocketShouFa extends Thread{
        Socket socket;
        public SocketShouFa (Socket s){
            this.socket=s;
        }
        public void firstSend(String str,Socket nowsocket){
            try {
                socket.getOutputStream().write((Thread.currentThread().getName()+"端口:"+nowsocket.getPort()+"-->发送:"+str+"\r\n").getBytes());
            } catch (IOException e) {
                 System.out.println("断开了客户端,没有客户端接收信息");//当前接收的客户端断开,无法写给客户端信息会报错
                e.printStackTrace();
                //需要写一个清除当前该客户端的方法
                QunFa.getQunFa().remove(this,socket);
            }    
        }
        //重写run方法
        //线程执行至此,需要接收客户端的信息
        @Override
        public void run() {
            firstSend("您已经连接到服务器",socket);
            try {
                BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
                String line=null;
                //接收客户端的数据
                while ((line=br.readLine())!=null) {
                //然后服务器再将所有的信息转发给每一个客户端,调用qunFa方法 
                  QunFa.getQunFa().qunFa(this, line);
       }
            } catch (IOException e) {
                 System.out.println(Thread.currentThread().getName()+"客户端已断开");
                e.printStackTrace();
            }
        }
    }
    //创建群发类(创建单例模型;集合存储 SocketShouFa线程 和套接字 ;实现群发,调取发送方法)
    class QunFa{
        //创建单例模型
        private QunFa(){
        }
        private static final QunFa si= new QunFa();
        public static QunFa getQunFa(){
            return si;
        }
        //创建一个存储 线程 和端口  的Hashtable集合
        Hashtable<SocketShouFa,Socket> ht = new Hashtable<SocketShouFa,Socket>();
         public void add(SocketShouFa ri,Socket nowsocket){
             //将每一个线程 和 对应的socket 加入集合中
             ht.put(ri, nowsocket);
         }
         //线程清除
         public void remove(SocketShouFa ri,Socket nowsocket) {
              ht.remove(ri, nowsocket);
         }
        //表示当前的线程给集合中的 非自己 的线程发送的信息
         public void qunFa(SocketShouFa ri,String img){
             //遍历集合
             for (Entry<SocketShouFa, Socket> entry : ht.entrySet()) {
                 SocketShouFa nowRi = entry.getKey();
                     //如果遍历线程 和 当前线程不一致,就进入
                    if(!ri.equals(nowRi)){
                        //传递 不一致客户端线程,并且发送当前 线程的socket信息
                        nowRi.firstSend(img, ht.get(ri));
                    }
                }
         }
    }
    

    Client端

    这里有两个线程被启用了,一个是主线程去读取信息,一个子线程发送信息

    import java.io.BufferedReader;
    import java.io.IOException;
    import java.io.InputStreamReader;
    import java.io.PrintWriter;
    import java.net.Socket;
    import java.net.UnknownHostException;
    import java.util.Scanner;
    public class ClientB {
        public static void main(String[] args) {
            // 创建客户端对象
            try {
                Socket s = new Socket("192.168.0.243", 35555);
                SendImgClientB sendclient = new SendImgClientB(s);
                sendclient.start();
                // 创建读取字符流
                BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream()));
                while(true){
                    //读取服务端发过来的信息
                    String serverimg = br.readLine();
                    System.out.println(serverimg);
                }
            } catch (IOException e) {
                System.out.println("服务器下线了");
                e.printStackTrace();
            }
        }
    }
    class SendImgClientB extends Thread {
        Socket ss;
        PrintWriter pw;
        Scanner sc =new Scanner(System.in);
        public SendImgClientB(Socket s) {
            this.ss = s;
            try {
                this.pw = new PrintWriter(s.getOutputStream());
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        @Override
        public void run() {
            try {
                while (true) {
                    // 客户端发送信息
                    String in = sc.next();
                    pw.println(in);
                    pw.flush();// 输出流刷新
                    if (in.equals("end")) {
                        break;
                    }
                }
            } catch (Exception e) {
                System.out.println("服务器下线了");
                e.printStackTrace();
            } finally {
                if (pw != null) {
                    pw.close();
                }
                if (ss != null) {
                    try {
                        ss.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
    

    结果

    版权声明

    本文仅代表作者观点。
    本文系作者授权发表,未经许可,不得转载。

    发表评论