Loading

Java 网络编程

关键的两个类

Socket类

该类实现客户端套接字,套接字指的是两台设备之间通讯的端点。

构造方法

public Socket(String host, int port) :创建套接字对象并将其连接到指定主机上的指定端口号。如果指定的host是null ,则相当于指定地址为回送地址。

Socket client = new Socket("127.0.0.1", 6666);

这里的参数中的ip地址和端口不是设定本机的,而是设定的服务器的ip和服务器中服务端的端口号

成员方法

  • public InputStream getInputStream() : 返回此套接字的输入流。
    • 如果此Scoket具有相关联的通道,则生成的InputStream 的所有操作也关联该通道。
    • 关闭生成的InputStream也将关闭相关的Socket。
  • public OutputStream getOutputStream() : 返回此套接字的输出流。
    • 如果此Scoket具有相关联的通道,则生成的OutputStream 的所有操作也关联该通道。
    • 关闭生成的OutputStream也将关闭相关的Socket。
  • public void close() :关闭此套接字。
    • 一旦一个socket被关闭,它不可再使用。
    • 关闭此socket也将关闭相关的InputStream和OutputStream 。
  • public void shutdownOutput() : 禁用此套接字的输出流。
    • 任何先前写出的数据将被发送,随后终止输出流。

ServerSocket类

ServerSocket类:这个类实现了服务器套接字,该对象等待通过网络的请求。

构造方法

public ServerSocket(int port) :使用该构造方法在创建ServerSocket对象时,就可以将其绑定到一个指定的端口号上,参数port就是端口号。

ServerSocket server = new ServerSocket(6666);

这里的端口号指的是本地服务的端口号,客户端通过这个端口号找到相应的服务

成员方法

public Socket accept() :侦听并接受连接,返回一个新的Socket对象,用于和客户端实现通信。该方法会一直阻塞直到建立连接。

一个简单的TCP通信案例

服务端

public class MyTcpServer {
    public static void main(String[] args) throws  IOException{
        ServerSocket server = new ServerSocket(6666);
        while (true){
            Socket clientSocket = server.accept();
            System.out.println(clientSocket);
            new Thread(()->{
                try (InputStream inputStream = clientSocket.getInputStream();
                     OutputStream outputStream = clientSocket.getOutputStream()
                ) {
                    deal(inputStream,outputStream);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }).start();
        }
    }

    private static void deal(InputStream inputStream, OutputStream outputStream) throws IOException {
        BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
        BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(outputStream));
        String str = null;
        str = reader.readLine();
        System.out.println(str);

        writer.write("收到!");
        writer.newLine();
        writer.flush();

        System.out.println("deal finish!");
    }
}

客户端

public class MyTcpClient {
    public static void main(String[] args) throws IOException {
        try(Socket socket = new Socket("localhost", 6666);
            InputStream inputStream = socket.getInputStream();
            OutputStream outputStream = socket.getOutputStream();
            ){
            BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(outputStream));
            bufferedWriter.write("r u ok?");
            bufferedWriter.newLine();
            bufferedWriter.flush();

            BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
            String str= null;
            str = reader.readLine();
            System.out.println(str);
        }

    }

}

注:一开始写代码的时候设置读取数据的时候使用的循环,但是使用循环就会产生,再读取完数据之后依然会继续等待直到这个连接结束(因为readLine方法会阻塞)
所以如果两边都有使用循环获取文字就会产生死锁,导致不能继续进行下去,见下方

发送多行数据的程序

一个错误的案例

服务端

public class MyTcpServer {
    public static void main(String[] args) throws  IOException{
        ServerSocket server = new ServerSocket(6666);
        while (true){
            Socket clientSocket = server.accept();
            System.out.println(clientSocket);
            new Thread(()->{
                try (InputStream inputStream = clientSocket.getInputStream();
                     OutputStream outputStream = clientSocket.getOutputStream()
                ) {
                    deal(inputStream,outputStream);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }).start();
        }
    }

    private static void deal(InputStream inputStream, OutputStream outputStream) throws IOException {
        BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
        BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(outputStream));
        String str = null;
        while ((str =  reader.readLine())!=null){
            System.out.println(str);
        }

        writer.write("收到!");
        writer.newLine();
        writer.flush();

        System.out.println("deal finish!");
    }
}

客户端

public class MyTcpClient {
    public static void main(String[] args) throws IOException {
        try(Socket socket = new Socket("localhost", 6666);
            InputStream inputStream = socket.getInputStream();
            OutputStream outputStream = socket.getOutputStream();
            ){
            BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(outputStream));
            bufferedWriter.write("hello!");
            bufferedWriter.newLine();
            bufferedWriter.write("r u ok?");
            bufferedWriter.newLine();
            bufferedWriter.flush();

            BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
            String str= null;
            while ((str = reader.readLine())!=null){
                System.out.println(str);
            }

        }

    }

}

然后就会发生之前讲过的问题

  1. 客户端在发送完数据之后给服务端,服务端输出了客户端给的数据,
  2. 但是服务端的循环还没跳出 在下一次循环判断的时候,阻塞了,就不能执行下面发送给客户端数据的代码
  3. 客户端此时也在等待服务端发送数据,而服务端等待客户端发送数据,于是就导致程序卡住了

程序改进

只要程序客户端在发送完数据之后告诉服务端我再也不发送数据了,那么服务端就不会继续阻塞等待了,所以解决办法就是,先发送数据的一方调用socket.shutdownOutput();,这样的话,服务端在接受完数据之后就不会继续阻塞等待客户端发送数据了,这样程序就能够正常的执行下去
客户端

public class MyTcpClient {
    public static void main(String[] args) throws IOException {
        try(Socket socket = new Socket("localhost", 6666);
            InputStream inputStream = socket.getInputStream();
            OutputStream outputStream = socket.getOutputStream();
            ){
            BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(outputStream));
            bufferedWriter.write("hello!");
            bufferedWriter.newLine();
            bufferedWriter.write("r u ok?");
            bufferedWriter.newLine();
            bufferedWriter.flush();

            socket.shutdownOutput();//// 关闭输出流,通知服务端,写出数据完毕

            BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
            String str= null;
            while ((str = reader.readLine())!=null){
                System.out.println(str);
            }
        }
    }
}

服务端

public class MyTcpServer {
    public static void main(String[] args) throws  IOException{
        ServerSocket server = new ServerSocket(6666);
        while (true){
            Socket clientSocket = server.accept();
            System.out.println(clientSocket);
            new Thread(()->{
                try (InputStream inputStream = clientSocket.getInputStream();
                     OutputStream outputStream = clientSocket.getOutputStream()
                ) {
                    deal(inputStream,outputStream);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }).start();
        }
    }

    private static void deal(InputStream inputStream, OutputStream outputStream) throws IOException {
        BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
        BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(outputStream));
        String str = null;
        while ((str =  reader.readLine())!=null){
            System.out.println(str);
        }

        writer.write("收到!");
        writer.newLine();
        writer.flush();

        System.out.println("deal finish!");
    }
}

TCP文件传输

客户端

public class MyTcpFileClient {
    public static void main(String[] args) throws IOException {
        try(Socket socket = new Socket("127.0.0.1",9000);
            InputStream is = socket.getInputStream();
            OutputStream os = socket.getOutputStream();

            BufferedInputStream bis = new BufferedInputStream(new FileInputStream("./src/kehao/net/tcp/gakki.jpg"))){
            byte[] bytes = new byte[1024];
            int len=0;
            while ((len = bis.read(bytes))!=-1){
                os.write(bytes,0,len);
            }
            os.flush();

            System.out.println("发送完成");
            socket.shutdownOutput();

            BufferedReader reader = new BufferedReader(new InputStreamReader(is));
            String str = null;
            while ((str = reader.readLine())!=null){
                System.out.println(str);
            }
        }
    }
}

服务端

public class MyTcpFileServere {
    public static void main(String[] args) throws IOException {
        ServerSocket serverSocket = new ServerSocket(9000);
        while (true){
            Socket socket = serverSocket.accept();
            new Thread(()->{
                try (InputStream is = socket.getInputStream();
                     OutputStream os = socket.getOutputStream()
                ){
                    deal(is,os);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }).start();
        }
    }

    private static void deal(InputStream is, OutputStream os) throws IOException {
        try (BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("src/kehao/net/tcp/download/"+System.currentTimeMillis()+"gakki.jpg"))){
            byte[] bytes = new byte[1024];
            int len = 0;
            while ((len = is.read(bytes))!=-1){
               bos.write(bytes,0,len);
            }
            bos.flush();
        }
        System.out.println("写入完成");
        BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(os));
        writer.write("已经收到!");
        writer.newLine();
        writer.flush();
    }

}
posted @ 2021-03-25 21:23  克豪  阅读(66)  评论(0)    收藏  举报