TCP编程

发布时间 2023-04-07 10:21:29作者: 七分酷

TCP编程

Java中的TCP

java对TCP协议进行了封装,客户端和服务端都使用Socket代表两个的通信端口,不过客户端使用的是Socket类,服务端使用的是ServerSocket.

客户端代码示例
public class ClientDemo {
    public static void main(String[] args) throws IOException {
        //创建客户端套接字数据,参数是目的服务器的IP地址和端口
        Socket socket = new Socket("127.0.0.1",8080);
        //根据套接字创建套接字输出流
        OutputStream os = socket.getOutputStream();
        String str="今天天气好";
        //输出流写入
        os.write(str.getBytes());
        socket.close();
    }
}
服务端代码示例
public class ServerDemo {
    public static void main(String[] args) throws IOException {
        //创建一个服务器接收套接字对象,参数应该是服务器上的端口
        ServerSocket ss = new ServerSocket(8080);
        //监听客户端发送的套接字对象
        Socket socket = ss.accept();
        //根据套接字获取字节输入流
        InputStream is = socket.getInputStream();
        //操作套接字输入流
        byte[] bytes = new byte[1024];
        int len;
        while ((len= is.read(bytes))!=-1){
            System.out.println(new String(bytes,0,len));
        }
        //先开后关
        is.close();
        ss.close();
    }
}

必须先开启服务端,再开启客户端

多发多收

DataInputStream 和 DataOutputStream 是数据字节流,分别继承了FilterInputStream和FilterOutputStream,爷爷类是InputStream和OutputStream

作用

用来封装和装饰其他字节流

客户端代码示例
public class ClientDemo {
    public static void main(String[] args) {
        //JDK7的新特性,try-with-resources
        //格式: try(启用的资源) {} catch(e) {}
        //作用,自动为启用的资源关流,凡是实现了Closeable接口的,都需要关流
        try (Socket socket = new Socket("localhost",8080);){

            OutputStream os = socket.getOutputStream();

            DataOutputStream dos = new DataOutputStream(os);

            Scanner sc = new Scanner(System.in);
            System.out.println("请输入:");

            while (true){
                String next = sc.next();

                dos.writeUTF(next);
                dos.flush();

                if (next.equals("886")){
                    System.out.println("客户端停止");
                    break;
                }
            }

        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}
服务端代码示例
public class ServerDemo {
    public static void main(String[] args) {
		
        
        try (ServerSocket ss = new ServerSocket(8080);
             Socket accept = ss.accept();
        ) {

            InputStream is = accept.getInputStream();

            DataInputStream dis = new DataInputStream(is);

            while (true) {
                try {
                    //报EOFException异常意味着,流已经读到末尾了
                    String s = dis.readUTF();
                    System.out.println(s);
                } catch (EOFException e) {
                    //getRemoteSocketAddress() 获取客户端IP地址
                    System.out.println(accept.getRemoteSocketAddress() + "下机了");
                    break;
                }
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}

自由发送

以上代码有个缺点,next()和readUTF()方法都会造成阻塞,这会导致信息以固定的模式发送:客户端发送--->服务端接收--->服务端发送--->客户端接收,因此应该设置两个线程,让两端发送时互不干涉。

客户端代码示例
public class ClientDemo {
    public static void main(String[] args) {

        try (Socket socket = new Socket("localhost", 8080)

        ) {
            //封装套接字输入流,用于接收数据
            DataInputStream dis = new DataInputStream(socket.getInputStream());
            //封装套接字输出流,用于发送数据
            DataOutputStream dos = new DataOutputStream(socket.getOutputStream());
            //创建键盘输入流
            Scanner sc = new Scanner(System.in);
            System.out.println("开始聊天");

            //获取对方的IP地址,先获取原始格式,再强转成InetSocketAddress
            InetSocketAddress rsa = (InetSocketAddress) socket.getRemoteSocketAddress();
            String server = rsa.getHostString();
            //创建一个线程用来服务端发送数据,客户端接收数据,避免readUTF() 造成的阻塞问题
            new Thread() {
                @Override
                public void run() {
                    //接送数据
                    try {
                        while (true) {
                            String rMsg = dis.readUTF();
                            System.out.println("我是服务端" + server + ":" + rMsg);
                        }
                    } catch (IOException e) {
                        System.out.println("客服已下线,请为客服打分吧");
                    }
                }
            }.start();

            while (true) {
                String s = sc.nextLine();
                //发送数据
                dos.writeUTF(s);
                dos.flush();

                //当客户端输入886,终止循环
                if (s.equals("886")) {
                    System.out.println("我是客户端,我下机了");
                    break;
                } else {
                    System.out.println("我是客户端:" + s);
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
服务端代码示例
public class ServerDemo {
    public static void main(String[] args) {

        try (
                //创建服务器套接字对象ServerSocket,并监听指定端口
                ServerSocket ss = new ServerSocket(8080);
                //监听客户端,并获取客户端套接字对象
                Socket accept = ss.accept();
        ) {
            //读取并接收数据
            //获取套接字输入流,接收数据
            DataInputStream dis = new DataInputStream(accept.getInputStream());
            //获取套接字输出流,发送数据
            DataOutputStream dos = new DataOutputStream(accept.getOutputStream());
            //获取对方的IP
            InetSocketAddress rsa = (InetSocketAddress) accept.getRemoteSocketAddress();
            String client = rsa.getHostString();

            Scanner sc = new Scanner(System.in);
            //创建另一个线程用来客户发送数据,服务端接收数据,避免readUTF() 造成的阻塞问题
            new Thread(() -> {
                try {
                    //循环接收数据
                    while (true) {
                        String s = dis.readUTF();
                        System.out.println("我是客户端" + client + ":" + s);
                    }
                } catch (IOException e) {
                    System.out.println("用户已下线");
                    ;
                }
            }).start();

            //循环发送
            while (true) {
                try {
                    //发送数据
                    String s1 = sc.next();
                    dos.writeUTF(s1);
                    System.out.println("我是服务端:" + s1);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

    }
}

image-20230324142245390

image-20230324142310376