Socket引发的死锁问题。

     刚刚看了下socket的知识,于是自己做了一个简单的从客户端向服务端传递图片的程序。 但是中间出现了一些小问题,程序曾经一度在某个位置进入等待状态,无法继续运行下去。

     下面分别是服务端和客户端的代码....


     客户端代码:

   

package cn.dzr.tcp.Client;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.Socket;

public class ClientTcp
{
	public static void main(String[] args) throws IOException
	{
		System.out.println("客服端启动,准备传输数据....");
		
		//1,创建Socket
		int port = 10010;						//设定端口号
		InetAddress address = InetAddress.getLocalHost();
		Socket socket = new Socket(address,port);
		
		//2,获取Socket中的数据
		OutputStream out = socket.getOutputStream();
		InputStream in = socket.getInputStream();
		//选择需要传输的文件.
		File file = new File("D:+"+File.separator+"Client/ke.jpg");
		
		//将文件进行传输
		//1,将文件内容转为文件流,先是传输到一个字节数组中。
		//2,将字节数组中的内容传输到out中。
		byte[] buff = new byte[1024];
		FileInputStream fis = new FileInputStream(file);
		
		int len = 0;   
		
		while((len =fis.read(buff))!=-1)
		{
			out.write(buff, 0, len);
		}
		System.out.println("文件传输完毕....");
		
		byte[] bufin = new byte[1024];
		int reclen = in.read(bufin);
		
		
		String str = new String(bufin,0,reclen);
		
		System.out.println("服务端传来的消息:"+str);
		
		
		
		fis.close();
		
		
		socket.close();
		
		
	}
}

服务端的代码如下:

 

package cn.dzr.tcp.Server;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;

public class ServerTcp
{
	public static void main(String[] args) throws IOException
	{
		System.out.println("服务端启动....准备接受数据.");
		//创建ServSocket,
		ServerSocket ss = new ServerSocket(10010);
		//获取连接过来的socket
		
		//为了让服务端能够一直接受消息,我们需要定义个while循环。
		
		int count = 0;
		int time = 1;
		while(true)
		{
			System.out.println("准备第"+(time++)+"次接受数据");
		Socket socket = ss.accept();
		
		
		InputStream in = socket.getInputStream();
		OutputStream out = socket.getOutputStream();
		
		//创建文件保存传输过来的数据.
		File file = new File("D:"+File.separator+"Server/ke.jpg");
		
		while(file.exists())
		{
			file = new File("D:"+File.separator+"Server"+
					File.separator+"keke("+(++count)+").jpg");
		}
		//直到文件不存在,才创建新的文件。
		file.createNewFile();
		
		System.out.println("保存的文件名字为: "+file.getName());
		//绑定输出流,等会将接受的数据传递到该文件中....
		FileOutputStream fos = new FileOutputStream(file);
		
		
		byte[] buff = new byte[1024];
		
		int len = 0;  //保存读取的字节个数
		int c = 1;
		while((len = in.read(buff))!=-1)
	  //while((len = in.read(buff))!=-1)
		{
			
			fos.write(buff, 0, len);
			System.out.println("第"+(c++)+"次"+"接受成功.. 数据长度为:"+len );
		}
		
		String str = "文件读取完毕!";
		System.out.println(str);
		out.write(str.getBytes());
		
		try
		{
			Thread.sleep(1000);
		} catch (InterruptedException e)
		{
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		fos.close();
		socket.close();
		}
		//ss.close();
		
		
	}
}

 在启动服务端之后,我再启动客户端。
       结果我发现客户端和服务都没有停止运行。为了了解到更详细的信息,我在服务端设置了很多的数据输出..
我发现服务端在下面这个while循环中一直都没有退出。

 
while((len = in.read(buff))!=-1)
	  //while((len = in.read(buff))!=-1)
		{
			
			fos.write(buff, 0, len);
			System.out.println("第"+(c++)+"次"+"接受成功.. 数据长度为:"+len );
		}

 我百思不得其解,客户端数据已经传输完毕,为什么服务端会停留在这个while循环,在len的值变成60之后,就没有继续执行下去。为什么服务端一直都没有读取到文件流的末尾,从而让len的值变成-1,退出循环。
     
       最后,我想,如果客户端的的socket的out流没有关闭,那该out就可能一直都在传输数据。所以,服务端就会处于一直接受数据的状态。
       我再回头看我客户端的代码。

  while((len =fis.read(buff))!=-1)
		{
			out.write(buff, 0, len);
		}
		System.out.println("文件传输完毕....");
		
		byte[] bufin = new byte[1024];
		int reclen = in.read(bufin);

 在如上这段代码中。
      当客户端将整个图片的数据发送出去之后,他就进入到“准备接受从服务端发送过来的数据”的状态。
      而此时,由于客户端的socket,socket中的out流都没有关闭。
      所以导致服务端停止在while循环中。其下面的

String str = "文件读取完毕!";
		System.out.println(str);
		out.write(str.getBytes());
  这段向客户端发送数据的代码就无法执行,而客户端就一直在等待服务端传过来的消息,以便继续执行下去。所以,双方都在等待对方的资源,从而造成一个类似死锁的情况。
 
      想到这一点,于是,我就在客户端准备接受数据之前。
      将socket中的OutputStream流给关闭。
      
      也就是在

while((len =fis.read(buff))!=-1)
		{
			out.write(buff, 0, len);
		}
		System.out.println("文件传输完毕....");
		
		byte[] bufin = new byte[1024];
		int reclen = in.read(bufin);

这段代码的System.out.println("文件传输完毕....");的后面加上out.close();
    我想,这样关闭了out流,那么服务端应该就会跳出循环,让代码执行下去。

 
   这个时候,服务端的数据是继续执行下去了,但是客服端却又出现了新的问题。运行客户端程序的时候,会出现如下的提示:


 客服端启动,准备传输数据....
   文件传输完毕....
Exception in thread "main" java.net.SocketException: socket closed
    at java.net.SocketInputStream.socketRead0(Native Method)
    at java.net.SocketInputStream.read(SocketInputStream.java:150)
    at java.net.SocketInputStream.read(SocketInputStream.java:121)
    at java.net.SocketInputStream.read(SocketInputStream.java:107)
    at cn.dzr.tcp.Client.ClientTcp.main(ClientTcp.java:43)


 socket closed,流已经被关闭。
 
  我想到的是,这里客户端的程序还没有执行下去,因为我没看到输出结果。

System.out.println("文件传输完毕....");
		out.close();
		byte[] bufin = new byte[1024];
		int reclen = in.read(bufin);
		
		
		String str = new String(bufin,0,reclen);
		
		System.out.println("服务端传来的消息:"+str);


这一段代码,下面的System.out.println("服务端传来的消息:"+str);没有被执行。
 那么,这段代码只有可能  int reclen = in.read(bufin); 这句代码执行时出现问题。因为如果这句通过,后面的代码没有什么可以阻碍他们的运行。
            
       (at cn.dzr.tcp.Client.ClientTcp.main(ClientTcp.java:43))
        这里其实已经标明了错误的位置....!!
        再看服务端那边的情况:
        
        其输出情况如下:

 服务端启动....准备接受数据.
准备第1次接受数据
保存的文件名字为: keke(28).jpg
第1次接受成功.. 数据长度为:1024
第2次接受成功.. 数据长度为:1024
第3次接受成功.. 数据长度为:1024
第4次接受成功.. 数据长度为:1024
第5次接受成功.. 数据长度为:1024
第6次接受成功.. 数据长度为:1024
第7次接受成功.. 数据长度为:1024
第8次接受成功.. 数据长度为:1024
第9次接受成功.. 数据长度为:1024
第10次接受成功.. 数据长度为:1024
第11次接受成功.. 数据长度为:1024
第12次接受成功.. 数据长度为:1024
第13次接受成功.. 数据长度为:1024
第14次接受成功.. 数据长度为:1024
第15次接受成功.. 数据长度为:1024
第16次接受成功.. 数据长度为:1024
第17次接受成功.. 数据长度为:1024
第18次接受成功.. 数据长度为:1024
第19次接受成功.. 数据长度为:1024
第20次接受成功.. 数据长度为:1024
第21次接受成功.. 数据长度为:1024
第22次接受成功.. 数据长度为:1024
第23次接受成功.. 数据长度为:1024
第24次接受成功.. 数据长度为:1024
第25次接受成功.. 数据长度为:1024
第26次接受成功.. 数据长度为:1024
第27次接受成功.. 数据长度为:1024
第28次接受成功.. 数据长度为:1024
第29次接受成功.. 数据长度为:1024
第30次接受成功.. 数据长度为:1024
第31次接受成功.. 数据长度为:1024
第32次接受成功.. 数据长度为:1024
第33次接受成功.. 数据长度为:1024
第34次接受成功.. 数据长度为:60
文件读取完毕!
准备第2次接受数据

这标明服务端已经正常执行完成。

   那么,到底是什么引起客户端的 (at cn.dzr.tcp.Client.ClientTcp.main(ClientTcp.java:43))
    
    socket closed,流已经被关闭。
  这个问题呢。
   开始我在服务端加入了一个sleep(),线程休眠。


  	System.out.println(str);
		out.write(str.getBytes());
		
		try
		{
			Thread.sleep(1000);
		} catch (InterruptedException e)
		{
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		fos.close();
		socket.close();
		}
		//ss.close();



   就是为了让服务端进行短暂的休眠,而不去执行socket.close();让客户端可以正常的接受数据。
   我开始以为是因为服务端执行速度过快,导致socket.close()执行,从而关闭了流。
   但是目前看来,情况并非如此。
   于是,我想,是不是因为socket的out流关闭,就会导致in流也关闭,或者整个socket都被关闭呢。
   我先试试看,将out.close();改成 in.close();
   看看有什么情况。
       
    结果,程序依然如上面 out.close() 被执行一般。
    证明in.close和out.close会起到同样的作用。
    那么,他是否可能会导致socket流被关闭呢。
    于是,我想在in.close()下,再加上一句socket.close();
    看下会发生什么情况。
  

 客服端启动,准备传输数据....
文件传输完毕....
Exception in thread "main" java.net.SocketException: socket closed
    at java.net.SocketInputStream.socketRead0(Native Method)
    at java.net.SocketInputStream.read(SocketInputStream.java:150)
    at java.net.SocketInputStream.read(SocketInputStream.java:121)
    at java.net.SocketInputStream.read(SocketInputStream.java:107)
    at cn.dzr.tcp.Client.ClientTcp.main(ClientTcp.java:44)  


错误依旧指向   “    int reclen = in.read(bufin); ”
    同时,我在socket.close()下加上许多句socket.close();
    在执行的时候,这些socket.close()并不会产生错误。
    这说明socket.close()是可以被执行无数次的,一个关闭的流再次被关闭并不会引发异常。
    
    不过,我们依然无法验证,in.close()是否导致socket.close()发生。
    虽然在错误提示信息里,有一个socket.closed的提示,但是异常分析通常具有非常大的不可预料性。很可能其他的原因,最后也导致出现这么一个提示。
    比如在一段程序里,你少写一个;号,结果系统就提示一堆莫名其妙的错误。
    如果你试图根据这些错误来解决问题,这比南辕北辙更加离谱了。

    后来发现socket有一个isclosed的方法。
     于是,我想通过代码验证一下。

while((len =fis.read(buff))!=-1)
		{
			out.write(buff, 0, len);
		}
		System.out.println("文件传输完毕....");
		in.close();
		System.out.println(socket.isClosed());


结果,这里会输出true。也就是说在in.close,out.close()后,socket也就随之关闭了。我思考下原因,可能是因为socket本来就是用来传输数据的,而你把这个数据的输入输出流给关闭了,他就不具备传输数据的作用,自然再以打开状态运行也是无效的。


   

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章