zoukankan      html  css  js  c++  java
  • day24_网络编程(下)(TCP并发,模拟IE,DNS解析)


    1.并发上传(也就是多个用户连接上服务端并发上传->服务端多线程)

    如果不采取多线程,简单的加个while(true)行不行?:

    /*
     FileOutputStream fos=new FileOutputStream("C:\Users\ZhangHongQ\Desktop\Heart.txt");
       while(true){
           Socket s=ss.accept();
           System.out.println("IP: "+s.getInetAddress().getHostAddress()
                               +" Connect Success");
           
           InputStream is=s.getInputStream();
           OutputStream os=s.getOutputStream();
    
           byte[] buf=new byte[1024];
           int bytes=0;
           while((bytes=is.read(buf))!=-1)
                fos.write(buf,0,bytes);
           
           os.write("上传成功".getBytes());
           fos.close();
           s.close();
       }
     //如果有多个用户想上传,采用while(true),这时候产生了一个局限性
     //那就是B用户必须等A上传完才能上传,这是因为只有在下次循环才能
     //执行到ss.accept(),获取下一个用户的Socket流对象
    //那么为了可以让多个客户端同时并发访问服务端
    //服务端最好把每个客户端封装到一个单独的线程中,这样就可以同时处理多个客户端请求
    */

    在服务端开启多个线程(连上一个,开一个):

    客户端:

    package tcp;
    import java.net.Socket;
    import java.net.ServerSocket;
    import java.io.FileInputStream;
    import java.io.FileOutputStream;
    import java.io.InputStream;
    import java.io.OutputStream;
    import java.io.DataInputStream;
    import java.io.DataOutputStream;
    import java.io.File;
    class TCPCopyClient{
      public static void main(String[] args)throws Exception{
      
        if(args.length!=1){
          System.out.println("请上传一张图片");
           return;
         }
         File file=new File(args[0]);
         if(!(file.exists()&&file.isFile())){
             System.out.println("文件不存在或不是文件");
              return;
         }
         if(!args[0].endsWith(".jpg")){
            System.out.println("不能上传非.jpg文件");     
            return;
         }
         if(file.length()>5*1024*1024){
           System.out.println("文件过大");
            return;
         }
         Socket s=new Socket("192.168.1.101",10004);//只有不满足以上条件时才建立连接
         FileInputStream fis=new FileInputStream(file);
         OutputStream os=s.getOutputStream();
         
         
         byte[] buf=new byte[1024];
         int bytes=0;
         while((bytes=fis.read(buf))!=-1)
            os.write(buf,0,bytes);
         s.shutdownOutput();//在套接字的输出流末尾加了一个结束标志-1
         
         InputStream is=s.getInputStream();
         byte[] bufIn=new byte[1024];
         bytes=is.read(bufIn);
         System.out.println(new String(bufIn,0,bytes));
         fis.close();
         s.close();
      }
    }

    服务端:

    lass CurrentUpLoad implements Runnable{
    
    private Socket s;//s用连上服务端的客户端Socket对象初始化
     public CurrentUpLoad(Socket s){
        this.s=s;
     }
     public void run(){
      
       int count=0; 
       String ip=s.getInetAddress().getHostAddress();
      try{
      
       File file=new File(ip+".jpg");//已IP地址做为文件名
       while(file.exists())//循环判断该文件是否存在,如果已存在,防止覆盖,采取计数处理
           file=new File(ip+"("+(++count)+")"+".jpg");
       FileOutputStream fos=new FileOutputStream(file);
       
       System.out.println("IP: "+ip
                           +" Connect Success");
       
       InputStream is=s.getInputStream();
       OutputStream os=s.getOutputStream();
    
       byte[] buf=new byte[1024];
       int bytes=0;
       while((bytes=is.read(buf))!=-1)
            fos.write(buf,0,bytes);
       
       os.write("上传成功".getBytes());
       fos.close();
       s.close();
      }
      catch(Exception e){
       //throw new RuntimeException("上传失败");
       e.printStackTrace();
      }
    }
     
    }
    class Server{
      public static void main(String[] args)throws Exception{
          ServerSocket ss=new ServerSocket(10004);
         
    while(true
    ){
           Socket s
    =
    ss.accept();
    new Thread(new
     CurrentUpLoad(s)).start();
          }
          /*
           服务端启动:主线程,一旦一个客户端连上,创建一个线程并启动
                      如果依然执行主线程,Socket s=ss.accept();阻塞,直到
                      另一个客户端连接连接服务端
                      客户端的线程相互之间不影响
          */
      }
    }

    2.并发登录:

    客户端:

    /*
    客户端通过键盘录入用户民
    服务端对这个用户名进行校验
    
    如果该用户存在,在服务端显示xxx,已登录
    并在客户端显示xxx,欢迎光临
    
    如果该用户不存在,在服务端显示xxx,尝试登陆.
    并在客户端显示xxx,该用户不存在.
    
    最多登录三次(登录成功不在登录,不成功依然可以登录)
    */
    /*
    思想:
     对于每个连接成功,进行登录的用户
     服务端对每个用户单独用一个线程执行相同的动作(即三次验证)
    */
    package tcp;
    import java.net.*;
    import java.io.*;
    class LoginClient{
     public static void main(String[] args)throws Exception{
        Socket s=new Socket("192.168.1.100",10004);
        BufferedReader bfr=new BufferedReader(new InputStreamReader(System.in));
        PrintWriter pw=new PrintWriter(s.getOutputStream(),true);
        String user=null;
         BufferedReader bfrIn=new BufferedReader(new InputStreamReader(s.getInputStream()));
        
        for(int i=0;i<3;++i){//一个用户最多登录三次,成功服务端不在校验
          user=bfr.readLine();
          if(user==null)//不录入,直接结束
              break;
          pw.println(user);
          String line=bfrIn.readLine();
          System.out.println(line);
          if(line.contains("欢迎"))//已登录成功
              break;
        }
        s.close();
        
      }
    }

    服务端:

    class UserLogin implements Runnable{
     private Socket s;
     public UserLogin(Socket s){
      this.s=s;
     }
     public void run(){
         try{
             BufferedReader bfrIn=new BufferedReader(new InputStreamReader(s.getInputStream()));
             PrintWriter pw=new PrintWriter(s.getOutputStream(),true);
             String ip=s.getInetAddress().getHostAddress();
             System.out.println(ip+" Connect Success");
             for(int i=0;i<3;i++){
              String user=bfrIn.readLine();
              if(user==null)
                 break;//
    如果在客户端输入过程中结束输入(ctrl+c) //
    客户端for循环结束,s.close()加入-1标记,bfrIn.readLine()在循环过程中读到两次-1返回null
              BufferedReader bfr=new BufferedReader(new FileReader("user.txt"));//假设user.txt存入了允许验证通过的用户名
              String line=null;
              boolean flag=false;
              while((line=bfr.readLine())!=null){
                 System.out.print(line+" ");
                 if(line.equals(user)){//如果该用户存在,flag=true,结束循环
                  flag=true;           //否则遍历完也没有该用户,flag一直为false
                  break;
                }
              }
              if(flag){
                System.out.println(user+"已登录");
                pw.println(user+"欢迎光临");
                break;//成功登录不在进行校验
              }
              else{
                System.out.println(user+"尝试登录");
                pw.println(user+"用户不存在");
              }
             } 
           s.close();
         }
         catch(Exception e){
          e.printStackTrace();
         
         }
      }
    }
    class LoginServer{
        public static void main(String[] args)throws Exception{
            ServerSocket ss=new ServerSocket(10004);
            while(true){//n多客户端向服务端发出校验,因此循环连接
              Socket s=ss.accept();//再次循环,将在此堵塞,等待连接建立
              new Thread(new UserLogin(s)).start();
            }
         }
    }
    /*
    测试:
    服务端:
     s.shutdownOutput();
        System.out.println(s.isOutputShutdown());//true
        OutputStream out=s.getOutputStream();
        out.write("GG".getBytes());    
    客户端:
        byte[] buf=new byte[1024];
          int bytes=is.read(buf);
          System.out.println(bytes);//-1
        System.out.println(new String(buf,0,bytes));
        s.close();
    服务端调用 s.shutdownOutput()/s.close();会关闭服务端的s.getOutputStream,会在流的末尾写入-1
    客户端的s.getIuputStream()会读到写入的-1;
    
    客户端调用 s.shutdownOutput();会关闭客户端的s.getOutputStream,会在流的末尾写入-1 
    服务端的s.getIuputStream()会读到写入的-1;
    */

    3.客户端:浏览器,服务端:自定义

    /*
    1.客户端:浏览器
      服务端:java应用程序
    2.客户端:浏览器
      服务端:Tomcat服务器(java编写,封装了ServerSocket)
    */
    package tcp;
    import java.net.*;
    import java.io.*;
    class Server{
        public static void main(String[] args)throws Exception{
            
            ServerSocket ss=new ServerSocket(10003);    
            Socket s=ss.accept();
            System.out.println(s.getInetAddress().getHostAddress()+"Connect Success");
            PrintWriter pw=new PrintWriter(s.getOutputStream(),true);
            
            byte[] buf=new byte[1024];
            int bytes=s.getInputStream().read(buf);
            System.out.println(new String(buf,0,bytes));
            pw.println("客户端你好");
            s.close();
            ss.close();
            
            //System.out.println(InetAddress.getLocalHost().getHostAddress());
            
        }
    }
    /*
    在IE浏览器的地址栏输入http://自己主机的IP地址:端口
    看到服务端的反馈信息:客户端你好
    
    或者在cmd中使用telnet:
    Telnet协议是TCP/IP协议族中的一员,
    是Internet远程登陆服务的标准协议和主要方式。
    它为用户提供了在本地计算机上完成远程主机工作的能力。
    在终端使用者的电脑上使用telnet程序,用它连接到服务器。
    终端使用者可以在telnet程序中输入命令,这些命令会在服务器上运行,
    就像直接在服务器的控制台上输入一样。
    
    前提必须安装,在控制面板中的启用或关闭windows功能中选择开启telnet客户端/服务器
    命令:
     telnet 主机IP地址 端口
     同样会收到服务端反馈信息。
    */

    以上代码打印出浏览器向Server发送了什么请求信息:

    IE向自定义服务端发送的信息

    4.窗体模拟IE的输入链接->转到

    /*图形化界面的简单模拟*/
    /*
    客户端:图形化界面应用程序
    图形化界面:Frame,TextField,Button,TextArea
    事件源:button
    事件:ActionEvent
    */
    package myie;
    import java.net.*;
    import java.io.*;
    import java.awt.*;
    import java.awt.event.*;
    class MyIEForm{
       private Frame frame;
       private TextField textField;
       private Button button;
       private TextArea textArea;
       public MyIEForm(){
        initi();
       }
       public void initi(){
        frame=new Frame("MyIE");
        frame.setBounds(200,300,600,500);
        frame.setLayout(new FlowLayout());
        textField=new TextField(50);
        button=new Button("转到");
        textArea=new TextArea(30,60);
        frame.add(textField);
        frame.add(button);
        frame.add(textArea);
        ListenerReg();
        frame.setVisible(true);
       }
       public void ListenerReg(){
         frame.addWindowListener(new WindowAdapter(){
            public void windowClosing(WindowEvent we){
            
                System.exit(0);    
            
            }
          });
         button.addActionListener(new ActionListener(){
             public void actionPerformed(ActionEvent ae){
              textArea.setText("");
              String url=textField.getText();            
              /*url:http://192.168.1.101:8080/myweb/Demo.html*/
              int index_1=url.indexOf("/");
              int index_2=url.indexOf("/",index_1+2);
              String hostPort=url.substring(index_1+2,index_2);//192.168.1.101:8080
              String[] address=hostPort.split(":");
              String serverSource=url.substring(index_2);//192.168.1.101:8080
             try{ 
              openSource(address[0],Integer.parseInt(address[1]),serverSource);
             }
             catch(Exception e){
               throw new RuntimeException("Open Fail");
             
             }
            }
          });
        }
      public void openSource(String ip,int port,String serverSource)throws Exception{
         Socket s=new Socket(ip,port);
         PrintWriter pw=new PrintWriter(s.getOutputStream(),true);
         pw.println("GET "+serverSource+" HTTP/1.1");
         pw.println("Accept:  */*");
         pw.println("Accept-Language: zh-Hans-CN,zh-Hans");
         pw.println("Host: "+ip+":"+port);
         pw.println("Connection: Keep-Alive");
         pw.println();//结尾必须要有换行
         InputStream is=s.getInputStream();
         byte[] buf=new byte[1024];
         int bytes=is.read(buf);
         textArea.append(new String(buf,0,bytes));
         
      } 
       
    }
    
    class MyIE{
     public static void main(String[] args){
     
        new MyIEForm(); 
    }
    }

    这里用到的服务端是Tomcat(Web 应用服务器)服务器,下载地址:http://tomcat.apache.org/ 左边的download可以选择6.0/7.0,Tomcat服务端默认端口为8080

    下载后直接解压,找到bin目录下的startup.bat启动服务端,在其webapps目录下新建myweb目录,在myweb下放入一个demo.html

    之后运行服务端:

    MyIE

    可以看到上面的一堆信息为客户端收到的Tomcat服务端发送的响应消息头:服务器,最后一次修改时间,内容长度byte等等,下面获取到的demo.html,由于不能像IE一样解析html代码,因此也会显示出来,可以看到传输层的TCP协议获取服务端全部信息(反馈信息+请求资源).

    5.类URL与URLConnection

    /*
    URL:
    类 URL 代表一个统一资源定位符,它是指向互联网“资源”的指针。
    资源可以是简单的文件或目录,也可以是对更为复杂的对象的引用,
    例如对数据库或搜索引擎的查询。有关 URL 的类型和格式的更多信息,
    可从以下位置找到: 
    http://www.socs.uts.edu.au/MosaicDocs-old/url-primer.html 
    通常,URL 可分成几个部分。上面的 URL 示例指示使用的协议为 http (超文本传输协议)并且该信息驻留在一台名为 www.socs.uts.edu.au 的主机上。
    主机上的信息名称为 /MosaicDocs-old/url-primer.html。
    主机上此名称的准确含义取决于协议和主机。该信息一般存储在文件中,但可以随时生成。
    该 URL 的这一部分称为路径 部分。 
    
    URL 可选择指定一个“端口”,它是用于建立到远程主机 TCP 连接的端口号。
    如果未指定该端口号,则使用协议默认的端口。
    例如,http 协议的默认端口为 80。还可以指定一个备用端口,如下所示: 
    http://www.socs.uts.edu.au:80/MosaicDocs-old/url-primer.html
    
    */
    package url;
    import java.net.URL;
    import java.net.URLConnection;
    import java.io.InputStream;
    class URLDemo{
        public static void method(URL url){
          System.out.println("getFile: "+url.getFile()+"
    getHost: "
                               +url.getHost()+"
    getPath: "+url.getPath()
                              +"
    getPort: "+url.getPort()+"
    getProtocol: "+
                               url.getProtocol()+"
    getQuery: "+url.getQuery());
        
        }
        public static void main(String[] args)throws Exception{
          /*重要方法*/
          URL url=new URL("http://192.168.1.101:80/myweb/demo.html?name=zhang&age=10");//可能解析不了该字符串->MalformedURLException
          method(url);
          System.out.println();
          url=new URL("http://192.168.1.101/myweb/demo.html");
          method(url);
          /*
          如果getPort返回-1(未指定端口),则使用协议默认端口
          */
        }
    }

    URL

    /*
    public URLConnection openConnection()
                                 throws IOException
    返回一个 URLConnection 对象,它表示到 URL 所引用的远程对象的连接。 
    每次调用此 URL 的协议处理程序的 openConnection 方法都打开一个新的连接。 
    
    如果 URL 的协议(例如,HTTP 或 JAR)存在属于以下包或其子包之一的公共、专用 URLConnection 子类:java.lang、java.io、java.util、java.net,返回的连接将为该子类的类型。例如,对于 HTTP,将返回 HttpURLConnection,对于 JAR,将返回 JarURLConnection。 
    
    
    */
    class URLConnDemo{
     public static void main(String[] args)throws Exception{
       URL url=new URL("http://192.168.1.101:8080/myweb/Demo.html");//连接到Tomcat服务器
       URLConnection urlCon=url.openConnection();//连接到远程主机
       //System.out.println(urlCon);
       /*
       sun.net.www.protocol.http.HttpURLConnection:
       http://192.168.101.1:8080/myweb/Demo
       .html
       */
       InputStream is=urlCon.getInputStream();//获取到输入流
       byte[] buf=new byte[1024];
       int bytes=is.read(buf);
       System.out.println(new String(buf,0,bytes));
       /*
       读取到的反馈信息中没有响应头
       原因参照:(示意图)
       */
     }
    }

    URLConn

    应用层HTTP协议获取信息

    6.域名解析:

    /*
    浏览器对 http://192.168.1.101:8080/myweb/Demo.html 解析:
    首先查看是什么协议,然后启动相应的协议软件解析,
    把192.168.1.101:8080封装成Socket
    但是一般写法(便于记忆):http://www.sina.com.cn/myweb/demo.html
      www.sina.com.cn->将主机名翻译成IP地址->域名解析->DNS   

    DNS:
    DNS 是域名系统 (Domain Name System) 的缩写,
    是因特网的一项核心服务,它作为可以将域名和IP地址相互映射的一个分布式数据库,
    能够使人更方便的访问互联网,而不用去记住能够被机器直接读取的IP数串。

    */

    示意图:

    DNS解析

  • 相关阅读:
    关于管理的经典故事(员工激励)
    通过SQL语句获取MSSQL数据库的数据字典
    AMF(java)学习笔记(一)
    ActionScript 3.0著名开源库 大集合
    Flash全屏功能详解
    NIO网络编程框架MINA2.0学习笔记(一)
    一份相当巨大的AS类库
    java NIO非阻塞式IO网络编程学习笔记(一)
    数据传输序列化与反序列化协议大全
    RED5学习笔记(一):RED5简介
  • 原文地址:https://www.cnblogs.com/yiqiu2324/p/3188654.html
Copyright © 2011-2022 走看看