zoukankan      html  css  js  c++  java
  • JavaSE——TCP协议网络编程(二)

    1.Java网络编程与多线程的综合应用:

          类Socket提供了方法getInputStream ()和getOutStream()来得到对应的输入/输出流以进行读/写操作,这两个方法分别返回InputStream和OutputSteam类对象。为了便于读/写数据,我们可以在返回的输入/输出流对象上建立过滤流,如DataInputStream、DataOutputStream或PrintStream类对象,对于文本方式流对象,可以采用InputStreamReader和OutputStreamWriter、PrintWirter等处理。

    服务器端程序设计
        在服务器端,利用ServerSocket类的构造函数ServerSocket(int port)创建一个ServerSocket类的对象,port参数传递端口,这个端口就是服务器监听连接请求的端口,如果在这时出现错误将抛出IOException异常对象,否则将创建ServerSocket对象并开始准备接收连接请求。
        服务程序从调用ServerSocket的accept()方法开始,直到连接建立。在建立连接后,accept()返回一个最近创建的Socket对象,该Socket对象绑定了客户程序的IP地址或端口号。
    客户端程序设计
        当客户程序需要与服务器程序通信时,需在客户机创建一个Socket对象。Socket类有构造函数Socket(InetAddress addr,int port)和Socket(String host,intport),两个构造函数都创建了一个基于Socket的连接服务器端流套接字的流套接字。对于第一个InetAddress子类对象通过addr参数获得服务器主机的IP地址,对于第二个函数host参数包被分配到InetAddress对象中,如果没有IP地址与host参数相一致,那么将抛出UnknownHostException异常对象。两个函数都通过参数port获得服务器的端口号。假设已经建立连接了,网络API将在客户端基于Socket的流套接字中捆绑客户程序的IP地址和任意一个端口号,否则两个函数都会抛出一个IOException对象。
        如果创建了一个Socket对象,那么它可通过get-InputStream()方法从服务程序获得输入流读传送来的信息,也可通过调用getOutputStream()方法获得输出流来发送消息。在读写活动完成之后,客户程序调用close()方法关闭流和流套接字。

    例实现服务端与客户端简单的通信;

    服务器端代码:

    public class Server1 {
    
        public static void main(String[] args) {
            Scanner input = new Scanner(System.in);
    
            try {
                @SuppressWarnings("resource")
    //创建一个ServerSocket在端口4700监听客户请求 ServerSocket server
    = new ServerSocket(9880); while (true){
    //使用accept()阻塞等待客户请求,有客户
              //请求到来则产生一个Socket对象,并继续执行
    Socket client
    = server.accept(); System.out.println("来自"+ client.getInetAddress().getHostAddress()+"的客户端已连接成功!"); if(client != null){ //开始对话 OutputStream os = client.getOutputStream();//从 client(Socket对象) 获取一个输出字节流
    //由Socket对象得到输出流,并构造PrintWriter对象 PrintWriter pw = new PrintWriter(new BufferedWriter(new OutputStreamWriter(os))); System.out.print("我说:"); String serverMsg = input.nextLine(); pw.println(serverMsg); //发送 serverMsg 给客户端 pw.flush();//刷新输出流,使Client马上收到该字符串 //服务端读取客户端发送来的信息 InputStream is = client.getInputStream(); //获取来自客户端的字节流 //由Socket对象得到输入流,并构造相应的BufferedReader对象 BufferedReader reader = new BufferedReader(new InputStreamReader(is)); String msg = reader.readLine(); //每次读取一行信息 System.out.print("客户端说:"+msg); } } } catch (IOException e) { e.printStackTrace(); } input.close(); } }
    客户端代码:

    public class Client1 { public static void main(String[] args) { @SuppressWarnings("resource") Scanner input = new Scanner(System.in); try { @SuppressWarnings("resource")
    //向本机的9880端口发出客户请求 Socket client
    = new Socket("127.0.0.1",9880); //通过socket获得输入流 InputStream is = client.getInputStream();
    //由系统标准输入设备构造BufferedReader对象 BufferedReader reader
    = new BufferedReader(new InputStreamReader(is)); //读取输入流 String msg = reader.readLine(); //打印来自服务端的信息
    //系统标准输出上打印读入的字符串
    System.out.println(
    "服务端说:"+msg); //向服务端发送消息 System.out.print("我说:");
           //输入要向服务端发送的信息 String clientMsg
    = input.nextLine(); //获得输出字节流 OutputStream os = client.getOutputStream();
            //将输出字节流转换为字节流加缓冲(缓冲后的为字符流)构造打印流对象
    PrintWriter pw = new PrintWriter(new BufferedWriter(new OutputStreamWriter(os))); pw.println(clientMsg); //将从系统标准输入读入的字符串输出到Server pw.flush();//刷新输出流,使Server马上收到该字符串 } catch (UnknownHostException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } }

    Output:

    存在问题:

    双方通信须服务端先发起对话,并且是一问一答的形式;

    客户端向服务端发送完最后一句话后结束程序。

    改进方法:线程操作。分别将读取和发送信息装载在两个线程中,使读取和发送可以独立进行。

    使双方可以自由对话

    改进代码:

    读线程
    
    public class ReaderThread extends Thread {
    
        
            private Socket socket ; //传入 socket
            public ReaderThread(String name ,Socket socket){
                super(name);
                this.socket = socket;
                start();
                
            }
            public void run(){
                InputStream is;
                try {
                    is = socket.getInputStream();
                    BufferedReader reader = new BufferedReader(new InputStreamReader(is));
                    boolean flag = true;
                    do{
                        
                        String msg = reader.readLine();
                        System.out.println("对方说:" + msg);
                        flag = "byebye".equals(msg)?false:true;//当 msg 为 byebye 时 flag 为 false,线程结束
                    }while(flag);
                    
                } catch (IOException e) {
                    
                    e.printStackTrace();
                }finally{
                    try {
                        socket.shutdownInput(); //关闭输入流
                    } catch (IOException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }
            }
    }
    写线程
    
    public class WriterThread extends Thread {
        private Socket socket;
        public  WriterThread(String name,Socket socket){
            super(name);
            this.socket = socket;
            start();
        }
        public void run(){
            @SuppressWarnings("resource")
            Scanner input = new Scanner(System.in);
            try {
                OutputStream os = socket.getOutputStream();
                PrintWriter pw = new PrintWriter(new BufferedWriter(new OutputStreamWriter(os)));
                boolean flag = true;
                
                do{
                    
                    System.out.print("我说:");
                    String servermsg = input.nextLine();
                    pw.println(servermsg);
                    pw.flush();
                    flag = "byebye".equals(servermsg)?false:true;
                }while(flag);
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }finally{
                try {
                    socket.shutdownOutput();
                } catch (IOException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        }
    }
    服务器端
    
    public class Server1 {
    
        public static void main(String[] args) {
            Scanner input = new Scanner(System.in);
    
            try {
                
                @SuppressWarnings("resource")
                ServerSocket server = new ServerSocket(9880);
                while (true){
                     Socket client = server.accept();
                     System.out.println("来自"+ client.getInetAddress().getHostAddress()+"的客户端已连接成功!");
                     if(client != null){
                         //开始对话
                        
                         new WriterThread("服务端",client);
                         new ReaderThread("服务端",client);
                            
                             
                     }
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
            
            
            input.close();
    
        }
    
    }
    客户端
    
    public class Client1 {
    
        public static void main(String[] args) {
            
            try {
                
                Socket client = new Socket("127.0.0.1",9880);
                
                 new ReaderThread("客户端",client);
                 new WriterThread("客户端",client);
                
            } catch (UnknownHostException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
    
        }
    
    }

    运用线程操作,将用户键盘输入的内容装载在 WriterThread 中,将接收到的消息装载在 ReadThread 中,在服务器端和客户端分别实例化这两个线程,需要注意的是,在服务器端实例化的线程名称设置为 服务器 ,客户端为 客户端 ,但两个线程的套接字均为Socket的对象 client ,因为 ServerSocket 一般仅用于设置端口号和监听,真正进行通信的是服务器端的Socket与客户端的Socket,在ServerSocket 进行accept之后,就将主动权转让了。 

    缓冲区读者从输入流读取数据,且这个输入流是系统默认输入流,也就是用户在控制台的输入,用户在控制台的输入的东西会先到缓冲区,如果不放到缓冲区,一次只能读到1个字符。

    while循环中的reader.readLine()这个方法是从缓冲区读出一行字符放到 msg 这个字符串里面,再判断这个字符串是否与 byebye 相等,相等则结束循环,比较字符串内容相等,必须用字符串的equals()方法,不可用 == 因为 == 是比较地址是否相等。

    输出部分,是先声明一个打印流,这个打印流通过实例获取的输出字节流,将输出字节流装入输出缓冲流,,然后实例该缓冲流获得的。有了这个打印流之后,就可以使用 pw.println(servermsg)来对其打印对象输出 servermag 信息,将打印流里的文本信息 发送出去,如果是服务端执行到该代码时,则将打印流中的信息发送到客户端,反之亦然。servermsg 为用户输入的内容,输入可用 Scanner 函数实现。

    最后结束时,要关闭这个流,Socket 类有一个方法 shutdownOutput/shutdownInput 分别为关闭输出流和输入流。

    输入输出流进行双方通信,而线程则使得双方可以自由通信,即读和写的自由进行。

    如有不对之处还望指正,谢谢(●'◡'●)

    //由系统标准输入设备构造BufferedReader对象

  • 相关阅读:
    ERP渠道管理添加验证和查询(二十二)
    SqlServer导入Excel数据
    WebApi帮助类
    SqlServer 递归查询树
    SqlServer查看表、存储过程、耗时查询、当前进程、开销较大的语句
    Excel上传找到错误数据类型
    索引Hint提示(INDEX Hint)
    SqlServer批量Sql一个表的数据导入到另一个数据
    SqlServer 游标
    JAVA运算符
  • 原文地址:https://www.cnblogs.com/linlin0/p/6210389.html
Copyright © 2011-2022 走看看