[Java]一个TCP文本上传相关的异常处理和偶然引出的中文编码问题

时间:2023-01-10 20:25:46

源程序:

Client:

class  TcpClient4
{
public static void main(String[] args)
{
Socket s=null;//要在块外定义,不然catch中无法找到!------->并初始化,否则finally中判断时提示你可能尚未初始化!!
try
{
s=new Socket("127.0.0.1",10013);//单独try,连不上直接终止运行,没有继续运行下面程序的必要!

try
{
//经测试,按记事本默认编码保存,这里只有用UTF-8解码复制结果是乱码!按utf-8保存,复制结果仍然不一致(字节数都不一致!),但竟然不乱码!(开头多了个?)
//这里的测试:其中sscc.txt是utf-8文件,文本:“你妈逼你结婚了吗?”30个字节。故意用gb2312解码,结果当然乱码:字节、内容都不一致,但这里按utf-8正常解码,下面一切默认,则不乱码,前边多了个?,并且字节数不一致,显示是21个字节,说明是GBK或GB2312编码!
BufferedReader bufr=new BufferedReader(new InputStreamReader(new FileInputStream("c:\\sscc.txt"),"utf8"));//底层都是读字节,这里可以指定码表,并且是用转换流转成字符流读取方式

//BufferedReader bufr=new BufferedReader(new FileReader("c:\\x.txt"));

//这是默认编码GBK
PrintWriter pw=new PrintWriter(s.getOutputStream(),true);//第二个参数true:自动刷新

String line=null;

while((line=bufr.readLine())!=null){
pw.println(line);//自动加回车换行,那边readLine可以判断并成功读取一行
}
}
catch (Exception ex)
{
throw new RuntimeException(ex);
}
}
catch (Exception e)
{
throw new RuntimeException(e);
}

finally{
try
{
//s是引用
if(s!=null)
s.close();
}
catch (Exception exc)
{
throw new RuntimeException(exc);
}
}
}
}

Server:

class  TcpServer4
{
public static void main(String[] args)
{
ServerSocket ss=null;//要在块外定义,不然catch找不到!
Socket s=null;
try
{
ss=new ServerSocket(10013);

s=ss.accept();

try
{
BufferedReader bufr=new BufferedReader(new InputStreamReader(s.getInputStream()));

PrintWriter pw=new PrintWriter(new FileWriter("c:\\x2.txt"),true);

String line=null;

while((line=bufr.readLine())!=null){
pw.println(line);
}
}
catch (Exception ex)
{
throw new RuntimeException(ex);
}
}
catch (Exception e)
{
throw new RuntimeException(e);
}
finally{
try
{
if(s!=null)
s.close();

if(ss!=null)
ss.close();
}
catch (Exception exc)
{
throw new RuntimeException(exc);
}

}

}
}
关于异常处理:

1.Socket相关要单独try处理,连接出现问题就没有继续运行下面代码的必要;

2.Socket(引用)要在try块外定义,不然catch块找不到!

3.Socket需要在定义时初始化,不然编译提示:finally中的if判断可能会出现Socket尚未初始化的情况!


按不同码表复制的结果:

经测试验证:按照记事本默认ANSI编码保存,程序中一切按照默认码表处理,正常复制,结果的内容和字节数均与源文件一致,没有问题:ANSI对应中文编码为GBK,而FileReader,FileWriter等的默认码表也是GBK,不乱码

按照UTF-8保存文本(可以从字节数看出来,UTF-8中文一个汉字3个字节),客户端读取时按照UTF-8正常解码(解码出汉字字符串),其他地方一切按默认编码,结果并不乱码,只是文本开头莫名多了一个?

结论:有些原因不明,只能看实际结果,不要主观臆测,想当然!实际编程中只要严格按照同一种码表编码和解码就不会出现问题!抓住这一点,其他一切不要深究,不要冒险尝试!!!


上传成功后反馈信息的程序改进:

注意在客户端的while循环结束后(它是可以结束的,读文件),如果没有额外再输出一个结束标记,那么由于其下面还有一个阻塞式的readLine等待反馈信息,而服务端那边的while得不到结束标记,则两端都会陷入阻塞。

Client:

class  TcpClient4
{
public static void main(String[] args)
{
Socket s=null;//要在块外定义,不然catch中无法找到!------->并初始化,否则finally中判断时提示你可能尚未初始化!!
try
{
s=new Socket("127.0.0.1",10013);//单独try,连不上直接终止运行,没有继续运行下面程序的必要!

try
{

BufferedReader bufr=new BufferedReader(new InputStreamReader(new FileInputStream("c:\\xxcc.txt"),"gbk"));

//BufferedReader bufr=new BufferedReader(new FileReader("c:\\x.txt"));


PrintWriter pw=new PrintWriter(s.getOutputStream(),true);//第二个参数true:自动刷新

String line=null;

while((line=bufr.readLine())!=null){
pw.println(line);//自动加回车换行,那边readLine可以判断并成功读取一行
}

//可以定义结束标记那边判断(读取,传过去,那边得到后判断,用DataOutputStream),比如空行(Tomcat的做法,分割请求和响应的头和主体)和时间戳(长整型,DataInputStream的readLong方法),这里调用Socket的方法关闭输出流
s.shutdownOutput();//关闭客户端的输出流,相当于给流中加入一个结束标记-1,让那边的循环停下来

//读取一个上传成功的反馈
BufferedReader bufIn=new BufferedReader(new InputStreamReader(s.getInputStream()));

String str=bufIn.readLine();//一样需要换行符

System.out.println(str);

bufr.close();

s.close();//和Socket相关的流随Socket关闭而自然关闭
}
catch (Exception ex)
{
throw new RuntimeException(ex);
}
}
catch (Exception e)
{
throw new RuntimeException(e);
}

finally{
try
{
//s是引用
if(s!=null)
s.close();
}
catch (Exception exc)
{
throw new RuntimeException(exc);
}
}
}
}

Server:

class  TcpServer4
{
public static void main(String[] args)
{
ServerSocket ss=null;//要在块外定义,不然catch找不到!
Socket s=null;
try
{
ss=new ServerSocket(10013);

s=ss.accept();

try
{
BufferedReader bufr=new BufferedReader(new InputStreamReader(s.getInputStream()));

PrintWriter pw=new PrintWriter(new FileWriter("c:\\x2.txt"),true);

String line=null;

while((line=bufr.readLine())!=null){
pw.println(line);
}

//那边关闭Socket输出流以结束这边的循环,那么这里仍然可以调用,难道是重新打开?
PrintWriter pout=new PrintWriter(s.getOutputStream(),true);

pout.println("Upload Succeed.");//必须有回车换行,那边才能readLine

pw.close();//和Socket有关的流随Socket关闭而自然关闭!

}
catch (Exception ex)
{
throw new RuntimeException(ex);
}
}
catch (Exception e)
{
throw new RuntimeException(e);
}
finally{
try
{
if(s!=null)
s.close();

if(ss!=null)
ss.close();
}
catch (Exception exc)
{
throw new RuntimeException(exc);
}

}

}
}

文件成功复制,测试成功。客户端结果:

D:\java\practice3>java TcpClient4
Upload Succeed.

D:\java\practice3>