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()+"\ngetHost: "
                           +url.getHost()+"\ngetPath: "+url.getPath()
                          +"\ngetPort: "+url.getPort()+"\ngetProtocol: "+
                           url.getProtocol()+"\ngetQuery: "+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解析

posted @ 2013-07-13 21:13  伊秋  阅读(632)  评论(0编辑  收藏  举报