Java-day10-NIO和Socket

  • NIO

  •    介绍:从JDK1.4开始,Java提供了一些改进输入/输出处理的新功能,这些新功能被统称为新IO(New IO 简称NIO),新增了许多用于处理输入/输出的类,这些类都被放入java.nio包以及子包中,并对原java.io中的很多类都以NIO为基础进行改进,新增了满足NIO的功能
  •   Java NIO核心部分组成

          Channels : 通道  Buffer : 缓冲区   Selectors : 选择器

          Channels和Buffer是新IO中的两个核心对象,Channel是对传统的输入/输出系统的模拟,在新IO系统中所有的数据都需要通过通道传输。

          Channels和传统的InputStream,OutputStream最大的区别在于它提供了一个map()方法,通过该map()方法可以直接将"一块数据"映射到内存中,如果说传统的输入/输出系统是面向流处理,则新IO则是面向块的处理

          Buffer可以被理解为一个容器(缓冲区,数组),发送到Channel中的所有对象都必须首先放到Buffer中,而从Channel中读取的数据也必须放到Buffer中,也就是说数据可以从Channel读取到Buffer中,也可以从Buffer写到Channel中

  •  使用Buffer

          Buffer就像一个数组,它可以保存多个类型相同的数据,Buffer是一个抽象类其最常用的类是ByteBuffer,它可以在底层字节数组上进行get/set操作,除了ByteBuffer之外,对应于其他基本数据类型(boolean除外)都有相应的Buffer类

  •  NIO与IO的区别

        1.IO面向流,NIO面向缓冲区
             Java IO面向流意味着每次从流中读一个或多个字节,直至读取所有字节,它们没有被缓存在任何地方。此外,它不能前后移动流中的数据。如果需要前后移动从流中读取的数据,需要先将它缓存到一个缓冲区。Java NIO的缓冲导向方法略有不同。数据读取到一个它稍后处理的缓冲区,需要时可在缓冲区中前后移动。这就增加了处理过程中的灵活性
        2.IO是阻塞式的,NIO有非阻塞式的
             Java IO的各种流是阻塞的。这意味着,当一个线程调用read() 或 write()时,该线程被阻塞,直到有一些数据被读取,或数据完全写入。该线程在此期间不能再干任何事情了。Java NIO的非阻塞模式,使一个线程从某通道发送请求读取数据,但是它仅能得到目前可用的数据,如果目前没有数据可用时,就什么都不会获取,而不是保持线程阻塞,所以直至数据变的可以读取之前,该线程可以继续做其他的事情。 非阻塞写也是如此。
        3.IO没有选择器,NIO有选择器
            Java NIO的选择器允许一个单独的线程来监视多个输入通道,你可以注册多个通道使用一个选择器,然后使用一个单独的线程来“选择”通道:这些通道里已经有可以处理的输入,或者选择已准备写入的通道。这种选择机制,使得一个单独的线程很容易来管理多个通道

  •  使用Buffer读写数据一般遵循以下四个步骤:

         1.写入数据到Buffer

         2.调用filp()方法

         3.从Buffer中读取数据

         4.调用clear()方法或者compact()方法

             当向buffer写入数据时,buffer会记录下写了多少数据。一旦要读取数据,需要通过flip()方法将Buffer从写模式切换到读模式。在读模式下,可以读取之前写入到buffer的所有数据。一旦读完了所有的数据,就需要清空缓冲区,让它可以再次被写入。有两种方式能清空缓冲区:调用clear()或compact()方法。clear()方法会清空整个缓冲区。compact()方法只会清除已经读过的数据。任何未读的数据都被移到缓冲区的起始处,新写入的数据将放到缓冲区未读数据的后面。

  •  代码
package com.qf.NIO;

import java.nio.ByteBuffer;

public class Demo1 {
	public static void main(String[] args) {
		
		//获取缓冲区
		ByteBuffer buffer = ByteBuffer.allocate(1024);
		//向缓冲区添加数据
		buffer.put("hello".getBytes());
		//转换成读模式
		buffer.flip();
		//读取一个字节
//		byte b = buffer.get();
//		System.out.println((char)b);
		//读取多个字节
		//这里必须先将读取单个字符注释掉,因为执行一次get后相当于指针已经指向了下标1,
		//所以再继续读取buffer.limit个字符后越界.报错BufferUnderflowException
		byte[] arr = new byte[buffer.limit()];
		buffer.get(arr);
		System.out.println(new String(arr));
		System.out.println("底层:"+new String(buffer.array()));
	}
}
  •  Buffer的capacity,position和limit

        缓冲区本质上是一块可以写入数据,然后可以从中读取数据的内存。这块内存被包装成NIO Buffer对象,并提供了一组方法,用来方便的访问该块内存。
        为了理解Buffer的工作原理,需要熟悉它的三个属性:capacity(容量) position(容量开始的位置) limit(容量结束的位置) position和limit的含义取决于Buffer处在读模式还是写模式。不管Buffer处在什么模式,capacity的含义总是一样的。

        写模式 :limit和capacity相等,position为写入的位置,初始为0

        读模式 :capacity不变,position变为0,limit变为原来position所在的位置,相当于把buffer中没有数据的空间封印起来,从而避免读取Buffer数据的时候读到null值

  • 代码
package com.qf.NIO;

import java.nio.ByteBuffer;

public class Demo2 {
	public static void main(String[] args) {
		/*
		 * 在创建buffer对象的时候传递的参数就是capacity
		 * 容量为1024的缓冲区
		 * 此时buffer的limit和capacity都为1024
		 * 此时的position是0
		 */
		ByteBuffer buffer = ByteBuffer.allocate(1024);//开辟容量1024字节
		System.out.println("position:"+buffer.position());//0
		System.out.println("limit:"+buffer.limit());//1024
		
		/*
		 * position是5,说明写入了5个字节,position指向的是当前内容的结尾,方便接着往下写
		 */
		buffer.put("hello".getBytes());
		System.out.println(buffer.position());//5
		System.out.println(buffer.limit());//1024
		
		//可以继续写
		buffer.put("world".getBytes());
		System.out.println(buffer.position());//10
		
		//切换为读模式
		/*这一步很重要 flip可以理解为模式切换 之前的代码实现的是写入操作
		 *当调用这个方法后就变成读取操作,那么position和limit的值就要发生变换
		 *此时capacity为1024不变
		 *此时limit就移动到原来position所在的位置,相当于把buffer中没有数据的空间
		 *"封印起来"从而避免读取Buffer数据的时候读到null值
		 *相当于 limit = position  limit = 10
		 *此时position的值相当于 position = 0
		 * 
		 */
		
		buffer.flip();
		System.out.println("3:"+buffer.position());//0
		System.out.println("3:"+buffer.limit());//10
//		//获取单个字节
//		//buffer.get();
//		//获取多个字节
		byte[] data=new byte[buffer.limit()];
		buffer.get(data);
		System.out.println("data:"+new String(data));
		System.out.println("读取data后:"+buffer.position());//从0变成了10,position相当于读字节的指针,内容读完了指到了结尾
		System.out.println("读取data后:"+buffer.limit());
		
		//将position设回0,所以你可以重读Buffer中的所有数据。limit保持不变,仍然表示能从Buffer中读取多少个元素
//		buffer.rewind();
//		byte[] data1=new byte[buffer.limit()];
//		buffer.get(data1);
//		System.out.println(new String(data1));
//		System.out.println(buffer.position());//从0变成了10,position相当于读字节的指针,内容读完了指到了结尾
//		System.out.println(buffer.limit());
		
		/*
		 * clear():
		 * API中的意思是清空缓冲区
		 * 而是将缓冲区中limit和position恢复到初始状态
		 * 即limit和capacity都为1024 position是0
		 * 此时可以完成写入模式
		 */
		buffer.clear();
		System.out.println("clear后:"+buffer.position());
		System.out.println("clear后:"+buffer.limit());
		
		//可以继续写
		buffer.put("temp".getBytes());
		//继续读
		buffer.flip();
		byte[] arr = new byte[buffer.limit()];
		buffer.get(arr);
		System.out.println("temp:"+new String(arr));
	}
}
  •  使用Channel

      Channel类似于传统的流对象,但与传统的流对象有两个主要的区别:
        1.Channel可以直接将我指定文件的部分或全部直接映射成Buffer
        2.程序不能直接访问Channel中的数据,包括读取,写入都不行.Channel只能与Buffer进行交互
      Java为Channel接口提供了FileChannel,DategramChannel,ServerSocketChannel,SocketChannel等
        现在以FileChannl来使用,在使用FileChannel之前,必须先打开它。但是,我们无法直接打开一个FileChannel,需要通过使用一个InputStream、OutputStream的getChannel()方法来返回对应的Channel

  •  代码
package com.qf.NIO;

import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;

public class Demo3 {
	public static void main(String[] args) throws IOException {
		/*
		 * 写入文本文件
		 */
		//创建文件写出流
		FileOutputStream fileOutputStream = new FileOutputStream("temp1.txt");
		//获取通道
		FileChannel fileChannel = fileOutputStream.getChannel();
		//创建缓冲流
		ByteBuffer buffer = ByteBuffer.allocate(1024);
		//向缓冲流中放入数据
		buffer.put("helloworld".getBytes());
		//转换模式为读取内容
		buffer.flip();
		//利用通道写入
		fileChannel.write(buffer);
		//关闭资源
		fileChannel.close();
		fileOutputStream.close();
		System.out.println("写入完毕");
	}
}
  •  利用Channel和Buffer实现文件读取的代码演示
package com.qf.NIO;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.util.Arrays;

public class Demo4 {
	public static void main(String[] args) throws IOException {
		/*
		 * 文件读取
		 */
		//创建文件输入流
		FileInputStream fileInputStream = new FileInputStream("temp1.txt");
		//获取通道
		FileChannel fileChannel = fileInputStream.getChannel();
		//创建缓冲流对象
		ByteBuffer buffer = ByteBuffer.allocate(1024);
		//利用通道读取内容
		int num = fileChannel.read(buffer);
		System.out.println(num);
		//转换模式为读取模式
		buffer.flip();
		//利用缓冲流读取数据到控制台
		System.out.println(new String(buffer.array(),0,num));
		byte[] bs = new byte[buffer.limit()];
		buffer.get(bs);
		System.out.println(Arrays.toString(bs));
		//关闭资源
		fileChannel.close();
		fileInputStream.close();
		System.out.println("读完了");
	}
}
  •  使用Paths实现文件的复制
package com.qf.NIO;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.file.OpenOption;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;

public class Demo5 {
	public static void main(String[] args) throws IOException {
		/*
		 * 文件的复制
		 * //打开或创建一个文件,返回一个文件通道来访问该文件
           //StandardOpenOption是枚举里面显示的是文件打开方式
		 */
		//创建通道
		FileChannel inChannel = FileChannel.open(Paths.get("temp1.txt"), StandardOpenOption.READ);
		FileChannel outChannel = FileChannel.open(Paths.get("temp2.txt"), StandardOpenOption.WRITE,StandardOpenOption.CREATE);
		//创建缓冲流
		ByteBuffer buffer = ByteBuffer.allocate(1024);
		int num = 0;
		//复制
		while ((num = inChannel.read(buffer)) != -1) {
			buffer.flip();
			outChannel.write(buffer);
			buffer.clear();//让指针重新回到0
		}
		
		inChannel.close();
		outChannel.close();
		System.out.println("复制完毕");
	}
}
  •  内存映射读写文件

       使用内存映射缓冲区

       只读:任何试图修改导致缓冲区会造成ReadOnlyBufferException。(MapMode.READ_ONLY)
       读/写:对导致缓冲区的变化最终会传递到文件;他们可能会或可能不会有映射相同文件的其他程序可见。 (MapMode.READ_WRITE)
       私人:对导致缓冲区的更改不会传播到文件不会有映射同一文件的其他程序可见;相反,他们会导致缓冲区的修改部分 私有副本被创建。(MapMode.PRIVATE)

  •  代码
package com.qf.NIO;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.FileChannel.MapMode;

public class Demo6 {
    public static void main(String[] args) throws IOException {
		/*
		 * 内存映射读写文件
		 */
    	//创建通道
    	FileChannel inChannel = new FileInputStream("temp1.txt").getChannel();
    	FileChannel outChannel = new FileOutputStream("temp3.txt").getChannel();
    	
    	//这里注意第一个参数是操作的模式是只读   第二个参数是读取文件的起始位置   第三个参数是终止位置
    	//如果文件的大小超过2G,一般建议建立多个映射
    	MappedByteBuffer mappedByteBuffer = inChannel.map(MapMode.READ_ONLY, 0, inChannel.size());
    	outChannel.write(mappedByteBuffer);
    	
    	inChannel.close();
    	outChannel.close();
    	System.out.println("读写完毕");
    	System.out.println(inChannel.getClass().toString());
    	
	}
}
  •  NIO2

         Java7对原有NIO进行了重大改进,改进主要包括如下两方面内容

         1.提供了全面的文件IO和文件系统访问支持

         2.基于异步Channel的IO    Path、Paths和Files

     之前学习中学习过File类来访问文件系统,但是File类的功能比较有限,NIO.2为了弥补这种不足, 引入了一个Path接口还提供了Files和Paths两个工具类,其中Files分装了包含大量静态方法来操作文件, Paths则包含了返回Path的静态工厂方法

  •  代码的演示
package com.qf.NIO;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;

public class Demo7 {
    public static void main(String[] args) throws IOException {
    	 //Path就是用来替代File
	    //构建Path对象的两种方式
	    //传一个路径
	    Path path1 = Paths.get("D:\\123");
	    //第一个参数是盘符 ,第二个参数是可变参数 下面有多少文件路径就写多少
	    Path path2 = Paths.get("D:\\", "123","456.txt");
	    //Path是结合Files工具类使用的
	    //创建目录
	    Files.createDirectories(path1);
	    //判断文件是否存在
	    if(!Files.exists(path2)) {
	    	//创建文件
	    	Files.createFile(path2);
	    }
	    //复制文件
	    //第一个参数原文件路径, 第二个参数目标文件路径
//	  Files.copy(new FileInputStream(new File("D:\\123\\456.txt")), Paths.get("D:\\", "123","222.txt"));
//	   Files.copy(path2, new FileOutputStream(new File("D:\\123\\789.txt")));
//	   Files.copy(path2, Paths.get("D:\\", "123","111.txt")); 
	   //一次读取文件中所有的行
	    List<String> readAllLines = Files.readAllLines(Paths.get("src/com/qiangfeng/test/Demo1.java"));
        for (String str : readAllLines) {
			System.out.println("haha:"+str);
		}
        //将集合中的内容写入到文件中
        Files.write(Paths.get("D:\\", "123","Demo.java"), readAllLines);
        /*
        static BufferedReader newBufferedReader(Path path) 
		打开一个文件进行读取,返回一个 BufferedReader以有效方式从文件中读取文本。  
		static BufferedReader newBufferedReader(Path path, Charset cs) 
		打开一个文件进行读取,返回一个 BufferedReader可以用来有效方式从文件中读取文本。  
		static BufferedWriter newBufferedWriter(Path path, Charset cs, OpenOption... options) 
		打开或创建一个文件写入,返回一个 BufferedWriter,可以有效的方式将文件写入文本。  
		static BufferedWriter newBufferedWriter(Path path, OpenOption... options) 
		打开或创建一个文件写入,返回一个 BufferedWriter能够以有效的方式的文件写入文本。  
 		
 		直接创建缓冲流对象 可以执行 字符集 和访问权限
         */
	}
}
  •  使用FileVisitor便利文件和目录

         在以前的Java版本中,如果程序要遍历指定目录的所有文件和子目录,则只能使用递归进行遍历, 但这种方法不仅复杂,而且灵活性也不高,有了Files工具类的帮助,现在可以使用更优雅的方式来遍历和子目录

  • 代码演示
package com.qf.NIO;

import java.io.IOException;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;

public class Demo8 {
	public static void main(String[] args) throws IOException {
		 /*
		    * FileVisitor参数代表的是一个文件访问器,walkFileTree()方法会自动遍历
		    * start路径下的所有文件和子目录,遍历文件和子目录都会触发FileVisitor中相应
		    * 的方法
		    * FileVisitResult postVisitDirectory(T dir, IOException exc) 
			     访问子目录之后会触发这个方法 
			  FileVisitResult preVisitDirectory(T dir, BasicFileAttributes attrs) 
			     访问子目录之前会触发这个方法  
			  FileVisitResult visitFile(T file, BasicFileAttributes attrs) 
			     访问file文件时触发该方法  
			  FileVisitResult visitFileFailed(T file, IOException exc) 
	                          访问file文件失败时触发该方法  
	                          
	                          返回FileVisitResult是一个枚举
	           CONTINUE 代表继续访问的后续行为
			   SKIP_SIBLINGS 代表继续访问的后续行为,但不访问该文件后目录的兄弟文件或目录  
			   SKIP_SUBTREE 代表继续访问的后续行为,但不访问该文件或目录的子目录树  
	           TERMINATE 代表终止访问的后续行为  
	                           实际开发中没有必要4个方法都要重写,可以通过FileVisitor的子类SimpleFileVisitor(适配器)来
	                           创建自己的文件访问器,选择性的重写方法     
		    */
		    //遍历文件
		   Files.walkFileTree(Paths.get("D:\\workspace\\BigDataNIO"), new SimpleFileVisitor<Path>() {
			   //访问文件时触发该方法
			   @Override
			public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
				  System.out.println("正在访问"+file+"文件");
//				  if(file.endsWith("MergeSort.java")) {
//					  System.out.println("--找到了文件--");
//					  return FileVisitResult.TERMINATE;
//				  }
				  return FileVisitResult.CONTINUE;
			}
			   //开始访问目录时触发的方法		
			   @Override
			public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
				   System.out.println("正在访问"+dir+"路径");
				   return FileVisitResult.CONTINUE;
			}
		   });
		   
		   
		}
	}
  •  网络通信

          网络通信 :三大要素 :IP,端口号,协议

          1.IP : 可以在网络中唯一的标记的一台主机  127.0.0.1(本地地址/本机地址/保留地址)   java中将IP面向对象了---InetAddress

          2.端口 : 用来区分一台主机上的多个服务器(不可以重复)  取值范围(0---65535)  注意点 :在通信时两边的端口号要一致 

          3.网络协议 : 相当于指定的一个统一的标准

  •  七层协议

          应用层, 表示层, 会话层,传输层, 网络层,数据链路层,物理层

  •  InetAddress  java将IP面向对象了形成的类

        方法的演示:

        //获取本机的网络信息
		InetAddress inetAddress = InetAddress.getLocalHost();
		System.out.println(inetAddress.getHostName());
		System.out.println(inetAddress.getHostAddress());
		
		//获取网络上的任意一台主机的信息
		InetAddress inetAddress2 = InetAddress.getByName("www.baidu.com");
		System.out.println(inetAddress2.getHostName());//www.baidu.com
		System.out.println(inetAddress2.getHostAddress());//182.61.200.7
  •  URL :统一资源定位器    URI :同一资源定位符

        URL的功能:可以通过他的方法直接获取网址的各个部分,还可以访问网络的资源

        url的方法的演示:

public static void main(String[] args) throws IOException {
		URL url = new URL("https://www.bingbing.com:333/a/b?name=bing&age=18");
		System.out.println(url.getProtocol());//协议
		System.out.println(url.getPath());//资源路径
		System.out.println(url.getHost());//主机名/域名/IP地址
		System.out.println(url.getQuery());//查询条件
		System.out.println(url.getPort());//端口
		System.out.println(url.getDefaultPort());//默认端口    443
		//System.out.println(url.getContent());//查询的内容
		
		URL url2 = new URL("https://www.baidu.com");
		//System.out.println(url2.getContent());
		//URLConnection代表客户端与服务器端的通信通道,这个类的实例可以用于读取或者写入url引入的资源
		URLConnection connection = url2.openConnection();
		
		//获取内部的字节输入流
		InputStream inputStream = connection.getInputStream();
		//进行读写
		byte[] arr = new byte[1024];
	     inputStream.read(arr);
	     System.out.println(new String(arr,"utf8"));
	}
  •  Socket通信:

        客户端 : (app/浏览器)    服务端 !=主机

        端口号:同一台主机上的每一个服务器都拥有自己的端口号,取值范围(0,65535)

        注意点:1.要保证客户端和服务器端的端口号一致   2.要保证同一台主机上的不同服务器端口号不同

  • TCP与UDP的区别

                  TCP                                                 UDP 

     1.是建立在连接的基础上                建立在非连接的基础上

     2.安全性更高                                  安全性低

     3.传输速度低                                  速度高

     4.适合传输数据量大的数据            数据量小的数据

  •  模拟UDP通信

        客户端

          1.创建UDP通信的对象-socket对象:对应的类是DatagramSocket.(用于UDP数据的发送与接收)
          2.数据的准备-封装包:DatagramPacket(数据包,包括相关的属性,数据)
          3.发送数据,通过send方法
          4.关闭socket对象

          代码:

public static void main(String[] args) throws IOException {
		 //* 1.创建UDP通信的对象-socket对象:对应的类是DatagramSocket.(用于UDP数据的发送与接收)
		DatagramSocket socket = new DatagramSocket();
//		 * 2.数据的准备-封装包:DatagramPacket(数据包,包括相关的属性,数据)
		 /* 打包
		 * 参数一:要传输的数据
		 * 参数二:数据的长度
		 * 参数三:服务器所在主机的地址
		 * 参数四:服务器的端口
		 */
		BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
		String data = null;
		while ((data = bufferedReader.readLine()) != null) {
			DatagramPacket packet = new DatagramPacket(data.getBytes(), data.length(), InetAddress.getLocalHost(), 20000);
			 //* 3.发送数据,通过send方法
			socket.send(packet);
			
			//当输入over的时候结束
			if (data.equals("over")) {
				break;
			}
		}
		
		 //* 4.关闭socket对象
		socket.close();
	}

       服务端:

          服务器端:接收数据
             1.创建socket对象,并绑定端口号---DatagramSocket
             2.创建包对象,创建空数组,准备接收传来的数据
             3.接收数据
             4.关闭相关的对象

          代码:

public static void main(String[] args) throws IOException {
		System.out.println("启动服务器成功,可以随时接收数据");
		 // * 1.创建socket对象,并绑定端口号---DatagramSocket
		DatagramSocket socket  = new DatagramSocket(20000);
		//不断的接收数据
		while (true) {
			 //* 2.创建包对象,创建空数组,准备接收传来的数据
			byte[] buf = new byte[1024];
			DatagramPacket packet = new DatagramPacket(buf, buf.length);
			 //* 3.接收数据:当服务器运行起来,让他一直处于监听状态的方法
			socket.receive(packet);
			//显示所有数据
			byte[] bytes = packet.getData();
			String data = new String(bytes);
			System.out.println(new String(bytes));
			//当输入over的时候结束
			if (data.trim().equals("over")) {
				break;
			}
		}

		 //* 4.关闭相关的对象
		//socket.close();
	}
  •  模拟TCP通信

       在客户端与服务器端通信的时候,对于客户端既要进行输入又要进行输出,所以在Socket对象的内部就内置了输入流和输出流,当进行数据传输的时候,将数据放入socket对象的内部,将socket对象传到服务器端,相当于在客户端与服务器端建立了一个通道,两端使用同一个socket对象.

        客户端:

public static void main(String[] args) throws IOException {
		
		//1.创建Socket对象并绑定端口,管理服务器主机
		Socket socket = new Socket(InetAddress.getLocalHost(), 30000);
		//2.准备数据
		String data = "BigData1923,你好";
		//3.调用Socket内部的输出流向网络写数据
		OutputStream outputStream = socket.getOutputStream();
		outputStream.write(data.getBytes());
		
		//3.使用socket的输入流将服务器回传的信息打印
		InputStream inputStream = socket.getInputStream();
		//将内容打印到控制台
		byte[] arr = new byte[100];
		int num = inputStream.read(arr);
		String data1 = new String(arr,0,num);
		System.out.println(data1);
		
		socket.close();
	}

         服务端:

public static void main(String[] args) throws IOException {
		//1.创建ServerSocket对象并设置端口
		ServerSocket serverSocket = new ServerSocket(30000);
		//2.接收从客户端传来的socket对象
		Socket socket = serverSocket.accept();
		//3.使用socket的输入流卸货
		InputStream inputStream = socket.getInputStream();
		//将内容打印到控制台
		byte[] arr = new byte[100];
		int num = inputStream.read(arr);
		String data = new String(arr,0,num);
		System.out.println(data);
		
		//给客户端回发一个信息,告知接收成功
		//获取socket的输出流
		OutputStream outputStream = socket.getOutputStream();
		outputStream.write("你好,BigData1923".getBytes());
		
		//4.关闭资源
		serverSocket.close();
	}
  •  TCP通信案例(实现大小写转换的服务器)

        这里最好都不要使用缓冲流等进行封装,就使用原始的流.否则在进行读写的时候,不能正确的在客户端与服务器端之间响应,如果使用了缓冲流等,要使用打印流的println,printf等方法,并且打印流要使用new PrintWriter(OutputStream o,boolean b),并且b=true

       客户端:

public static void main(String[] args) throws UnknownHostException, IOException {
		//1.创建Socket对象并绑定服务器的地址和端口
		Socket socket = new Socket(InetAddress.getLocalHost(), 22200);
		
		//2.准备数据---实现让用户从控制台输入
		BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
	
		//3.获取socket内部的输出流
		OutputStream outputStream = socket.getOutputStream();
		PrintWriter printWriter = new PrintWriter(outputStream,true);//当第二个参数为true的时候,会自动刷新,
																	//只有调用println/printf方法是起作用.
		//4.获取socket内部的输入流,将socket的数据输入到内存中
        InputStream inputStream = socket.getInputStream();
        BufferedReader bufferedReader2 = new BufferedReader(new InputStreamReader(inputStream));
        
		String data = null;
		while ((data = bufferedReader.readLine()) != null) {
			printWriter.println(data);

			//当输入over的时候,代表结束
			if ("over".equals(data)) {
				break;
			}
			
			//接收从服务器传回的数据
			//将内容写到控制台
			String data2 = bufferedReader2.readLine();
			System.out.println("data1:"+data2);
		}
		
		//5.关闭资源
		bufferedReader.close();
		bufferedReader2.close();
		printWriter.close();
		socket.close();
	}

         服务端:

public static void main(String[] args) throws IOException {
		//1.创建ServerSocket对象并绑定接口
		ServerSocket serverSocket  = new ServerSocket(22200);
		
		//2.接收套接字的连接,保证客户端与服务器端使用同一个socket对象---一直处于监听的状态
		Socket socket =  serverSocket.accept();
		
		//3.获取输入流
		BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
		//获取输出流
		PrintWriter printWriter = new PrintWriter(socket.getOutputStream(),true);
		
		
		//读写过程
		String data = null;
		while ((data = bufferedReader.readLine()) != null) {
			//打印到控制台
			System.out.println(data.toUpperCase());
			
			//实现将服务器的数据写回客户端
			//这里必须使用println,不能用print
			printWriter.println(data.toUpperCase());
			
			System.out.println("haha");
			
			//当输入over的时候,代表结束
			if ("over".equals(data)) {
				break;
			}
		}
		
		//5.关闭资源
		bufferedReader.close();
		printWriter.close();
		serverSocket.close();
	}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值