Java 笔记 23:序列化,网络,TCP群聊,UDP

序列化

1、java.io.Serializable接口:

  •  类通过实现 java.io.Serializable 接口以启用其序列化功能。未实现此接口的类将无法使其任何状态序列化或反序列化。
    
  •  可序列化类的所有子类型本身都是可序列化的。序列化接口没有方法或字段,仅用于标识可序列化的语义。
    
  • 如果实现Serializable接口,对象如何序列化,各个属性序列化的顺序是什么,都是默认的,程序员无法指定,也不用关心。
  • 如果属性前面有static和transient修饰,不参与序列化。
  • 2、java.io.Externalizable接口:
  • 若某个要完全控制某一对象及其超类型的流格式和内容,则它要实现 Externalizable 接口的 writeExternal 和 readExternal 方法。
  • 程序员在writeExternal方法中,自定定制哪些属性要序列化,顺序是什么样。
  • 程序员在readExternal方法中,自定定制哪些属性要反序列化,顺序和writeExternal的方法中一致。

打印流:输出流

  • (1)PrintStream:
  •  经典代表:System.out
    
  •  		Sysetm.err
    
  • new PrintStream(文件名)
  • new PrintStream(文件名,编码)
  • new PrintStream(另一个字节输出流)
  • (2)PrintWriter
  •  Web阶段学习时,从服务器端往客户端返回消息时用到response,response.getWriter()可以返回PrintWriter对象。
    
  •  即  Web服务器往客户端(例如:浏览器)返回html网页时,用的是PrintWriter对象的输出方法。
    
	public void test04() throws IOException{
		//所有数据类型写出去,都是按照文本处理
		PrintStream ps = new PrintStream("1.txt");
		ps.println(12);
		ps.println(true);
		ps.println(new Goods("《从入门到放弃》", 99.99, 1000));
		ps.close();
	}

System.in:默认情况下是从键盘输入的数据中扫描

  • Scanner:可以从你指定的文件、流中读取文本数据
public class TestScanner {
	@Test
	public void test05() throws FileNotFoundException{
		Scanner input = new Scanner(new File("d:/1.txt"),"GBK");//InputStream
		
		while(input.hasNextLine()){
			String line = input.nextLine();
			System.out.println(line);
		}
		
		input.close();
	}
	
	@Test
	public void test04() throws FileNotFoundException{
		Scanner input = new Scanner("1.txt");//InputStream
		
		while(input.hasNextLine()){
			String line = input.nextLine();
			System.out.println(line);
		}
		
		input.close();
	}
	
	@Test
	public void test03() throws FileNotFoundException{
		Scanner input = new Scanner(new File("1.txt"));//InputStream
		
		while(input.hasNextLine()){
			String line = input.nextLine();
			System.out.println(line);
		}
		
		input.close();
	}
	
	@Test
	public void test02() throws FileNotFoundException{
		Scanner input = new Scanner(new FileInputStream("1.txt"));//InputStream
		
		while(input.hasNextLine()){
			String line = input.nextLine();
			System.out.println(line);
		}
		
		input.close();
	}
	
	@Test
	public void test01(){
		Scanner input = new Scanner(System.in);
		System.out.print("请输入一个整数:");
		int num = input.nextInt();
		System.out.println("num = " + num);
		input.close();
	}
}

在Java层面是常量对象,但是可以同C等底层语言进行修改

  • System.in:
  • System.out
  • System.err
public class TestSystem {
	@Test
	public void test02() throws FileNotFoundException{
		System.setOut(new PrintStream("1.txt"));
		
		System.out.println("aaaa");
		System.out.println("bbb");
		System.out.println("ccc");
		System.out.println("ddd");
	}
	
	@Test
	public void test01(){
		PrintStream out = System.out;
		System.out.println(out);
	}
}

JDK1.7中新增了一种try…catch处理的方式,

  • 称为try…with…resource,它是为资源关闭专门设计的try…catch的语法
  • try(
  •  需要关闭的资源对象
    
  • ){
  •  可能发生异常的逻辑代码
    
  • }catch(异常类型 e){
  •  异常处理代码
    
  • }catch(异常类型 e){
  •  异常处理代码
    
  • }
  • 凡是在try()中声明的资源对象,都会自动关闭,无论是否发生异常。
public class TestTryWithResource {
	@Test
	public void test03() {
		//从d:/1.txt(GBK)文件中,读取内容,写到项目根目录下1.txt(UTF-8)文件中
		try(
			FileInputStream fis = new FileInputStream("d:/1.txt");
			InputStreamReader isr = new InputStreamReader(fis,"GBK");
			BufferedReader br = new BufferedReader(isr);
			
			FileOutputStream fos = new FileOutputStream("1.txt");
			OutputStreamWriter osw = new OutputStreamWriter(fos,"UTF-8");
			BufferedWriter bw = new BufferedWriter(osw);
		){
			String str;
			while((str = br.readLine()) != null){
				bw.write(str);
				bw.newLine();
			}
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	@Test
	public void test02() {
		//从d:/1.txt(GBK)文件中,读取内容,写到项目根目录下1.txt(UTF-8)文件中
		BufferedReader br = null;
		BufferedWriter bw = null;
		try {
			br = new BufferedReader(new InputStreamReader(new FileInputStream("d:/1.txt"),"GBK"));
			bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream("1.txt"),"UTF-8"));
			
			String str;
			while((str = br.readLine()) != null){
				bw.write(str);
				bw.newLine();
			}
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}finally{
			try {
				if(bw!=null){
					bw.close();
				}
			} catch (IOException e) {
				e.printStackTrace();
			}
			try {
				if(br!=null){
					br.close();
				}
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		
	}
	
	@Test
	public void test01(){
		//从d:/1.txt(GBK)文件中,读取内容,写到项目根目录下1.txt(UTF-8)文件中
		FileInputStream fis = null;
		InputStreamReader isr = null;
		BufferedReader br = null;
		FileOutputStream fos = null;
		OutputStreamWriter osw = null;
		BufferedWriter bw = null;
		try {
			fis = new FileInputStream("d:/1.txt");
			isr = new InputStreamReader(fis,"GBK");
			br = new BufferedReader(isr);
			
			fos = new FileOutputStream("1.txt");
			osw = new OutputStreamWriter(fos,"UTF-8");
			bw = new BufferedWriter(osw);
			
			String str;
			while((str = br.readLine()) != null){
				bw.write(str);
				bw.newLine();
			}
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}finally{
			try {
				if(bw!=null){
					bw.close();
				}
			} catch (IOException e) {
				e.printStackTrace();
			}
			try {
				if(osw!=null){
					osw.close();
				}
			} catch (IOException e) {
				e.printStackTrace();
			}
			try {
				if(fos!=null){
					fos.close();
				}
			} catch (IOException e) {
				e.printStackTrace();
			}
			
			try {
				if(br!=null){
					br.close();
				}
			} catch (IOException e) {
				e.printStackTrace();
			}
			try {
				if(isr!=null){
					isr.close();
				}
			} catch (IOException e) {
				e.printStackTrace();
			}
			try {
				if(fis!=null){
					fis.close();
				}
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		
	}
	
/*	public static void main(String[] args) {
		//从d:/1.txt(GBK)文件中,读取内容,写到项目根目录下1.txt(UTF-8)文件中
		FileInputStream fis = new FileInputStream("d:/1.txt");
		InputStreamReader isr = new InputStreamReader(fis,"GBK");
		BufferedReader br = new BufferedReader(isr);
		
		FileOutputStream fos = new FileOutputStream("1.txt");
		OutputStreamWriter osw = new OutputStreamWriter(fos,"UTF-8");
		BufferedWriter bw = new BufferedWriter(osw);
		
		String str;
		while((str = br.readLine()) != null){
			bw.write(str);
			bw.newLine();
		}
		
		bw.close();
		osw.close();
		fos.close();
		
		br.close();
		isr.close();
		fis.close();
		
	}*/
}

io:

  • 阻塞式IO
  • nio: JDK1.4–>JDK1.7
  • 非阻塞式的IO
  • Path(接口):类似于File,用路径名表示一个目录或文件
  • Paths:工具类,用来创建Path接口的对象
  • Files工具类,操作文件或目录的工具类:
  • (1)Files.copy(src, dest, StandardCopyOption.REPLACE_EXISTING);
  • (2)delete(Path path) 功能类似于File类的delete()
  •  					不同的地方是会报异常,当文件不存在时
    
  • deleteIfExists(Path path) 功能相同于File类的delete() 存在就删除,不存在就什么也不干
  • (3)move(Path source, Path target, CopyOption… options)
  • (4)List readAllLines(Path path, Charset cs)
public void test06() throws IOException{
		Path file = Paths.get("testIO","java","2.txt");
		List<String> allLines = Files.readAllLines(file, Charset.forName("UTF-8"));
		for (String string : allLines) {
			System.out.println(string);
		}
	}
	
	@Test
	public void test05() throws IOException{
		Path src = Paths.get("2.txt");
		Path dest = Paths.get("testIO","java","2.txt");
		Files.move(src, dest, StandardCopyOption.REPLACE_EXISTING);
	}
	
	
	@Test
	public void test04() throws IOException{
		File file = new File("1.txt");
		file.delete();
	}
	
	@Test
	public void test03() throws IOException{
		Path src = Paths.get("1.txt");
		Files.delete(src);
	}
	
	@Test
	public void test02() throws IOException{
		Path src = Paths.get("1.txt");
		Path dest = Paths.get("2.txt");
		Files.copy(src, dest, StandardCopyOption.REPLACE_EXISTING);
	}
	
	@Test
	public void test01(){
		Path file = Paths.get("testIO", "java" ,"1.txt");
		
		int count = file.getNameCount();
		System.out.println(count);
		
		Path name = file.getName(0);
		System.out.println(name);
	}

网络

网路通信的三个要素:

  • (1)地址:
  •  IP地址,定位到一台设备
    
  • (2)端口号
  •  定位到一个应用程序(进程)
    
  • (3)网络协议
  •  对速率、传输代码、代码结构、传输控制步骤、出错控制等制定标准。
    
  •  通俗的讲:保证如何准确的到达对方那里,能够正确的解析出数据出来。
    
  • OSI(Open System Interconnection)开放系统互连参考模型。
  • 它把计算机网络分成物理层、数据链路层、网络层、传输层、会话层、表示层、应用层等七层。
  • 物理层:建立、维护、断开物理连接
  • 数据链路层:建立逻辑连接、进行硬件地址寻址、差错校验等功能。将比特组合成字节进而组合成帧,用MAC地址访问介质,错误发现但不能纠正。
  • 网络层:进行逻辑地址寻址,实现不同网络之间的路径选择。协议有:ICMP、IGMP、IP(IPV4 IPV6)、ARP、RARP。
  • 传输层:定义传输数据的协议端口号,以及流控和差错校验。协议有:TCP、UDP。
  • 会话层:建立、管理、终止会话。对应主机进程,指本地主机与远程主机正在进行的会话
  • 表示层:数据的表示、安全、压缩。格式有:JPEG、ASCll、DECOIC、加密格式等
  • 应用层:网络服务与最终用户的一个接口。协议有:HTTP、FTP、SMTP、DNS、TELNET、HTTPS、POP3等等。
  • 最早确定的,也是最重要的是TCP/IP协议。把这些协议家族称为TCP/IP协议簇。
  • (1)主机-网路层:硬件层面
  • (2)网络层:例如IP寻址
  • (3)传输层:协议有:TCP、UDP。
  • (4)应用层:程序员面对的
  • TCP:(Transmission Control Protocol,传输控制协议)面向连接的,可靠的,基于字节流的,适用于大数据量的传输的协议。
  • UDP:(User Datagram Protocol,用户数据报协议)非面向连接,不可靠的,基于用户数据报(报文),只能支持最多64k以内的数据的发送。
    IP地址:
  • IPV4:32位,4个整数,每一个整数是1个字节(无符号)0~255
  •  例如:192.168.30.142
    
  • IPV6:
  •  128 位无符号数字,8个16进制值
    
  •  例如:1080:0:0:0:8:800:200C:417A  
    
  • 特殊的IP:
  •  127.0.0.1    本地回传IP
    
  •  224.0.0.0至239.255.255.255  广播IP地址
    
  •  ....
    
  • 域名:
  • 用人比较方便记忆和识别的单词来代表IP地址
  • 例如:www.atguigu.com
  •  www.jd.com
    
  •  www.taobao.com
    
  •  www.baidu.com
    
  •  ....
    
  • 域名–》域名解析器–>IP地址
  • 域名:
  • 一级域名:.com商业
  •       .cn 中国
    
  •       .org 组织,机构,非盈利型
    
  •       .gov政府
    
  •       .edu教育
    
  •       ....
    
  • 域名需要注册

端口号:

  • [0,65535]之间无符号 2个字节
  • (1)公认端口(Well-Known Ports)范围从0到1023
  •  http: 80  ftp:21  SMTP:25
    
  • (2)注册端口(Registered Ports):端口号从1024到49151。
  •  例如:Tomcat(8080),JBOSS(8080),Oracle(1521),MySQL(3306),SQL Server(1433),QQ(1080)。
    
  • (3)动态/私有端口(Dynamic and/Private Ports):端口的范围从49152到65535,这些端口号一般不固定分配给某个服务。

java.net.InetAddress:用来包装IP地址对象的

  • InetAddress有两个子类:Inet4Address 、Inet6Address
  • (1)InetAddress.getLocalHost()
  • (2)InetAddress.getByName(“www.baidu.com”)
  • (3)InetAddress.getByAddress(addr)
  • 如果后面的API中,要接收IP的形参类型是InetAddress的话,就可以刚才的方法来创建对象

不管传输层使用的是TCP/UDP协议,Java中用这样的API来表示,Socket(套接字)

  • 分类:
  • (1)流套接字:用于TCP,ServerSocket类和Socket类
  • (2)数据报套接字:用于UDP,DatagramSocket类
  • Socket(也可以叫“套接字”),是两台机器间通信的端点。可以和网卡驱动进行交流。负责把数据交给网卡驱动,或者从网卡驱动中提取数据。

TCP协议编程

/*
 * TCP:
 * 	 客户端,主动连接服务器
 * 
 * Socket(InetAddress address, int port) 
 * Socket(String host, int port)
 * 
 * 步骤:
 * 1、连接服务器
 * Socket socket = new Socket("192.168.30.142",9999);
 * 
 * 2、发送或接受数据
 * 
 * 3、断开连接
 */
public class TestClient {
	public static void main(String[] args) throws UnknownHostException, IOException {
//		1、连接服务器
		Socket socket = new Socket("192.168.30.142",9999);
		
		//2、例如:接受数据
		//字节流,输入流,InputStream
		InputStream in = socket.getInputStream();
		byte[] data = new byte[1024];
		int len;
		while((len = in.read(data)) != -1){
			System.out.println(new String(data,0,len));
		}
		
		//3、断开
		socket.close();
	}
}

/*
 * TCP:面向连接,可靠的,基于字节流的
 * 		服务器:等待被连接的过程
 * 
 * ServerSocket:只负责接受和建立连接,不负责数据的传输
 * Socket:负责数据的传输
 * 
 * 步骤:
 * 1、开启服务器
 * 	  指定服务器监听的端口号
 * 2、等待客户端并接受客户端的连接
 * 
 * 3、接受/发送数据
 * 发送方:输出流
 * 接受方:输入流
 * 
 * 4、断开连接
 * 
 * 5、关闭服务器
 */
public class TestServer {
	public static void main(String[] args) throws IOException {
		//1、开启服务器:网卡驱动就监听9999端口号的数据
		ServerSocket server = new ServerSocket(9999);
		
		//2、等待客户端并接受客户端的连接
		Socket socket = server.accept();//这句代码执行一次,就接受一个客户端连接
		System.out.println("一个客户端连接成功!");
		//3、例如:发送数据
		//发送:欢迎你登录
		//字节流,输出流  OutputStream
		//(1)获取输出流
		OutputStream out = socket.getOutputStream();
		//(2)发送数据
		out.write("欢迎你登录".getBytes());
		
		//4、断开连接
		socket.close();
		
		//5、关闭服务器
		server.close();
	}
}

服务器端:

  • (1)接收客户端的连接
  • (2)接收客户端的词语
  • (3)把词语“反转”返回给客户端
  • (2)(3)多次,直到客户端发送"bye"为止
  • 加一个条件,服务器端可以同时接收n个客户端连接
  • 服务器端得加多线程
public class TestServer {
	public static void main(String[] args) throws IOException {
		//1、开启服务器
		ServerSocket server = new ServerSocket(8989);
		
		boolean flag = true;
		while(flag){
			//2、接收一个客户端的连接
			Socket socket = server.accept();//每个客户端的socket是独立的
			
			//为没一个客户端开启一个独立的线程维护它的通信
			MessageHandler mh = new MessageHandler(socket);
			mh.start();
		}
			
		//5、关闭服务器
		server.close();
	}
}
class MessageHandler extends Thread{
	private Socket socket;
	
	public MessageHandler(Socket socket) {
		super();
		this.socket = socket;
	}

	public void run(){
		try {
			//3、先获取输入流和输出流
			InputStream in = socket.getInputStream();
			/*
			 * 因为是接收一个词语,反转一个,返回一个
			 * 那么如果仅仅使用字节流,不好区分词语
			 * 需要用到字符流
			 * 那么就意味着需要把字节流转为字符流
			 */
			InputStreamReader isr = new InputStreamReader(in);//这里不涉及编码问题,仅仅为了转换流的类型
			/*
			 * 字符流中几个字符是一个词语
			 * 那么我们这里选择“换行符”来作为词语的分割
			 * 意味着我们可以按行读取Scanner或BufferedReader
			 */
			BufferedReader br = new BufferedReader(isr);
			
			OutputStream out = socket.getOutputStream();
			/*
			 * 客户端收到字节,同样不方便处理几个字节是一个词语,仍然要把字节输出流转为字符流
			 * 而且字符之间也不好区分,那么也选择“换行符”进行区别词语
			 * 我们现在需要把OutputStream转为一个可以按行写的字符流或其他的处理流
			 * 
			 * 可以按行写的:BufferedWriter(newLine())
			 * 		   PrintStream(println())
			 */
			PrintStream ps = new PrintStream(out);
			
			//从客户端接收词语
			String word;
			while((word = br.readLine()) != null){
				if("bye".equals(word)){
					break;
				}
				
				//如果不是bye,要反转,并且返回
				StringBuilder sb = new StringBuilder(word);
				sb.reverse();
				
				//返回给客户端
				ps.println(sb.toString());
			}
			
			
		} catch (IOException e) {
			e.printStackTrace();
		}finally{
			try {
				//4、断开
				socket.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}
}

客户端:

  • (1)从键盘输入词语
  • (2)发送给服务器
  • (3)接收服务器返回的结果
  • (1)(2)(3)多次进行,直到键盘输入bye并发送给发服务器之后就结束
  • 加一个条件,服务器端可以同时接收n个客户端连接
  • 客户端代码不用修改
public class TestClient {
	public static void main(String[] args) throws UnknownHostException, IOException {
		//1、连接服务器
		Socket socket = new Socket("192.168.30.142",8989);
		
		/*
		 *  * (1)从键盘输入词语
		 * (2)发送给服务器
		 * (3)接收服务器返回的结果
		 * (1)(2)(3)多次进行,直到键盘输入bye并发送给发服务器之后就结束
		 */
		Scanner input = new Scanner(System.in);
		/*
		 * 同样考虑到发送词语,以及词语之间分割问题,那我们选择PrintStream和BufferedReader
		 */
		PrintStream ps = new PrintStream(socket.getOutputStream());
		BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
		while(true){
			//从键盘输入词语
			System.out.print("请输入词语:");
			String word = input.next();
			
			//发送给服务器端
			ps.println(word);
			
			if("bye".equals(word)){
				break;
			}
			
			//接收服务器返回的结果
			String result = br.readLine();
			System.out.println("服务器返回的反转后的结果:" + result);
		}
		
		input.close();
		socket.close();
	}
}

从客户端发送文件到服务器端

  • (1)接收客户端的连接
  • (2)接收文件名.后缀名
  • 思考:
  • 存哪里 ①在当前项目中找一个位置存储,例如:upload文件夹
  • 如何解决文件名重名的问题 文件名需要处理,加入时间戳或其他的唯一编码的UUID等值
  • .后缀名需要保留,因为它代表文件的类型
  • (3)接收文件内容
  • (4)反馈结果
  • 思考:
  •  这里既要接收文件名.后缀名,又要接收文件内容。
    
  •  这里既有  文本信息“文件名.后缀名”,又有其他类型的数据“文件内容”,只能选择字节流。
    
  •  如何区别,文件名.后缀名   与  文件内容呢
    
  •  想哪种字节输入流,可以处理字符串,和字节类型的数据。
    
  •  FileInputStream
    
  •  BufferedInputStream
    
  •  DataInputStream
    
  •  ObjectInputStream
    
  •  这些里面:DataInputStream:readUTF() 和  read(byte[])
    
  •  		ObjectInputStream也可以,但是麻烦,我这里选择DataInputStream
    
public class TestServer {
	public static void main(String[] args) throws IOException {
		//1、开启服务器
		ServerSocket server = new ServerSocket(9999);
		
		//2、接收客户端的连接
		Socket socket = server.accept();
		
		//3、获取输入流
		InputStream in = socket.getInputStream();
		DataInputStream dis = new DataInputStream(in);
		
		//接收文件名.后缀名
		String fileName = dis.readUTF();
		
		//处理文件名
		/*
		 * 希望我要在服务器存储的文件名:   原来的文件名 + 时间戳
		 */
		long timestamp = System.currentTimeMillis();
		//.的下标
		int index = fileName.lastIndexOf(".");
		//后缀名
		String ext = fileName.substring(index);
		// 原来的文件名
		String name = fileName.substring(0, index);
		//新文件名
		String newFileName = "upload/" + name + timestamp + ext;
		
		//创建文件输出流,把接收到的文件内容,写入新文件
		FileOutputStream fos = new FileOutputStream(newFileName);
		
		//接收文件内容
		byte[] data = new byte[1024];
		int len;
		while((len = dis.read(data))!=-1){
			fos.write(data, 0, len);
		}
		
		//还可以给客户端反馈:文件接收完毕
		OutputStream out = socket.getOutputStream();
		PrintStream ps = new PrintStream(out);
		ps.println("文件接收完毕!");
		
		//断开
		fos.close();
		socket.close();
		server.close();
	}
}

从客户端发送文件到服务器端
*
*

  • 客户端:
  • (1)从键盘输入文件的路径名,即选择要发送的文件
  • (2)给服务器先把“文件名.后缀名"
  • (3)发送文件内容
  • (4)接收服务器的反馈结果
  • 这里同样因为既要发送“文件名.后缀名",又要发送“文件内容”,选择字节流,选择DataOutputStream
public class TestClient {
	public static void main(String[] args) throws UnknownHostException, IOException {
		//1、连接服务器
		Socket socket = new Socket("192.168.30.142",9999);
		
		//2、从键盘输入文件的路径名,即选择要发送的文件
		Scanner input = new Scanner(System.in);
		System.out.print("请选择你要发送的文件(例如:D:/尚硅谷_190513班_柴林燕_JavaSE/开学典礼所发资料.rar):");
		String fileName = input.nextLine();
		File file = new File(fileName);
		
		//3、给服务器发送“文件名.后缀名"
		OutputStream out = socket.getOutputStream();
		DataOutputStream dos = new DataOutputStream(out);
		
		//发送“文件名.后缀名"
		dos.writeUTF(file.getName());
		
		//4、发送文件内容
		//先从file文件读取内容
		FileInputStream fis = new FileInputStream(file);
		byte[] data = new byte[1024];
		int len;
		while((len = fis.read(data)) != -1){
			//一边读,一边给服务器发送
			dos.write(data,0,len);
		}
		socket.shutdownOutput();//表示发送完毕了
		
		//5、接收反馈
		InputStream in = socket.getInputStream();
		InputStreamReader isr = new InputStreamReader(in);
		BufferedReader br = new BufferedReader(isr);
		String result = br.readLine();
		System.out.println("结果:" + result);
		
		//6、关闭
		socket.close();
		fis.close();
	}
}

群聊实现

public class TestServer {
	private static ArrayList<Socket> online = new ArrayList<Socket>();
	
	public static void main(String[] args) throws IOException {
		//1、开启服务器
		ServerSocket server = new ServerSocket(9999);
		
		while(true){
			//2、接收客户端的连接
			Socket socket = server.accept();
			
			//把这个客户端加入到online中
			online.add(socket);
			
			//每一个客户端独立的线程
			MessageHandler mh = new MessageHandler(socket);
			mh.start();
		}
	}

	//私有的静态的内部类
	//这里用内部类的原因,是为了用上面的online集合
	private static class MessageHandler extends Thread{
		private Socket socket;
		private String ip;
		
		public MessageHandler(Socket socket) {
			super();
			this.socket = socket;
			this.ip = socket.getInetAddress().getHostAddress();
		}

		public void run(){
			//这个客户端的一连接成功,线程一启动,就可以告诉其他人我上线了
			sendToOthers(ip+"上线了");
			
			/*
			 * (1)接收当前的客户端发送的消息
			 * (2)给其他在线的客户端转发
			 */
			//(1)接收当前的客户端发送的消息
			try {
				InputStream in = socket.getInputStream();
				InputStreamReader isr = new InputStreamReader(in);
				BufferedReader br = new BufferedReader(isr);
				
				String content;
				while((content = br.readLine()) !=null){
					if("bye".equals(content)){
						//给自己发一句bye
						OutputStream out = socket.getOutputStream();
						PrintStream ps = new PrintStream(out);
						ps.println("bye");
						
						break;
					}
					
					//收到一句,转发一句
					sendToOthers(ip+"说:" + content);
				}
				
				sendToOthers(ip+"下线了");
			} catch (IOException e) {
				sendToOthers(ip+"掉线了");
			}
		}
		
		//因为转发的代码也很长,独立为一个方法
		public void sendToOthers(String str){
			//遍历所有online的客户端
			Iterator<Socket> iterator = online.iterator();
			while(iterator.hasNext()){
				Socket on = iterator.next();
				if(!on.equals(socket)){//只给其他客户端转发
					try {
						OutputStream out = on.getOutputStream();
						PrintStream ps = new PrintStream(out);
						
						ps.println(str);
					} catch (IOException e) {
						//说明on这个客户端要么下线了,要么掉线了
						iterator.remove();
					}
				}
			}
		}
	}
	
	
}
public class TestClient {
	public static void main(String[] args) throws UnknownHostException, IOException {
		//1、连接服务器
		Socket socket = new Socket("192.168.30.142",9999);
		
		//2、开启两个线程,一个收消息,一个发消息
		SendThread st = new SendThread(socket);
		ReceiveThread rt = new ReceiveThread(socket);
		
		st.start();
		rt.start();
		
		//等发送线程停下来再往下走
		try {
			st.join();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		
		//让接收数据的线程停下
		rt.setFlag(false);
		
		//等接收线程停下来,再往下走,断开连接
		try {
			rt.join();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		
		socket.close();
	}
}
class SendThread extends Thread{
	private Socket socket;
	
	public SendThread(Socket socket) {
		super();
		this.socket = socket;
	}

	public void run(){
		try {
			//键盘输入
			Scanner input = new Scanner(System.in);
			OutputStream out = socket.getOutputStream();
			PrintStream ps = new PrintStream(out);
			while(true){
				//从键盘输入
				System.out.print("请输入要发送的消息:");
				String content = input.nextLine();
				System.out.println("content:" + content);
				
				//给服务器发送
				ps.println(content);
				
				//如果bye,就结束发送
				if("bye".equals(content)){
					break;
				}
			}
			input.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}
class ReceiveThread extends Thread{
	private Socket socket;
	private boolean flag = true;
	
	public ReceiveThread(Socket socket) {
		super();
		this.socket = socket;
	}
	
	public void run(){
		try {
			InputStream in = socket.getInputStream();
			InputStreamReader isr = new InputStreamReader(in);
			BufferedReader br = new BufferedReader(isr);
			
			while(flag){
				String line = br.readLine();
				System.out.println(line);
				if("bye".equals(line)){
					break;
				}
			}
			
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	public void setFlag(boolean flag) {
		this.flag = flag;
	}
	
}

UDP

public class TestReceive {
	public static void main(String[] args) throws IOException {
		//1、接收方也要socket
		//接收方的端口号,指定,IP自动获取
		DatagramSocket ds = new DatagramSocket(9999);
		
		//2、准备一个数据报,接收数据
		byte[] data = new byte[1024];
		DatagramPacket dp = new DatagramPacket(data,data.length);
		
		//3、接收数据
		ds.receive(dp);
		
		//4、把数据拆解出来
		byte[] bs = dp.getData();//接收的数据
		int len = dp.getLength();//接收的数据的实际长度
		System.out.println(new String(bs,0,len));
		
		//5、断开
		ds.close();
	}
}

public class TestSend {
	public static void main(String[] args) throws IOException {
		//1、发送方,建立一个Socket
		//发送方的端口号和IP地址,自动获取
		DatagramSocket ds = new DatagramSocket();
		
		//2、准备把要发送的数据打包
		String str = "马上下课了";
		byte[] bytes = str.getBytes();
		InetAddress ip = InetAddress.getByName("192.168.30.142");
		DatagramPacket dp = new DatagramPacket(bytes,0,bytes.length, ip, 9999);
		
		//3、发送,通过socket发送
		ds.send(dp);
		System.out.println("发送完毕");
		
		//4、关闭
		ds.close();
		
	}
}

猜你喜欢

转载自blog.csdn.net/qq_40473204/article/details/107713260