Java——网络编程(二)


第三讲     应用

一、用TCP客户端并发上传图片

1、一对一(单线程)上传的思路:

客户端

        a、服务端点。

        b、读取客户端已有的图片数据

        c、通过Socket输出流将数据发给服务端

        d、读取服务端反馈信息。

        e、关闭

服务端

        a、服务端服务,并监听窗口

        b、获取客户端对象,并获取客户ip

        c、读取客户端输入流数据

        d、写入文件

        e、用客户端输出流反馈信息

        f、关流

2、单线程的服务端有个局限性。当A客户端连接上以后,被服务端获取到。服务端执行具体流程。这时B客户端连接,只能等待。因为服务端还没有处理完A客户端的请求。还没有循环回来执行下一次accept方法。所以,暂时获取不到B客户端对象。

        那么为了可以让多个客户端同时并发访问服务端。服务端最好就是将每个客户端封装到一个单独的线程中,这样,就可以同时处理多个客户端请求。

如何定义线程呢?

        只要明确了每一个客户端要在服务端执行的代码,将该代码存入run方法即可。

代码:

[java] view plaincopy

1. /* 

2. 需求:并发上传图片 

3. */  

4.   

5. import java.io.*;  

6. import java.net.*;  

7. //客户端  

8. class  PicClient  

9. {  

10.     public static void main(String[] args) throws Exception  

11.     {  

12.         //对传入的值进行判断  

13.         if (args.length!=1)  

14.         {  

15.             System.out.println("请指定一个图片文件!");  

16.             return;  

17.         }  

18.   

19.         File file=new File(args[0]);  

20.   

21.         //对文件路径进行判断  

22.         if (!(file.exists()&&file.isFile()))  

23.         {  

24.             System.out.println("你上传的文件有问题,非文件或者不存在!");  

25.             return;  

26.         }  

27.   

28.         //判断是否是图片文件  

29.         if (!file.getName().endsWith(".jpg"))  

30.         {  

31.             System.out.println("图片格式错误,请重新选择!");  

32.             return;  

33.         }  

34.   

35.         //对文件大小进行判断  

36.         if (file.length()>1024*1024*5)  

37.         {  

38.             System.out.println("你上传的文件过大,居心叵测!");  

39.             return;  

40.         }  

41.   

42.         //创建服务  

43.         Socket s=new Socket("localhost",10000);  

44.         //读取图片数据  

45.         FileInputStream fis=new FileInputStream(file);  

46.           

47.         //用Socket服务输出流写入数据  

48.         OutputStream out =s.getOutputStream();  

49.   

50.         BufferedReader in=new BufferedReader(new InputStreamReader(s.getInputStream()));  

51.   

52.         byte[] buf=new byte[1024];  

53.   

54.         int len=0;  

55.   

56.         while ((len=fis.read(buf))!=-1)  

57.         {  

58.             out.write(buf,0,len);  

59.         }  

60.   

61.         //结束标记,表示文件数据已经上传完了  

62.         s.shutdownOutput();  

63.   

64.         String info=in.readLine();//读取返回信息  

65.         System.out.println(info);  

66.   

67.         fis.close();//关流  

68.         s.close();  

69.   

70.     }  

71. }  

72.   

73. //服务端  

74. class PicServer  

75. {  

76.     public static void main(String[] args)throws Exception  

77.     {  

78.         //创建服务,监听端口  

79.         ServerSocket ss=new ServerSocket(10000);  

80.           

81.         while (true)  

82.         {  

83.             //获取客户端对象  

84.             Socket s=ss.accept();  

85.             //客户端执行线程  

86.             new Thread(new PicThread(s)).start();  

87.         }  

88.           

89.         //ss.close();  

90.     }  

91. }  

92.   

93. //利用多线程实现并发上传  

94. class PicThread implements Runnable  

95. {  

96.     private Socket s;  

97.     PicThread(Socket s)  

98.     {  

99.         this.s=s;  

100.     }  

101.     public void run()  

102.     {  

103.         int count=1;  

104.         //获取客户端ip  

105.         String ip=s.getInetAddress().getHostAddress();  

106.         try  

107.         {         

108.             System.out.println(ip+"  connected.....");  

109.   

110.             //通过客户端的读取流读取数据  

111.             InputStream in=s.getInputStream();  

112.             //文件保存路径  

113.             File dir =new File("C:\\Users\\asus\\Desktop");  

114.             //文件名  

115.             File file=new File(dir,ip+".jpg");  

116.             //判断文件是否存在  

117.             while(file.exists())  

118.             {  

119.                 file=new File(dir,ip+"("+(count++)+").jpg");  

120.             }  

121.   

122.             //将数据写入到指定文件中  

123.             FileOutputStream fos=new FileOutputStream(file);  

124.   

125.             byte[] buf=new byte[1024];  

126.             int len=0;  

127.             while ((len=in.read(buf))!=-1)  

128.             {  

129.                 fos.write(buf,0,len);  

130.             }  

131.   

132.             //将收到图片数据的信息返回给客户端  

133.             OutputStream out=s.getOutputStream();  

134.               

135.             out.write("上传成功!".getBytes());  

136.   

137.             fos.close();//关流  

138.             s.close();  

139.         }  

140.         catch (Exception e)  

141.         {  

142.             throw new RuntimeException(ip+"图片上传失败");  

143.         }  

144.     }  

145. }  


二、客户端并发登录

        客户端通过键盘录入用户名,服务端对这个用户名进行校验。

        如果该用户存在,在服务端显示xxx,已登陆;并在客户端显示xxx,欢迎光临。

        如果用户不存在,在服务端显示xxx,尝试登陆;并在客户端显示xxx,该用户不存在。

        最多就登录三次。

代码: 

[java] view plaincopy

1. import java.io.*;  

2. import java.net.*;  

3. //客户端  

4. class  LoginClient  

5. {  

6.     public static void main(String[] args) throws Exception  

7.     {  

8.         //创建服务  

9.         Socket s=new Socket("localhost",10000);  

10.         //键盘录入  

11.         BufferedReader br=new BufferedReader(new InputStreamReader(System.in));  

12.   

13.           

14.         //用Socket服务输出流写入数据  

15.         PrintWriter out =new PrintWriter(s.getOutputStream(),true );  

16.   

17.         //接收服务器返回的信息  

18.         BufferedReader in=new BufferedReader(new InputStreamReader(s.getInputStream()));  

19.   

20.         String line=null;  

21.   

22.         for(int x=0;x<3;x++)  

23.         {  

24.             line=br.readLine();//读取键盘录入  

25.             if (line==null)  

26.             {  

27.                 break;//如果键盘没有输入,则直接结束  

28.             }  

29.   

30.             out.println(line);//将数据写入流中  

31.   

32.             String info=in.readLine();//读取返回信息  

33.   

34.             System.out.println(info);  

35.   

36.             if (info.contains("欢迎"))//---------------  

37.             {  

38.                 break;//如果登录成功,就跳出循环  

39.             }  

40.         }  

41.   

42.         br.close();//关流  

43.         s.close();  

44.     }  

45. }  

46.   

47. //服务端  

48. class LoginServer  

49. {  

50.     public static void main(String [] args)throws Exception  

51.     {  

52.         //创建服务,监听端口  

53.         ServerSocket ss=new ServerSocket(10000);  

54.           

55.         while (true)  

56.         {  

57.             //获取客户端对象  

58.             Socket s=ss.accept();  

59.           

60.             //客户端执行线程  

61.             new Thread(new LoginThread(s)).start();  

62.         }  

63.           

64.         //ss.close();  

65.     }  

66. }  

67.   

68. //利用多线程实现并发登录  

69. class LoginThread implements Runnable  

70. {  

71.     private Socket s;  

72.     LoginThread(Socket s)  

73.     {  

74.         this.s=s;  

75.     }  

76.     public void run()  

77.     {  

78.         //获取客户端ip  

79.         String ip=s.getInetAddress().getHostAddress();  

80.         System.out.println(ip+"  connected.....");  

81.         try  

82.         {         

83.             for (int x=0;x<3 ;x++ )  

84.             {     

85.                 //通过客户端的读取流读取数据  

86.                 BufferedReader in=new BufferedReader(new InputStreamReader(s.getInputStream()));  

87.                   

88.                 //读取数据库中的数据,这里用文件来表示数据库  

89.                 BufferedReader br=new BufferedReader(new FileReader("users.txt"));  

90.   

91.                 String line=in.readLine();//读取客户端数据  

92.                 if (line==null)//--------------  

93.                 {  

94.                     break;//如果客户端没有发送数据,则跳出循环  

95.                 }  

96.                 String data=null;  

97.                 boolean flag=false;//设置标记  

98.                 //读取数据库中的用户数据  

99.                 while ((data=br.readLine())!=null)  

100.                 {  

101.                     if (line.equals(data))  

102.                     {  

103.                         flag=true;//如果用户存在,则将标记设为true  

104.                         break;  

105.                     }  

106.                 }  

107.   

108.                 //将数据写入到指定文件中  

109.                 PrintWriter out=new PrintWriter(s.getOutputStream(),true);  

110.   

111.                 if (flag)  

112.                 {  

113.                     System.out.println(line+",已登陆!");  

114.                       

115.                     out.println(line+",欢迎光临!");  

116.   

117.                     break;//-----------  

118.                 }  

119.                 else  

120.                 {  

121.                     System.out.println(line+",尝试登陆!");  

122.                     out.println(line+",用户名不存在!");  

123.                 }     

124.             }  

125.             s.close();//关流  

126.         }  

127.         catch (Exception e)  

128.         {  

129.             throw new RuntimeException("用户登陆失败");  

130.         }     

131.     }  

132. }  


三、客户端和服务的浏览器演示

        浏览器是一个标准的客户端,它可以对服务端传送过来的数据消息进行解析,把符合应用层协议的消息部分解析后,将头信息拆包掉,传送到应用层,只保留了正确的正文主题部分显示在主体部分上。

        而由于使用java编译是在传输层和网际层处理的,所以,会接受到全部的消息,包含了头消息。而浏览器处于应用层,已将发送来的头消息去除,只留下了主体信息。

示例:

        自定义服务器,用浏览器访问:

[java] view plaincopy

1. import java.io.*;  

2. import java.net.*;  

3.   

4. //服务器  

5. class  ServerDemo  

6. {  

7.     public static void main(String[] args)throws Exception   

8.     {  

9.         //创建服务,监听端口  

10.         ServerSocket ss=new ServerSocket(10000);  

11.         //获取客户端  

12.         Socket s=ss.accept();  

13.         //显示ip  

14.         String ip=s.getInetAddress().getHostAddress();  

15.   

16.         System.out.println(ip);  

17.         //读取客户端读取流数据  

18.         InputStream in=s.getInputStream();  

19.   

20.         byte[] buf=new byte[1024];  

21.         int len=in.read(buf);  

22.         //显示数据  

23.         System.out.println(new String(buf,0,len));  

24.         //返回信息写入客户端输出流  

25.         PrintWriter out=new PrintWriter(s.getOutputStream(),true);///true一定要记得写  

26.   

27.         out.println("<font color='red' size='7'>客户端你好!</font>");  

28.   

29.         s.close();//关流  

30.         ss.close();  

31.     }  

32. }  

 

四、URL和URLConnection

1、URL:

        URI:范围更大,条形码也包含于此范围

        URL:范围较小,即域名

方法:

        1)构造函数:URL(String protocol,String host,int port,String file);//根据指定 protocol、host、port号和 file 创建 URL对象。

        2)String getProtocol();//获取协议名称

        3)String getHost();//获取主机名

        4)int getPort();//获取端口号

        5)String getFile();//获取URL文件名

        6)String getPath();//获取此URL的路径部分

        7)String getQuery();//获取此URL的查询部,客户端传输的特定信息

注:一般输入网址,是不带端口号的,此时可进行获取,通过获取网址返回的port,若port为-1,则分配一个默认的80端口,如

        int port = getPort();

        if(port == -1)

              port = 80;

2、URLConnection

方法:

        1)URLConnection openConnection();//用URL调用此方法,返回一个 URLConnection 对象,它表示到 URL 所引用的远程对象的连接。

        2)InputStream getInputStream();//获取输入流

        3)OutputStream getOutputStream();//获取输出流

示例:

[java] view plaincopy

1. /* 

2. 自定义浏览器,显示网页信息 

3. */  

4.   

5. import java.io.*;  

6. import java.awt.*;  

7. import java.awt.event.*;  

8. import java.net.*;  

9.   

10. class MyIEGUIDemo  

11. {  

12.     //定义所需组件引用  

13.     private Frame f;  

14.     private Button but,bok;  

15.     private TextField tf;  

16.     private TextArea ta;  

17.   

18.     //构造函数  

19.     MyIEGUIDemo()  

20.     {  

21.         init();  

22.     }  

23.   

24.     //窗体基本设置于功能实现  

25.     public void init()  

26.     {  

27.         //组件实例化  

28.         f=new Frame("我的Window");  

29.         but=new Button("跳转");  

30.         tf=new TextField(50);  

31.         ta=new TextArea(25,60);  

32.   

33.         //基本设置  

34.         f.setBounds(300,150,500,500);  

35.         f.setLayout(new FlowLayout());  

36.   

37.         //添加组件  

38.         f.add(tf);  

39.         f.add(but);  

40.         f.add(ta);  

41.   

42.         //窗体事件  

43.         myEvent();  

44.   

45.         //窗体显示  

46.         f.setVisible(true);  

47.     }  

48.   

49.     //注册事件  

50.     public void myEvent()  

51.     {  

52.         //窗体关闭功能  

53.         f.addWindowListener(new WindowAdapter()  

54.         {  

55.             public void windowClosing(WindowEvent e)  

56.             {  

57.                 System.exit(0);  

58.             }  

59.         });  

60.   

61.         //“跳转”按钮事件  

62.         but.addActionListener(new ActionListener()  

63.         {  

64.             public void actionPerformed(ActionEvent e)  

65.             {  

66.                 showFile();//显示网页内容在文本区中  

67.             }  

68.         });  

69.   

70.           

71.   

72.         //文本框键盘事件  

73.         tf.addKeyListener(new KeyAdapter()  

74.         {  

75.             public void keyPressed(KeyEvent e)  

76.             {  

77.                 //如果键盘按下Enter键,就将网页内容显示在文本区中  

78.                 if(e.getKeyCode()==KeyEvent.VK_ENTER)  

79.                     showFile();  

80.             }  

81.         });  

82.     }  

83.   

84.     //显示网页内容  

85.         private void showFile()  

86.         {  

87.             ta.setText("");  

88.             String path=tf.getText();//获取输入的路径  

89.             try  

90.             {  

91.                 //封装地址对象  

92. URL url =new URL(path);  

93. 连接网页服务器  

94.                 URLConnection conn=url.openConnection();  

95.                 //读取流,用于读取服务器返回数据  

96.                 InputStream in=conn.getInputStream();  

97.   

98.                 byte[] buf=new byte[1024*1024];  

99.   

100.                 int len=in.read(buf);  

101.                 //将数据显示在文本区中  

102.                 ta.append(new String(buf,0,len));  

103.             }  

104.             catch (Exception e)  

105.             {  

106.                 throw new RuntimeException("连接"+path+"网站失败");  

107.             }  

108.         }  

109.   

110.     public static void main(String[] args)   

111.     {  

112.         //运行窗体  

113.         new MyIEGUIDemo();  

114.     }  

115. }  

 

小知识点

1、Socket类的构造函数中,有一个空参数的构造函数:

        Socket()//通过系统默认类型的 SocketImpl创建未连接套接字对象

       可以通过connect(SocketAddress endpoint)方法来连接服务器。而SocketAddress是一个抽象类,它的子类InetSocketAddress实现了IP套接字地址(IP地址+端口号)。所以就可以连接到服务器了。

2、ServerSocket对象中的构造函数:

        ServerSocket(int port,int backlog),其中的backlog表示队列的最大长度,即最多连入客户端的个数,即最大连接数。

3、在浏览器输入网址访问一台主机所做的操作:

       如输入http://61.135.169.125,可以直接连接此ip的主机,而我们一般是输入主机名:http:/www.baidu.ocm(百度主机对应的ip地址就是:61.135.169.125),那么此时浏览器做了神马操作呢?

       也就是说如何通过主机名获取IP地址,从而连接到这台主机呢?这就需要将主机名翻译成IP地址,即域名解析:DNS

        在进行访问的时候,会先在本地的hosts文件(c:\windows\system32\drivers\ext\host)中找对应的映射。若有,则直接返回请求;若无,则到公网的映射列表即DNS中找对应的映射,找到后,将主机名对应的IP地址返回给本机,本机通过这个IP地址找到对应的服务器。

示意图:


 host应用:可屏蔽一些恶意网址,即将对应的映射关系写入hosts中,将IP地址改为本机的回环地址,那么会直接找到hosts,就不会将请求发送出去了。

 

--------android培训java培训、java学习型技术博客、期待与您交流!------------ 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值