JAVA学习第六十二课 — TCP协议练习

通过练习掌握TCP在进行传输过程中的问题

练习1:创建一个英文大写转换服务器

客户端输入字母数据,发送给服务端,服务端收到后显示到控制台,并将该数据转成大写返回客户端,知道客户端输入over,转换结束

public class Main {

	public static void main(String[] args) throws IOException{
		Text_Transform_Client();
		Text_Transform_Server();
	}

	public static void Text_Transform_Server() throws IOException {
		//文本转服务端

		/* 转换服务端
		 * 1.创建ServerSocket服务端对象
		 * 2.获取Socket对象
		 * 3.源:Socket,读取客户端发过来需要转换的数据
		 * 4.汇:显示在控制台
		 * 5.将数据转成大写返回客户端
		 */
		//创建服务端对象
		ServerSocket ss = new ServerSocket(6534);

		//获取socket对象
		Socket socket = ss.accept();

		//获取ip,明确是谁连进来的
		String ip = socket.getInetAddress().getHostAddress();
		System.out.println("ip : "+ip);

		//获取socket读取流,并装饰
		BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
		//获取socket输出流,并装饰
		PrintWriter pw = new PrintWriter(socket.getOutputStream(),true);
		                      //new PrintWriter(socket.Outputtream())
		String line = null;
		while((line = br.readLine())!=null){
			System.out.println(line);
			pw.println(line.toUpperCase());//pw.print(line.tpUpperCase+"\r\n");
		}                                      //pw.flush();
		socket.close();
		ss.close();
	}

	public static void Text_Transform_Client() throws IOException{
		//文本转换客户端
		/*
		 * 转换客户端:
		 * 1.创建Socket客户端对象
		 * 2.获取键盘录入
		 * 3.将录入的信息,发送给Socket输出流
		 */
		Socket socket = new Socket("127.0.0.1",6534);
		BufferedReader br =
				new BufferedReader(new InputStreamReader(System.in));

		//源是:键盘,汇:Socket输出流
		//new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
		PrintWriter pw = new PrintWriter(socket.getOutputStream(),true);
		                                  //new PrintWriter(socket.getOutstream());
		//Socket输入流,读取服务端返回的大写数据
		BufferedReader br2 = new BufferedReader(new InputStreamReader(socket.getInputStream()));

		String line = null;
		while((line = br.readLine())!=null){
			if("over".equals(line))break;

			pw.println(line);//pw.print(line+"\r\n")
			                 //pw.flush();
			//读取服务端返回的大写信息
			String str = br2.readLine();
			System.out.println("up : "+str);
		}
		socket.close();
	}
}

常见问题:

一、上述代码有一个问题,就是客户端输入over后客户端结束,服务端有没有结束?

    结束,readline()方法是阻塞式方法,但是在客户端输入over后,客户端的socket关闭返回一个-1,服务端的readline()方法中的read方法读取-1,所以readline()方法就读取到null,所以会结束。

二、如果在客户端和服务端的PrintWriter pw = new PrintWriter(socket.getOutputStream())的自动刷新去掉,pw.print()的自动换行去掉,会发生什么?

   客户端没有收到转换后的数据,服务端没有显示数据

因为在客户端,pw.print()写入的数据,都写到了PrintWriter中,并没有刷新到socket输入流中

PS:这就是TCP在传输过程中出现两端都在等待的情况,很可能是数据没有发出去,最大的可能就是有阻塞式方法。

当然,可以在pw.print();下加pw.flush(),但是问题依旧,因为readline读取结束的标记是换行,所以在客户端的pw.print(+"\r\n"),所以要想 解决问题,就要在客户端和服务端都加上刷新动作,和换行符。

一旦遇到上述问题,一般都是因为阻塞式方法造成的服务端、客户端都在等待的情况,所以按照上述代码示例所写,比较好

练习2:上传文本文件

public class Main {

	public static void main(String[] args)throws IOException{
		UpText_Client();
		UpText_Server();
	}

	public static void UpText_Server() throws IOException {

		ServerSocket ss = new ServerSocket(6534);
		Socket socket = ss.accept();
		BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));

		BufferedWriter bw = new BufferedWriter(new FileWriter("c:\\server.txt"));

		String line = null;
		while((line = br.readLine())!=null){
			//if("over".equals(line))break;//*
			bw.write(line);
			bw.newLine();//*
			bw.flush();//*
		}

		PrintWriter pw = new PrintWriter(socket.getOutputStream(),true);
		pw.println("上传成功");

		br.close();
		bw.close();
		socket.close();
		ss.close();
	}

	public static void UpText_Client() throws IOException {

		Socket socket = new Socket("127.0.0.1",6534);
		BufferedReader br = new BufferedReader(new FileReader("c:\\data.txt"));
		PrintWriter out = new PrintWriter(socket.getOutputStream(),true);
		String line = null;
		while((line = br.readLine())!=null){
			out.println(line);
		}
		//out.println("over");//*,开发的时候一般都是应用时间戳,做结束标记,先发给服务器一下时间戳,输入结束后,再发一次
		//socket里有方法
		socket.shutdownOutput();//告诉服务端数据写完了

		//读取socket流
		BufferedReader brin = new BufferedReader(new InputStreamReader(socket.getInputStream()));
		String string = brin.readLine();
		System.out.println(string);
		br.close();
		socket.close();
	}
}

*号处要注意,漏写容易造成,等待清理,客户端输入完毕后,服务端还在等待,不知道客户端已经输入完毕,阻塞,等待

演示的时候,分为两个主函数演示

练习3:上传图片

上传图片到客户端

public static void main(String[] args)throws IOException{
		UpText_Client();
	}
	public static void UpText_Client() throws IOException {

		//创建客户端
		Socket socket = new Socket("127.0.0.1",6534);

		//读取客户端要上传的图片文件
		FileInputStream fis = new FileInputStream("c:\\1.jpg");

		//获取socket输出流,将得到的图片数据发给服务端
		OutputStream out = socket.getOutputStream();
		byte[] buf = new byte[1024];
		int len = 0;
		while((len = fis.read(buf))!=-1){
			out.write(buf, 0, len);
		}

		//告诉服务端,客户端数据发送完毕,使其读取结束
		socket.shutdownOutput();

		InputStream in = socket.getInputStream();
		byte[] buf2 = new byte[1024];
		int len2 = in.read(buf2);
		String result = new String(buf2,0,len2);
		System.out.println(result);
		socket.close();
		fis.close();

	}

上传图片到服务端

public static void main(String[] args)throws IOException {
		UpText_Server();
	}
	public static void UpText_Server() throws IOException {
		//创建服务端
		ServerSocket ss = new ServerSocket(6534);

		//获取客户端
		Socket socket = ss.accept();
		//读取客户端发来的数据
		InputStream in = socket.getInputStream();

		String ip = socket.getInetAddress().getHostAddress();
		System.out.println("IP : "+ip+"....connect");
		//将读取的数据存储到文件中
		File dir = new File("c:\\CopyPic111111111");
		if (!(dir.exists())) {
			dir.mkdirs();
		}
		File file = new File(dir,ip+".jpg");
		FileOutputStream fos = new FileOutputStream(file);

		byte[] buf = new byte[1024];
		int len = 0;
		while((len = in.read(buf))!=-1){
			fos.write(buf, 0, len);
		}
		//获取socket输出流,显示上传结果
		OutputStream out  = socket.getOutputStream();
		out.write("上传成功".getBytes());
		fos.close();
		socket.close();
		ss.close();
	}

上述代码的服务端只能获取一个客户端上传,多个则不行。

服务端获取了1号客户端正在处理1号客户端,那么2号客户端就必须要等待,等待时间过长,就会连接超时,所以服务端结合线程,获取客户端对象为一个线程,处理客户端信息为一个线程,不停的切换,就可以实现多个客户端上传图片到服务端

服务端结合线程,改进

客户端部分不变

服务端

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
public class Up {

	private static ServerSocket ss;
	public static void main(String[] args)throws IOException {
		UpText_Server();
	}
	public static void UpText_Server() throws IOException {

		ss = new ServerSocket(6534);

		while(true){
			Socket socket = ss.accept();//不停的接收客户端对象
			new Thread(new UPtask(socket)).start();//创建多个线程执行不同客户端的信息
		}
	}
}

服务端线程

public class UPtask implements Runnable {
	private Socket socket;
	public UPtask(Socket socket){
		this.socket = socket;
	}
	public void run() {
		int count = 1;
		try {
		String ip = socket.getInetAddress().getHostAddress();
		System.out.println("IP : "+ip+"....connect");
		InputStream in = socket.getInputStream();

		  File dir = new File("c:\\CopyPic111111111");
			if (!(dir.exists())) {
				dir.mkdirs();
			}
			File file = new File(dir,ip+".jpg");
			//如果已经存在
			while(file.exists()) {
				file = new File(dir,ip+"("+(count++)+").jpg");
			}
			FileOutputStream fos = new FileOutputStream(file);

			byte[] buf = new byte[1024];
			int len = 0;
			while((len = in.read(buf))!=-1){
				fos.write(buf, 0, len);
			}
			//获取socket输出流,显示上传结果
			OutputStream out  = socket.getOutputStream();
			out.write("上传成功".getBytes());
			fos.close();
			socket.close();
		} catch (Exception e) {
			// TODO: handle exception
			throw new RuntimeException("服务器异常,请稍等");
		}
	}
}

时间: 2024-10-12 10:57:52

JAVA学习第六十二课 — TCP协议练习的相关文章

JAVA学习第五十二课 — IO流(六)File对象

File类 用来给文件或者文件夹封装成对象 方便对文件与文件夹的属性信息进行操作 File对象可以作为参数传递给流的构造函数 一.构造函数和分隔符 public static void FileDemo() {//构造函数演示 //可以将一个已存在或不存在的文件或目录封装成File对象 File file = new File("d:\\a.txt"); File file2 = new File("d:","a.txt"); File file

JAVA学习第六十四课 — 反射机制

   Java反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法,对于任意一个对象,都能够调用它的任意一个方法和属性,这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制,简单说:能动态获取类中的信息(类中所有),就是java的反射,也可以理解为对类的解剖 反射机制的基本应用场景:    比如一个程序的应用程序(TomCat),为了提高其扩展性,会对外暴露一个接口,在外部定义一个类实现这个接口,但是在应用程序内部无法new对象,所以应用程序会提供一个配置

JAVA学习第四十二课 — 泛型(二)—泛型接口&&通配符应用

一.泛型接口 interface Inter<T>{ public void show(T t); } class InterImple implements Inter<String>{//知道是字符串类型 public void show(String str){ System.out.println("show "+str); } } class InterImple_2<Q> implements Inter<Q>{//不知道是什

JAVA学习第三十二课(常用对象API)- 基本数据类型对象包装类

将基本数据类型(8种:int..)封装成对象的好处就是可以在对象中封装更多的功能和方法来操控该数据 常见的操作就是:用于基本数据类型与字符串之间的转换 基本数据类型对象包装类一般用于基本类型和字符串之间的转换 基本类型----->字符串 1.基本类型数值+"" 2.用string类中的valueOf(基本类型数值) 3.用Integer.ValueOf(); 字符串-->基本类型数值 1.使用包装类中的静态方法XXX  parseXXX("XXXX")比

JAVA学习第六十五课 — 正則表達式

正則表達式:主要应用于操作字符串.通过一些特定的符号来体现 举例: QQ号的校验 6~9位.0不得开头.必须是数字 String类中有matches方法 matches(String regex) 告知此字符串是否匹配给定的正則表達式. regex,就是给定的正則表達式 public static void checkQQ() { //第一位是数字1-9,第二位以后是0-9,除去第一位数剩下数字位数范围是5到8位 String regex = "[1-9][0-9]{5,8}";//正

JAVA学习第六十五课 — 正则表达式

正则表达式:主要应用于操作字符串,通过一些特定的符号来体现 举例: QQ号的校验 6~9位,0不得开头,必须是数字 String类中有matches方法 matches(String regex) 告知此字符串是否匹配给定的正则表达式. regex,就是给定的正则表达式 public static void checkQQ() { //第一位是数字1-9,第二位以后是0-9,除去第一位数剩下数字位数范围是5到8位 String regex = "[1-9][0-9]{5,8}";//正

java之jvm学习笔记六-十二(实践写自己的安全管理器)(jar包的代码认证和签名) (实践对jar包的代码签名) (策略文件)(策略和保护域) (访问控制器) (访问控制器的栈校验机制) (jvm基本结构)

java之jvm学习笔记六(实践写自己的安全管理器) 安全管理器SecurityManager里设计的内容实在是非常的庞大,它的核心方法就是checkPerssiom这个方法里又调用 AccessController的checkPerssiom方法,访问控制器AccessController的栈检查机制又遍历整个 PerssiomCollection来判断具体拥有什么权限一旦发现栈中一个权限不允许的时候抛出异常否则简单的返回,这个过程实际上比我的描述要复杂 得多,这里我只是简单的一句带过,因为这

Java学习笔记—第十二章 Java网络编程入门

第十二章  Java网络编程入门 Java提供的三大类网络功能: (1)URL和URLConnection:三大类中最高级的一种,通过URL网络资源表达方式,可以很容易确定网络上数据的位置.利用URL的表示和建立,Java程序可以直接读入网络上所放的数据,或把自己的数据传送到网络的另一端. (2)Socket:又称"套接字",用于描述IP地址和端口(在Internet中,网络中的每台主机都有一个唯一的IP地址,而每台主机又通过提供多个不同端口来提供多种服务).在客户/服务器网络中,当客

JAVA学习第五十九课 — 网络编程概述

网络模型 OSI(Open System Interconnection)开放系统互连:參考模型 TCP/IP 网络通讯要素 IP地址 port号 传输协议 网络參考模型 七层OSI模型的基本概念要了解 网际层协议:包含:IP协议.ICMP协议.ARP协议.RARP协议. 传输层协议:TCP协议.UDP协议. 应用层协议:FTP.Telnet.SMTP.HTTP.RIP.NFS.DNS. 要真正实现网络通讯,首先要找到IP地址,IP地址是网络通讯的一大要素 IP地址:InetAddress 网络