javase_22(TCP的学习-->*)
TCP传输
Socket(客户端)和ServerSocket(服务端)
1.建立客户端和服务器端
2.建立连接后,通过Socket中的IO流进行数据的传输
3.关闭Socket()当关闭这个流,其实就是把底层的流所关闭
同样,客户端与服务器端程序是两个独立运行的应用程序.
基本思路:(客户端)
客户端需要明确服务器的IP地址和端口,这样才可以试图去建立连接.如果连接失败,会出现异常.
连接成功,说明客户端与服务器端建立了通道,那么通过IO流就可以进行数据的传输,而Socket对象已经提供了输入流和输出流对象,通过
getInputStream(), getOutputStream()获取即可.
与服务端通信结束,关闭Socket.
基本思路:(服务器端)
服务端需要明确它要处理的数据是从那个端口进入的.
当有客户端访问时,要明确是那个客户端,可通过accept()获取已经连接的客户端对象,并且通过该对象与客户端通过IO流进行数据的传输.
当客户端访问结束,关闭该客户端.
生动的比喻:
必须要建立114服务台,客户才可以拨打的道理..当TCP服务器程序运行到ServerSocket.accept方法等待客户的连接,在正常的情况下,accept方法会发生阻塞,一直等到客户连接请求的到来.该方法才会返回.如果没有客户端请求来的情况下,accept方法没有发生阻塞,那么肯定前面的程序是有问题的.,通常端口被其他的程序所占用.
利用循环从客户端里面读取数据,并且在服务端显示出来?不要把问题那么复杂,该多线程就多线程-->该怎么走.比喻用到多线程.-->休息一下.等待它读取数据.要不然.我都还没有读取数据.-->你这边就已经执行了.
客户端
通过Socket建立对象并指定要连接的服务端主机与端口
1 Socket s = new Sockte("127.0.0.0.1",8888); 2 InputStream ips = s.getInputS tream(); 3 OutputStream ops = s.getOutputStream(); 4 S.close();//底层的流必须要关闭/.
服务端:
建立服务端需要监听的一个端口:
1 ServerSocket ss = new ServerSocket(8888); 2 //获取到客户端的Socket . 3 Socket ss = Ss.accept(); 4 InputStream ips = s.getInputStream(); 5 OutputStream ops = s.getOutputStream(); 6 byte [] buf = new byte[1024]; 7 Ips.read(buf); 8 int num = ips.read(buf); 9 String str = new String(buf,0,num); 10 Ss.close(); 11 S.close();
简单的TCP客户端与服务端:
客户端:
1 package com.javami.kudyDemo.Tcp; 2 3 import java.io.IOException; 4 import java.io.InputStream; 5 import java.io.OutputStream; 6 import java.net.Socket; 7 8 public class TcpClient 9 { 10 11 /** 12 * 客户端 13 */ 14 public static void main(String[] args) 15 { 16 Socket socket = null; 17 try 18 { 19 //连接到指定的主机端口号上 20 socket = new Socket("127.0.0.1",8888); 21 22 //-->读取服务端的数据 23 InputStream ips = socket.getInputStream(); 24 //-->发送服务端的数据 25 OutputStream ops = socket.getOutputStream(); 26 ops.write("服务器你最近还好吗?".getBytes());//转换成字节流 27 ops.write("可以同时写两个数据吗??".getBytes());//转换成字节流 28 byte [] buf = new byte[ips.available()]; 29 ips.read(buf); 30 System.out.println(new String(buf)); 31 } 32 catch(IOException e) 33 { 34 35 }finally 36 { 37 try 38 { 39 if(socket!=null) 40 socket.close(); 41 }catch(IOException e) 42 { 43 e.printStackTrace(); 44 } 45 } 46 47 } 48 49 }
服务端:
1 package com.javami.kudyDemo.Tcp; 2 3 import java.io.IOException; 4 import java.io.InputStream; 5 import java.io.OutputStream; 6 import java.net.ServerSocket; 7 import java.net.Socket; 8 9 public class TcpServer 10 { 11 /* 12 * 服务端 13 */ 14 public static void main(String[]args) 15 { 16 ServerSocket ss = null;; 17 Socket socket = null; 18 try 19 { 20 ss = new ServerSocket(8888); 21 socket = ss.accept();//监听端口--->和客户端已经绑定起来 22 InputStream ips = socket.getInputStream(); 23 OutputStream ops = socket.getOutputStream(); 24 byte[] buf = new byte[ips.available()];//个数为多少 25 ips.read(buf); 26 System.out.println(new String(buf)); 27 ops.write("我最近过得还好~你不需要担心我服务器的寿命".getBytes()); 28 }catch(IOException e) 29 { 30 e.printStackTrace(); 31 }finally 32 { 33 try 34 { 35 if(ss!=null) 36 ss.close(); 37 }catch(IOException e) 38 { 39 e.printStackTrace(); 40 } 41 } 42 } 43 }
字母转换大写功能:
客户端:
1 package com.javami.kudyDemo.Tcp; 2 3 import java.io.BufferedReader; 4 import java.io.BufferedWriter; 5 import java.io.IOException; 6 import java.io.InputStream; 7 import java.io.InputStreamReader; 8 import java.io.OutputStream; 9 import java.io.OutputStreamWriter; 10 import java.net.Socket; 11 12 public class MyClient 13 { 14 15 /** 16 *客户端 17 *循环边度边写的概念!!这一步不考虑多线程的问题 18 */ 19 public static void main(String[] args) 20 { 21 Socket socket = null; 22 try 23 { 24 socket = new Socket("127.0.0.1",8888); 25 InputStream ips = socket.getInputStream(); 26 OutputStream ops = socket.getOutputStream(); 27 byte[]buf = new byte[ips.available()]; 28 System.out.println(new String(buf)); 29 System.out.println("请输入内容:"); 30 while(true) 31 { 32 BufferedReader br = 33 new BufferedReader(new InputStreamReader(System.in)); 34 35 String line = br.readLine(); 36 if("quit".equals(line)) 37 break; 38 ops.write((line+"\r\n").getBytes()); 39 byte[] b = new byte[ips.available()]; 40 ips.read(b); 41 System.out.println("转换后的效果:"+new String(b)); 42 } 43 44 }catch(IOException e) 45 { 46 e.printStackTrace(); 47 }finally 48 { 49 if(socket!=null) 50 try 51 { 52 socket.close(); 53 } catch (IOException e) 54 { 55 // TODO Auto-generated catch block 56 e.printStackTrace(); 57 } 58 } 59 } 60 61 }
服务端:
1 package com.javami.kudyDemo.Tcp; 2 3 import java.io.BufferedReader; 4 import java.io.IOException; 5 import java.io.InputStream; 6 import java.io.InputStreamReader; 7 import java.io.OutputStream; 8 import java.net.ServerSocket; 9 import java.net.Socket; 10 11 public class MyServer 12 { 13 /** 14 * 服务端 15 */ 16 public static void main(String[] args) 17 { 18 ServerSocket ss = null; 19 try 20 { 21 //监听端口 22 ss = new ServerSocket(8888); 23 Socket socket = ss.accept();//等待客户端的连接 返回连接后的对象 24 InputStream ips =socket.getInputStream(); 25 OutputStream ops = socket.getOutputStream(); 26 while(true) 27 { 28 BufferedReader br = //把读取到的内容转换成字符流 29 new BufferedReader(new InputStreamReader(ips)); 30 String line = br.readLine(); 31 if("quit".equals(line)) 32 break; 33 String toLine = line.toUpperCase();//转换成大写字母 34 ops.write(toLine.getBytes()); 35 36 } 37 }catch(IOException e) 38 { 39 e.printStackTrace(); 40 }finally 41 { 42 if(ss!=null) 43 try 44 { 45 ss.close(); 46 } catch (IOException e) 47 { 48 // TODO Auto-generated catch block 49 e.printStackTrace(); 50 } 51 } 52 } 53 54 }
一个程序开启了多线程-->Thread-0线程去执行..-->Main线程也在执行的.
必须让main线程等待一下.让其有一个消化的过程.
字母的转换:(使用了多线程解决问题)
客户端类
1 package com.javami.kudyDemo.Tcp; 2 3 import java.io.BufferedReader; 4 import java.io.IOException; 5 import java.io.InputStream; 6 import java.io.InputStreamReader; 7 import java.io.OutputStream; 8 import java.net.Socket; 9 import java.net.UnknownHostException; 10 11 public class ReverseClinet 12 { 13 public static void main(String[]args) throws InterruptedException, UnknownHostException, IOException 14 { 15 Socket socket = null; 16 socket = new Socket("127.0.0.1",8888); 17 InputStream ips = socket.getInputStream(); 18 OutputStream ops = socket.getOutputStream(); 19 //接收欢迎语,必须要先休息一下.因为那边写入数据.如果不休息. 20 //由于是开了线程执行的.main主线程就打印出来.所以打印到你的内容必定是空白的 21 Thread.sleep(10); 22 int len = ips.available(); 23 byte[] buf = new byte[len]; 24 ips.read(buf); 25 System.out.println(new String(buf)); 26 BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); 27 System.out.println("开始吧!!"); 28 while(true) 29 { 30 String line = br.readLine(); 31 ops.write((line+"\r\n").getBytes());//写入进去 32 int l = ips.available(); 33 byte[] b = new byte[l]; 34 Thread.sleep(10); 35 ips.read(b); 36 System.out.println(new String(b)); 37 } 38 } 39 }
服务端:
1 package com.javami.kudyDemo.Tcp; 2 3 import java.io.IOException; 4 import java.net.ServerSocket; 5 import java.net.Socket; 6 7 /* 8 * 服务器提供字符反向服务 9 *多线程的思路: 10 *我等待用户的连接..当有用户连接.我交给别的线程去执行.对象是仍然没有变.-->socket重点. 11 *必须要循环监听.如果不循环监听...当一个服务端和客户端再运行..我就结束了.. 12 */ 13 public class ReverseServer 14 { 15 public static void main(String[]args) 16 { 17 ServerSocket ss = null; 18 try 19 { 20 ss = new ServerSocket(8888); 21 while(true) 22 { 23 Socket socket = ss.accept(); 24 new Thread(new ReverseServers(socket)).start(); 25 } 26 } catch (IOException e) 27 { 28 // TODO Auto-generated catch block 29 e.printStackTrace(); 30 }finally 31 { 32 if(ss!=null) 33 try 34 { 35 ss.close(); 36 } catch (IOException e) 37 { 38 // TODO Auto-generated catch block 39 e.printStackTrace(); 40 } 41 } 42 } 43 }
-->它的哥们
1 package com.javami.kudyDemo.Tcp; 2 3 import java.io.BufferedReader; 4 import java.io.IOException; 5 import java.io.InputStream; 6 import java.io.InputStreamReader; 7 import java.io.OutputStream; 8 import java.net.Socket; 9 10 public class ReverseServers implements Runnable 11 { 12 Socket socket ; 13 public ReverseServers(Socket socket) 14 { 15 this.socket = socket; 16 } 17 public void run() 18 { 19 try 20 { 21 InputStream ips = socket.getInputStream(); 22 OutputStream ops = socket.getOutputStream(); 23 ops.write("启动服务器提供反转功能:".getBytes()); 24 while(true) 25 { 26 BufferedReader br = 27 new BufferedReader(new InputStreamReader(ips));//把读取到的数据转换成字符流 28 String line = br.readLine(); 29 if("quit".equals(line)) 30 break; 31 StringBuilder sb = new StringBuilder(line); 32 String newLine = sb.reverse().toString();//转换 33 ops.write(newLine.getBytes()); 34 } 35 }catch(IOException e) 36 { 37 e.printStackTrace(); 38 }finally 39 { 40 if(socket!=null) 41 try 42 { 43 socket.close(); 44 } catch (IOException e) 45 { 46 // TODO Auto-generated catch block 47 e.printStackTrace(); 48 } 49 } 50 } 51 }
TCP网络程序的工作原理:
TCP客户端程序与TCP服务端程序的交互过程:
1.服务器端程序创建一个ServerSocket,然后调用accpept方法等待客户来连接.
2.客户端程序创建一个Socket并请求服务端建立连接.
3.服务器端接收客户的连接请求,并创建一个新的Socket与该客户建立专线连接.
4.建立了连接的两个Socket在一个单独的线程(由服务端程序创建)上对话.
5.服务器端开始等待i新的连接请求,当新的连接请求到达时,重复上述过程.
文件上传案例分析
一、需求分析
1.多线程的服务器
2.客户端程序
3.上传文件名
4.上传文件
二、实现步骤
1.实现多线程服务器
1)创建ServerSocket对象,监听指定的端口,7888
2)循环调用accept方法,等待客户端的连接,次方法阻塞,返回一个客户端Socket
3)开启新线程,运行一个服务对象,将socket给这个对象
2.实现客户端程序
1)创建Socket对象,连接服务器 127.0.0.1 7888
3.发送和接收欢迎语
服务器端: 获得输入输出流, 发送欢迎语, out.write();
客户端: 获得输入输出流, 接收欢迎语, in.read();
4.发送文件名,创建文件
1)客户端:
读键盘,获得文件路径名 BufferedReader
创建文件对象, File
判断文件是否存在,文件是否为标准文件 isFile
获得文件名:getName
将文件名上传给服务器,out.write
等待接收服务器反馈的信息
2)服务器端:
接收文件名,in.read();
创建文件对象, 在指定目录下创建 createNewFile
判断是否创建成功,文件已存在或创建失败, 将是否可以继续上传的信息发给客户端 out.write
5.上传文件
1)客户端:
如果可以开始上传文件了
BufferedInputStream包装FileInputStream
BufferedOutputStream包装 socket.getOutputStream()
实现流的对拷,逐个字节拷贝,
2)服务器端
接收客户端上传的数据写入新创建的文件中
BufferedInputStream包装socket.getInputStream()
BufferedOutputStream包装 FileOutputStream
实现流的对拷,逐个字节拷贝。
文件的上传功能小项目
客户端:
1 package com.javami.kudyDemo; 2 3 import java.io.BufferedInputStream; 4 import java.io.BufferedOutputStream; 5 import java.io.BufferedReader; 6 import java.io.File; 7 import java.io.FileInputStream; 8 import java.io.IOException; 9 import java.io.InputStream; 10 import java.io.InputStreamReader; 11 import java.io.OutputStream; 12 import java.net.Socket; 13 14 public class UploadClient 15 { 16 17 public static void main(String[] args) 18 { 19 Socket socket = null; 20 try 21 { 22 socket = new Socket("127.0.0.1",8888); 23 InputStream ips = socket.getInputStream(); 24 OutputStream ops = socket.getOutputStream(); 25 //接收欢迎语句 26 Thread.sleep(10);//-->等待线程的写入 27 int len = ips.available(); 28 byte[] buf = new byte[len]; 29 ips.read(buf); 30 System.out.println(new String(buf)); 31 32 //上传文件名 33 BufferedReader br = 34 new BufferedReader(new InputStreamReader(System.in)); 35 System.out.println("请输入你的文件名:"); 36 File file = null; 37 while(true) 38 { 39 String fileName = br.readLine(); 40 if("quit".equals(fileName)) 41 return ; 42 file = new File(fileName); 43 if(file.isFile()) 44 { 45 ops.write(file.getName().getBytes()); 46 break;//如果是文件--写入进去 47 } 48 if(!file.isFile()) 49 System.out.println("该文件不存在,请重新输入"); 50 else 51 System.out.println("只支持标准文件的上传"); 52 } 53 54 //等待服务端的反馈: 55 buf = new byte[1024]; 56 len = ips.read(buf); 57 String info = new String(buf,0,len); 58 System.out.println(info); 59 if(!"可以上传文件".equals(info)) 60 return; 61 long fileSize = file.length(); 62 ops.write(String.valueOf(fileSize).getBytes()); 63 Thread.sleep(100); 64 65 //上传文件 66 upload(file,ops); 67 68 //等待服务端的反馈 69 len = ips.read(buf); 70 System.out.println(new String(buf,0,len)); 71 }catch(Exception e) 72 { 73 e.printStackTrace(); 74 }finally 75 { 76 77 } 78 } 79 80 private static void upload(File file, OutputStream ops) throws IOException 81 { 82 BufferedInputStream bis = 83 new BufferedInputStream(new FileInputStream(file));//读取林的内容 84 BufferedOutputStream bos = new BufferedOutputStream(ops);//写入内容 85 System.out.println("文件上传中~~~~"); 86 int ch ; 87 while((ch=bis.read())!=-1) 88 { 89 bos.write(ch); 90 } 91 bos.flush();//这个刷新一下就可以.因为让底层去关.要不然就会发生冲突 92 bis.close();//这个必须要关 93 } 94 95 }
服务端:
1 package com.javami.kudyDemo; 2 3 import java.io.IOException; 4 import java.net.ServerSocket; 5 import java.net.Socket; 6 7 public class UploadServer 8 { 9 /* 10 * 必须要用到多线程去做.如果你没有使用多线程..那么main线程只能和一个连接打交道.. 11 * while 12 * 13 */ 14 public static void main(String[]args) 15 { 16 ServerSocket ss = null; 17 try 18 { 19 System.out.println("服务器已经开启,正在监听8888端口"); 20 ss = new ServerSocket(8888); 21 while(true)//我while循环没有结束-->在监听啦~~~ 22 { 23 Socket socket = ss.accept();//当每一个线程都进来的时候,这里会发生阻塞 24 System.out.println("成功与一个客户端建立连接"+ 25 socket.getInetAddress().getHostAddress()); 26 new Thread(new UploadServers(socket)).start(); 27 } 28 } catch (IOException e) 29 { 30 // TODO Auto-generated catch block 31 e.printStackTrace(); 32 }finally 33 { 34 if(ss!=null) 35 try 36 { 37 ss.close(); 38 } catch (IOException e) 39 { 40 // TODO Auto-generated catch block 41 e.printStackTrace(); 42 } 43 } 44 } 45 }
客户端的哥们-->
1 package com.javami.kudyDemo; 2 3 import java.io.BufferedInputStream; 4 import java.io.BufferedOutputStream; 5 import java.io.File; 6 import java.io.FileOutputStream; 7 import java.io.IOException; 8 import java.io.InputStream; 9 import java.io.OutputStream; 10 import java.net.Socket; 11 12 public class UploadServers implements Runnable 13 { 14 15 Socket socket ; 16 public UploadServers(Socket socket) 17 { 18 this.socket = socket; 19 } 20 21 @Override 22 public void run() 23 { 24 try 25 { 26 InputStream ips = socket.getInputStream(); 27 OutputStream ops = socket.getOutputStream(); 28 ops.write("连接成功,本站支持文件的上传".getBytes()); 29 30 //接收文件名 31 byte[] buf = new byte[1024]; 32 int len = ips.read(buf); 33 String fileName = new String(buf,0,len); 34 System.out.println("拿到文件名:"+fileName); 35 36 //创建文件 37 File file = new File("f:/a",fileName); 38 if(!file.createNewFile()) 39 { 40 ops.write("该文件已经存在".getBytes()); 41 return; 42 } 43 ops.write("可以上传文件".getBytes()); 44 45 //接收文件的大小 46 len = ips.read(buf); 47 String str = new String(buf,0,len); 48 int fileSize = (int)Integer.parseInt(str); 49 50 //接收客户端的文件 51 saveFile(ips,file,fileSize);//接收的内容.我的文件名 --> 接收过来的长度 52 //接收从客户端发送过来的文件 53 ops.write("文件上传完毕,恭喜-->使用了".getBytes()); 54 } catch (IOException e) 55 { 56 // TODO Auto-generated catch block 57 e.printStackTrace(); 58 } 59 60 } 61 62 private void saveFile(InputStream ips, File file, int fileSize) throws IOException 63 { 64 BufferedInputStream bis = 65 new BufferedInputStream(ips);//读取内容-->会发生阻塞.. 66 BufferedOutputStream bos = 67 new BufferedOutputStream(new FileOutputStream(file)); 68 int ch; 69 System.out.println("文件上传中,请等下------"); 70 for(int i=0; i<fileSize; i++) 71 { 72 ch = bis.read(); 73 bos.write(ch); 74 } 75 bos.close();//不需要把底层的所关闭~~ 76 } 77 78 }