• Socket 网络通信


    Socket 网络通信

    1.OSI (Open System Interconnect Reference Model)(开放系统互联参考模型)

    从下低到高 :物理层、数据链路层、网络层、传输层、会话层、表示层和应用层。

    2.TCP/IP

    TCP/IP 是一个协议族,里边包括很多协议,TCP,IP知识两个很重要的协议。

    TCP(Transmission Control Protocol,传输控制协议) 是面向连接的协议,在收发数据时,都需要与对面建立连接,TCP协议能够确保数据在传输过程中不会遗失。它的缺点是过程复杂,消耗的资源更多。

    UDP(User Datagram Protocol)用户数据报协议,相比TCP就是无需建立连接,结构简单,无法保证正确性,容易丢包。

    3.Socket 实现网络通信

    Socket(套接字),用来描述IP地址和端口,是通信链的句柄,应用程序可以通过Socket向网络发送请求或者应答网络请求。Socket是支持TCP/IP协议的网络通信的基本操作单元。

    3.1 实现服务端和客户端连接

    服务端:

        // 1.创建服务端 Socket 并绑定端口
        ServerSocket serverSocket=new ServerSocket(6666);
        String ip=InetAddress.getLocalHost().getHostAddress();
    
        S.println("~ ~ ~ 服务端已开启。 IP:"+ip);
    
        // 2.调用 accept() 方法,等待客户连接
        Socket socket=serverSocket.accept();
    
        // 3.获取输入流
        InputStream inputStream = socket.getInputStream();
        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
    
        //4. 不断读取客户端信息
        String info;
        while ((info=bufferedReader.readLine())!=null){
            S.println(info);
        }
    
        socket.shutdownInput(); //关闭输入流
        socket.close();//关闭连接
    

    客户端:

    private void acceptServer() throws IOException {
    
        // 1.创建客户端Socket,指定服务器地址和端口
        Socket socket=new Socket(IP,PORT);
        // 2.获取输出流
        OutputStream outputStream=socket.getOutputStream();
        String string = "Tiger connect to server.";
        // 3.向输出流中写入数据
        outputStream.write(string.getBytes());
        outputStream.flush(); // *** 强制发送缓冲区数据 ***
    
        socket.shutdownOutput(); //关闭输出流
        socket.close();//关闭连接
    }
    

    3.2 多个客户端之间通信

    实际上是所有客户端都向服务端发送消息,然后服务端再将消息返回给所有客户端。

    服务端:

    public class Server {
    
        private static final int PORT = 6666; //端口
        private List<Socket> list = new ArrayList<>(); //存储所有客户端
        private ServerSocket serverSocket = null;
        private ExecutorService myExecutorService = null; //线程池
    
        public static void main(String[] args) throws IOException {
    
            new Server(); //在构造函数中开启服务端
        }
    
    
        public Server() throws IOException {
    
            // 1.创建Socket 服务并指定端口
            serverSocket = new ServerSocket(PORT);
            // 2.创建线程池
            myExecutorService = Executors.newCachedThreadPool();
            S.println("Server is running...");
            // 3.不断地等待客户端连接,当有客户端连接后,添加到Socket集合,开启新的线程到线程池中
            Socket client = null;
            while (true) {
                client = serverSocket.accept();
                list.add(client);
                myExecutorService.execute(new Service(client));
            }
        }
    
        class Service implements Runnable {
    
            private Socket socket;
            private BufferedReader br;
            private String msg;
    
            public Service(Socket socket) throws IOException {
                this.socket = socket;
    
                br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
                msg = "User:" + socket.getInetAddress().getHostAddress() + " ~ join the chat group. The amount of online:" + list.size();
                this.sendMessage();
            }
    
            @Override
            public void run() {
    
                try {
                    // 不断接受客户端发来的消息,直到客户端退出, “bye”
                    while (true) {
                    if ((msg = br.readLine()) != null) {
                            if (msg.equals("bye")) { //客户端退出
    
                                S.println(" ~~~ ~~~ ~~~ ~~~ ");
                                list.remove(socket);
                                socket.close();
                                msg = "User:" + socket.getInetAddress() + " exit. The amount of online:" + list.size();
                                this.sendMessage();
                                break; // *** 退出循环 ***
                            } else { //客户端发送消息
                                msg = socket.getInetAddress() + " say:" + msg;
                                this.sendMessage();
                            }
                        }
                    }
    
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
    
            //为连接上服务端的每个客户端发送消息
            public void sendMessage() throws IOException {
    
                S.println(msg);
                //向每个客户端发送消息
                for (Socket socketClient : list) {
                    try {
                        PrintWriter pout = new PrintWriter(new BufferedWriter(
                                new OutputStreamWriter(socketClient.getOutputStream(),"UTF-8")),true);
                        pout.println(msg);
                    }catch (IOException e) {e.printStackTrace();}
                }
            }
        }
    }
    

    客户端:

    public class MainActivity extends AppCompatActivity implements Runnable {
    
        private final String IP = "10.0.2.2"; //IP
        private final int PORT = 6666; //端口
        private EditText etMessage;
        private TextView tvMessage;
    
        private Socket socket;
        private BufferedReader br;
        private OutputStream outputStream;
        private String msg;
    
        private PrintWriter pw;
        private boolean bIsClosed = false; //是否退出
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            //Init controls
            etMessage = (EditText) findViewById(R.id.et_Message);
            tvMessage = (TextView) findViewById(R.id.tv_Message);
    
            connectToServer();
            new Thread(MainActivity.this).start();
        }
    
        //连接到服务端
        private void connectToServer() {
    
            new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        // 1.创建客户端Socket,指定服务器地址和端口
                        socket = new Socket(IP, PORT);
                        // 2.获取输入流
                        br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
                        // 3.获取输出流
                        outputStream = socket.getOutputStream();
                        pw = new PrintWriter(new BufferedWriter(new OutputStreamWriter(outputStream)), true);
    
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
    
            }).start();
        }
    
        public void onClick(View view) throws IOException {
    
            //Send message
            msg = etMessage.getText().toString();
            sendMessage();
        }
    
        public void sendMessage(){
    
            //Socket 已经连接,并且输出流没有关闭
            if (socket.isConnected() && !socket.isOutputShutdown()) {
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        if (bIsClosed)
                            pw.println("bye");
                        else
                            pw.println(msg);
                    }
                }).start();
            }
        }
    
        @Override
        public void run() {
    
            try {
                //不断地从输入流中读取数据
                while (true) {
                    //Socket已经连接
                    if (socket != null) {
                        if ((msg = br.readLine()) != null) {
    
                            runOnUiThread(new Runnable() {
                                @Override
                                public void run() {
                                    tvMessage.setText(tvMessage.getText().toString() + msg + "
    ");
                                }
                            });
                        }
                    }
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    
        @Override
        protected void onDestroy() {
    
            bIsClosed = true;
            sendMessage();
            super.onDestroy();
        }
    }
    

    3.3 Socket 上传文件

    服务端:

        // 1.创建服务端 Socket 并绑定端口
        ServerSocket serverSocket=new ServerSocket(8888);
        S.println("~~~ Server is running,waiting for client  ~~~");
        // 2.调用 accept() 方法,等待客户连接
        Socket socket=serverSocket.accept();
        // 3.获取输入流
        InputStream inputStream = socket.getInputStream();
        // 4.获取文件名称
        String fileName="";
        int len2;
        while ((len2=inputStream.read())!=-1){
            if(len2=='	')
                break;
            else
                fileName+=String.valueOf ((char)(len2));
        }
        // 5.创建文件,用来存储用户上传数据
        FileOutputStream outputStream=new FileOutputStream(fileName,true);
        // 6.将用户上传文件保存到本地
        int len;
        byte[] buffer=new byte[1024];
        while ((len=inputStream.read(buffer,0,buffer.length))!=-1){
            outputStream.write(buffer,0,len);
        }
        outputStream.close();
        inputStream.close();
        socket.shutdownInput(); //关闭输入流
        socket.close();//关闭连接
    

    客户端:

        new AsyncTask<Void, Integer, Void>() {
            @Override
            protected Void doInBackground(Void... params) {
                try {
                    //1.创建Socket对象并获取输出流
                    Socket socket = new Socket(IP, PORT);
                    OutputStream outputStream = socket.getOutputStream();
                    // 2.写入文件名称,最后使用 	 分割
                    outputStream.write("Video5.mp4	".getBytes());
                    // 3.读取文件内容
                    InputStream inputStream=getResources().openRawResource(R.raw.video);
                    // 4.上传文件
                    int len;
                    byte[] buffer = new byte[1024];
                    while ((len = inputStream.read(buffer, 0, buffer.length)) != -1) {
                        
                        outputStream.write(buffer, 0, len);
                        outputStream.flush();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
                return null;
            }
            @Override
            protected void onPostExecute(Void aVoid) {
                Toast.makeText(UDPActivity.this, "Upload successfully.", Toast.LENGTH_SHORT).show();
            }
        }.execute();
    

    3.4UPD 通信

    服务端:

       //1.创建服务端 DatagramSocket 指定端口
       DatagramSocket socket=new DatagramSocket(8888);
       //2.创建数据报,用于接受客户端发送的数据
       byte[] data=new byte[1024]; //指定接受数据包的大小
       DatagramPacket packet=new DatagramPacket(data,data.length);
       //3.接受客户端发送的数据
       S.println("~~~ Server is running,waiting for client  ~~~");
       while (true){
           socket.receive(packet);
           //4.读取数据
           String info=new String(data,0,data.length);
           S.println("Client:"+ info);
           if(socket.isClosed())
           {
               S.println("Client has been exit.");
               break;
           }
       }
       socket.close(); //关闭 Socket
    

    客户端:

        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    //1.获取 InetAddress 对象,指定IP
                    InetAddress address = InetAddress.getByName(IP);
                    //2.要发送的信息
                    byte[] data = "Test".getBytes();
                    //3.创建 DatagramSocket,用来发送数据
                    DatagramSocket socket = new DatagramSocket();
                    //4.创建 DatagramPacket,用来存储要发送的数据,并指定目标IP和端口
                    DatagramPacket packet = new DatagramPacket(data, data.length, address, PORT);
                    //5.发送消息到服务端
                    socket.send(packet);
                    socket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }).start();
    

    3.5UPD 发送文件

    服务端:

       //1.创建服务端 DatagramSocket 指定端口
       DatagramSocket socket=new DatagramSocket(8888);
       S.println("~~~ Server is running,waiting for client  ~~~");
       //2.创建文件,用来存储用户上传数据
       File file=new File("Video.mp4");
       FileOutputStream outputStream=new FileOutputStream("Video.mp4",true);
       //3.不断接受客户端发送的数据
       while (true){
           //4.创建数据报,用于接受客户端发送的数据
           byte[] buffer=new byte[1024]; //指定接受数据包的大小
           DatagramPacket packet=new DatagramPacket(buffer,buffer.length);
           socket.receive(packet);
           //5.将数据写入文件
           outputStream.write(buffer,0,packet.getLength());
           if(socket.isClosed())
               break;
       }
       outputStream.close();
       socket.close(); //关闭 Socket
    

    客户端:

        new AsyncTask<Void, Integer, Void>() {
            @Override
            protected Void doInBackground(Void... params) {
                try {
                    //1.创建Socket对象并获取输出流
                    Socket socket = new Socket(IP, PORT);
                    OutputStream outputStream = socket.getOutputStream();
                    // 2.写入文件名称,最后使用 	 分割
                    outputStream.write("Video5.mp4	".getBytes());
                    // 3.读取文件内容
                    InputStream inputStream=getResources().openRawResource(R.raw.video);
                    // 4.上传文件
                    int len;
                    byte[] buffer = new byte[1024];
                    while ((len = inputStream.read(buffer, 0, buffer.length)) != -1) {
                        outputStream.write(buffer, 0, len);
                        outputStream.flush();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
                return null;
            }
            @Override
            protected void onPostExecute(Void aVoid) {
                Toast.makeText(UDPActivity.this, "Upload successfully.", Toast.LENGTH_SHORT).show();
            }
        }.execute();
    

    学习自:http://www.runoob.com/w3cnote/android-tutorial-socket-intro.html

  • 相关阅读:
    日期控件选择条件控制只能选择当前日期之前或当前日期之后
    记录一次ajax 429请求laravel api的错误
    如何配置Linux系统的IP地址?
    laravel 定时任务通过队列发送邮件
    ioutil.ReadFile 读取文件内容时为什么读取不到文件呢?open var2.go: no such file or directory
    swoole异步io操作
    PHP获取本周所有日期或者最近七天所有日期
    面试又给我问到MySQL索引,最全的一次整理
    Google资深工程师深度讲解Go语言基础语法(二)
    ps命令详解
  • 原文地址:https://www.cnblogs.com/-Tiger/p/8708443.html
Copyright © 2020-2023  润新知