JAVA自学教程之TCP协议练习



JAVA自学教程之TCP协议练习。

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

练习1:创建一个英文大写转换服务器
客户端输入字母数据,发送给服务端,服务端收到后显示到控制台,并将该数据转成大写返回客户端,知道客户端输入over,转换结束
[java] view plaincopy在CODE上查看代码片派生到我的代码片
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:上传文本文件
[java] view plaincopy在CODE上查看代码片派生到我的代码片
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:上传图片
上传图片到客户端
[java] view plaincopy在CODE上查看代码片派生到我的代码片
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();

}
上传图片到服务端
[java] view plaincopy在CODE上查看代码片派生到我的代码片
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号客户端就必须要等待,等待时间过长,就会连接超时,所以服务端结合线程,获取客户端对象为一个线程,处理客户端信息为一个线程,不停的切换,就可以实现多个客户端上传图片到服务端

服务端结合线程,改进
客户端部分不变
服务端
[java] view plaincopy在CODE上查看代码片派生到我的代码片
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();//创建多个线程执行不同客户端的信息
}
}
}

服务端线程
[java] view plaincopy在CODE上查看代码片派生到我的代码片
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(“服务器异常,请稍等”);
}
}
}
UDP和TCP的区别:
UDP:将数据打包,有限制,不连接,效率高,不安全,容易丢包
TCP:建立数据通道,无限制,效率低,安全