------- <a href="http://www.itheima.com" target="blank">android培训</a>、<a href="http://www.itheima.com" target="blank">java培训</a>、期待与您交流! ----------
一、用TCP客户端并发上传图片
1、一对一(单线程)上传的思路:
客户端
a、服务端点。
b、读取客户端已有的图片数据
c、通过Socket输出流将数据发给服务端
d、读取服务端反馈信息。
e、关闭
服务端
a、服务端服务,并监听窗口
b、获取客户端对象,并获取客户ip
c、读取客户端输入流数据
d、写入文件
e、用客户端输出流反馈信息
f、关流
2、单线程的服务端有个局限性。当A客户端连接上以后,被服务端获取到。服务端执行具体流程。这时B客户端连接,只能等待。因为服务端还没有处理完A客户端的请求。还没有循环回来执行下一次accept方法。所以,暂时获取不到B客户端对象。
那么为了可以让多个客户端同时并发访问服务端。服务端最好就是将每个客户端封装到一个单独的线程中,这样,就可以同时处理多个客户端请求。
如何定义线程呢?
只要明确了每一个客户端要在服务端执行的代码,将该代码存入run方法即可。
代码:
- <span style="font-size:14px;">/*
- 需求:并发上传图片
- */
- import java.io.*;
- import java.net.*;
- class PicSocket
- {
- public static void main(String[] args) throws Exception
- {
- if(args.length!=1){
- System.out.println("请选择单个文件上传");
- return;
- }
- //创建socket服务
- Socket s = new Socket("127.0.0.1",1005);
- //从控制台接收一个文件路径
- File file = new File(args[0]);
- if(!(file.exists()&&file.isFile)){
- System.out.println("您上传的文件可能不存在");
- return;
- }
- if(file.length=>1024*1024*5){
- System.out.println("您上传到文件不能超过5M");
- return;
- }
- if(!(file.getName().endsWith(".jpg"))){
- System.out.println("您只能上传.jpg文件');
- }
- //创建本地读取流,关联文件
- BufferedInputStream bufi =
- new BufferedInputStream(new FileInputStream(file));
- //定义目的,将数据写入socket流,并传输到客户端
- BufferedOutputStream bufo =
- new BufferedOutputStream(s.getOutputStream());
- //定义读取服务端反馈信息流
- BufferedReader bufr =
- new BufferedReader(new InputStreamReader(s.getInputStream()));
- //开始读取数据
- int len = 0;
- while((len=bufi.read())!=-1){
- bufo.write(len);
- }
- s.shutdownOutput();
- System.out.println(bufr.readLine());
- s.close();
- bufi.close();
- }
- }
- class ServerThread implements Runnable
- {
- //ServetSocket服务对象
- private Socket s;
- public ServerThread(Socket s){
- this.s = s;
- }
- public void run(){
- int count =1;
- try
- {
- //获取客户端对象
- String ip = s.getInetAddress().getHostAddress();
- File file = new File("d:\\pic");
- if(!file.exists())
- file.mkdir();
- File dir = new File(file,ip+"("+(count)+")"+".jpg");
- //创建读取流,从socket流中读取数据
- BufferedInputStream bufr =
- new BufferedInputStream(s.getInputStream());
- //判断文件是否存在,存在更改文件名
- while(dir.exists()){
- dir = new File(file,ip+"("+(count++)+")"+".jpg");
- }
- //创建输出流对象,将客户端传输的数据保存到本地硬盘
- BufferedOutputStream bufo =
- new BufferedOutputStream(new FileOutputStream(dir));
- int len = 0;
- while((len=bufr.read())!=-1){
- bufo.write(len);
- }
- //创建反馈信息流
- PrintWriter print =
- new PrintWriter(s.getOutputStream(),true);
- print.println("上传成功!!!");
- bufo.close();
- }
- catch (Exception e)
- {
- }
- }
- }
- class PicServer
- {
- public static void main(String [] args) throws Exception
- {
- ServerSocket ss = new ServerSocket(1005);
- while(true){
- Socket s = ss.accept();
- new Thread(new ServerThread(s)).start();
- }
- }
- }
- </span>
客户端通过键盘录入用户名,服务端对这个用户名进行校验。
如果该用户存在,在服务端显示xxx,已登陆;并在客户端显示xxx,欢迎光临。
如果用户不存在,在服务端显示xxx,尝试登陆;并在客户端显示xxx,该用户不存在。
最多就登录三次。
- /*
- 需求:客户端并发登录
- 客户端通过键盘录入用户名,服务端对这个用户名进行校验。
- 如果该用户存在,在服务端显示xxx,已登陆;并在客户端显示xxx,欢迎光临。
- 如果用户不存在,在服务端显示xxx,尝试登陆;并在客户端显示xxx,该用户不存在。
- 最多就登录三次。
- */
- import java.io.*;
- import java.net.*;
- class UserSocket
- {
- public static void main(String[] args) throws Exception
- {
- //创建Socket服务
- Socket s = new Socket("127.0.0.1",1006);
- //创建读取流,从键盘读取录入数据
- BufferedReader bufr =
- new BufferedReader(new InputStreamReader(System.in));
- //定义目的,将数据写入socket流,并传输到服务端
- PrintWriter print =
- new PrintWriter(new BufferedWriter(
- new OutputStreamWriter(s.getOutputStream())),true);
- //创建读取服务端反馈信息流
- BufferedReader readSer =
- new BufferedReader(new InputStreamReader(s.getInputStream()));
- String data = null;
- //控制用户输入次数
- for(int i=0;i<3;i++){
- data = bufr.readLine();
- print.println(data);
- //打印服务端反馈信息
- String str = readSer.readLine();
- System.out.println(str);
- if(str.contains("欢迎"))
- break;
- }
- bufr.close();
- s.close();
- }
- }
- class User implements Runnable
- {
- private Socket s;
- public User(Socket s){
- this.s = s;
- }
- public void run(){
- try
- {
- System.out.println("IP"+(s.getInetAddress().getHostAddress()));
- System.out.println("22222222222");
- //创建读取流,从socket流中读取客户端传输的数据
- BufferedReader bufr =
- new BufferedReader(new InputStreamReader(s.getInputStream()));
- //创建读取流,从本地硬盘读取用户信息
- BufferedReader bufUser =
- new BufferedReader(new FileReader("D:\\userInfo.txt"));
- //定义打印流,输出服务端反馈信息
- PrintWriter print =
- new PrintWriter(s.getOutputStream(),true);
- //这个for循环很重要,没有它,数据只能传输一次
- /*这里一定要放到循环中来,因为这是一个线程,如果没有循环,服务端接收完
- 一次客户端传输的数据后,该线程就结束了,和之前的控制台转换大写是一个道理
- */
- for(int i=0;i<3;i++){
- String name = bufr.readLine();
- //定义标记,用于判断用户是否存在
- boolean flag = false;
- String line = null;
- while((line=bufUser.readLine())!=null){
- if(line.equals(name)){
- flag = true;
- break;
- }
- }
- if(flag==true){
- print.println(name+"欢迎光临!");
- System.out.println(name+"已登陆");
- break;
- }
- else{
- print.println(name+"该用户不存在");
- System.out.println(name+"尝试登陆");
- }
- }
- }
- catch (Exception e)
- {
- }
- }
- }
- class UserServer
- {
- public static void main(String [] args)throws Exception
- {
- ServerSocket ss = new ServerSocket(1006);
- while(true){
- Socket s = ss.accept();
- System.out.println("1111111111111");
- new Thread(new User(s)).start();
- }
- }
- }
浏览器是一个标准的客户端,它可以对服务端传送过来的数据消息进行解析,把符合应用层协议的消息部分解析后,将头信息拆包掉,传送到应用层,只保留了正确的正文主题部分显示在主体部分上。
而由于使用java编译是在传输层和网际层处理的,所以,会接受到全部的消息,包含了头消息。而浏览器处于应用层,已将发送来的头消息去除,只留下了主体信息。
示例:
自定义服务器,用浏览器访问:
- <span style="font-size:14px;">/*
- 客户端为浏览器
- 向浏览器反馈信息
- 注意:使用浏览器作为客户端访问服务器时,客户端会给服务端发送“消息头”
- 只需要通过socket服务的getInputStream()方法即可获取:
- 如:在客户端输入http://192.168.1.254:11000/myweb/demo.html HTTP/1.1
- 则客户端向服务端发送的消息头为:
- GET /myweb/demo.html HTTP/1.1 //这是客户端在告诉服务端要访问的资源路径
- Accept: application/x-shockwave-flash, image/gif, image/x-xbitmap, image/jpeg, i
- mage/pjpeg, application/vnd.ms-excel, application/vnd.ms-powerpoint, application
- /msword, application/QVOD, application/QVOD,//这是在告诉服务端客户端支持的类型
- Accept-Language: zh-cn //这是在告诉服务端客户端支持的语言
- Accept-Encoding: gzip, deflate //这是在告诉服务端客户端支持的压缩格式
- User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 2.0
- .50727)
- Host: 192.168.1.254:11000 //这是告诉服务端客户端要访问的192.168.1.254主机的11000端口号
- Connection: Keep-Alive //这是告诉服务端要保持连接
- */
- import java.net.*;
- import java.io.*;
- class ServerToIE
- {
- //在客户端输入http://127.0.0.1访问
- public static void main(String[] args) throws Exception
- {
- //创建socket服务
- ServerSocket ss = new ServerSocket(10086);
- //获取客户端对象
- Socket s = ss.accept();
- //获取客户端发送的数据(包含消息头)
- InputStream in = s.getInputStream();
- byte [] data = new byte[1024];
- int len = in.read(data);
- System.out.println(new String(data,0,len));
- /*服务端反馈信息(包含应答消息头),不在浏览器上显示,
- 因为浏览器是应用层,被解析了,所以我们在浏览器上只能看到“哥们,收到!!”*/
- PrintWriter print = new PrintWriter(s.getOutputStream(),true);
- print.println("哥们,收到!!!");
- s.close();
- }
- }
- //向服务端z(Tomcat服务器)发送自定义消息头,并打印应答消息头
- class SocketToServer
- {
- public static void main(String [] args)throws Exception
- {
- //创建socket服务
- Socket s = new Socket("127.0.0.1",10086);
- //定义目的,将数据写入Socket流,并传输给服务端
- PrintWriter print = new PrintWriter(s.getOutputStream(),true);
- print.println("GET / HTTP/1.1");
- /*这里我们是直接访问10086端口,要想指定资源路径,
- 可以使用Tomcat服务器,把资源放到Tomcat的webapps目录下即可*/
- print.println("Accept: */*"); // */*代表所支持的类型及格式 如图片,视频及其格式
- print.println("Accept-Language: zh-cn"); //支持的语言
- print.println("Host: 127.0.0.1:10086 ");//要访问的主机的端口
- print.println("Connection: closed"); //这里一般我们自己测试是使用closed
- //注意:这里一定要输出空行
- print.println();
- print.println();
- //获取服务端反馈信息(包含应答消息头)
- BufferedReader bufr =
- new BufferedReader(new InputStreamReader(s.getInputStream()));
- String line = null;
- while((line=bufr.readLine())!=null){
- System.out.println(line);
- }
- s.close();
- }
- }
- </span>
四、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();//获取输出流
示例:
- <span style="font-size:14px;"> /*
- 自定义浏览器,显示网页信息
- */
- import java.io.*;
- import java.awt.*;
- import java.awt.event.*;
- import java.net.*;
- class MyIEGUIDemo
- {
- //定义所需组件引用
- private Frame f;
- private Button but,bok;
- private TextField tf;
- private TextArea ta;
- //构造函数
- MyIEGUIDemo()
- {
- init();
- }
- //窗体基本设置于功能实现
- public void init()
- {
- //组件实例化
- f=new Frame("我的Window");
- but=new Button("跳转");
- tf=new TextField(50);
- ta=new TextArea(25,60);
- //基本设置
- f.setBounds(300,150,500,500);
- f.setLayout(new FlowLayout());
- //添加组件
- f.add(tf);
- f.add(but);
- f.add(ta);
- //窗体事件
- myEvent();
- //窗体显示
- f.setVisible(true);
- }
- //注册事件
- public void myEvent()
- {
- //窗体关闭功能
- f.addWindowListener(new WindowAdapter()
- {
- public void windowClosing(WindowEvent e)
- {
- System.exit(0);
- }
- });
- //“跳转”按钮事件
- but.addActionListener(new ActionListener()
- {
- public void actionPerformed(ActionEvent e)
- {
- showFile();//显示网页内容在文本区中
- }
- });
- //文本框键盘事件
- tf.addKeyListener(new KeyAdapter()
- {
- public void keyPressed(KeyEvent e)
- {
- //如果键盘按下Enter键,就将网页内容显示在文本区中
- if(e.getKeyCode()==KeyEvent.VK_ENTER)
- showFile();
- }
- });
- }
- //显示网页内容
- private void showFile()
- {
- ta.setText("");
- String path=tf.getText();//获取输入的路径
- try
- {
- //封装地址对象
- URL url =new URL(path);
- //连接网页服务器,URLConnection是一个抽象类,里面包含getInputStream方法和getOutputStream方法
- URLConnection conn=url.openConnection();
- //读取流,用于读取服务器返回数据
- InputStream in=conn.getInputStream();
- byte[] buf=new byte[1024*1024];
- int len=in.read(buf);
- //将数据显示在文本区中
- ta.append(new String(buf,0,len));
- }
- catch (Exception e)
- {
- throw new RuntimeException("连接"+path+"网站失败");
- }
- }
- public static void main(String[] args)
- {
- //运行窗体
- new MyIEGUIDemo();
- }
- } </span>