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

原文地址:https://www.cnblogs.com/-Tiger/p/8708443.html