你能同时写入套接字输入和输出流吗?

时间:2013-01-24 05:05:36

标签: java multithreading sockets thread-safety

我有一个Java应用程序,它是Voip。我正在使用一个套接字通过线程同时发送和接收信息。代码如下所示..

Socket clientSocket = sockList.accept();
OutputStream outSock = clientSocket.getOutputStream();
InputStream inSock = clientSocket.getInputStream();
new Thread( new Capture(outSock)).start();
new Thread( new PlayAudio(inSock)).start();
outSock.close();
clientSocket.close();

我发现的问题是,当我写入输出流时,它会在第一次写入时阻塞。我发送的字节数不多。贝娄是我的写代码。

private class Capture implements Runnable{

    private OutputStream out;
    public Capture(OutputStream out){
        this.out = out;
    }
    @Override
    public void run() {
        try{
            int numBytesRead;
            TargetDataLine outLine = getMic();
            outLine.open();
            outLine.start();

            byte[] data = new byte[outLine.getBufferSize() / 5];
            byte[] test = {0x1,0x1,0x1};

            while(true) {       
                //numBytesRead =  outLine.read(data, 0, data.length);
                //System.out.println(numBytesRead);
                out.write(test, 0, test.length);
                out.flush();
                /*if(numBytesRead > 0){
                    out.write(data, 0, data.length);
                    System.out.println("C");
                }*/
            }
        }catch(Exception ex){}
    }
}

读取声音代码的另一个线程是......

private class PlayAudio implements Runnable{

    private InputStream in;
    public PlayAudio(InputStream in){
        this.in = in;
    }
    @Override
    public void run() {
        int write;
        try{
        SourceDataLine inLine = getSpeaker();
        inLine.open();
        inLine.start();
        byte[] data = new byte[inLine.getBufferSize()];
        byte[] test = new byte[3];
        while(true){
            System.out.println(1);
            //write = in.read(data, 0, data.length);
            in.read(test, 0 , test.length);
            System.out.println(2);
            /*if(write > 0){
                inLine.write(data, 0, write);
                System.out.println(3);
                System.out.println(write);
            }*/
        }
        } catch(Exception ex){}
    }

}

我已经评论了很多实际代码,因为我只是想让它发挥作用。我的写函数在第一次写入时无限期地阻塞。这可能是我的线程有问题吗?我唯一的想法是输出和输入流共享我的套接字对象,这可能会导致死锁或其他东西。请让我知道最新消息。

3 个答案:

答案 0 :(得分:14)

是的,您可以同时写入套接字输入和输出流。

来自do-java-sockets-support-full-duplex

  

由于输入流和输出流是Socket中的独立对象,因此您可能唯一关心的是,如果您有2个线程尝试读取或写入会发生什么(两个线程,相同的输入/输出流)同时? InputStream / OutputStream类的读/写方法不同步。但是,如果您正在使用InputStream / OutputStream的子类,那么您正在调用的读/写方法是同步的。你可以检查你所调用的任何类/方法的javadoc,并快速找到它。

答案 1 :(得分:4)

是的,您可以在读取时写入套接字,但您必须在独立的线程中读取套接字。我正在使用这个概念。这里的例子是(仔细阅读它也支持多个客户端):

public class TeacherServerSocket {

private Logger logger = Logger.getLogger(TeacherServerSocket.class);
public static Map<String, TeacherServerThread> connectedTeacher = new HashMap<String, TeacherServerThread>();
ServerSocket serverSocket;;

@Override
public void run() {
    // starting teacher server socket
    this.serverSocket = startServer();
    // if unable to to start then serverSocket would have null value
    if (null != this.serverSocket) {

        while (true) {
             //listening to client for infinite time
            Socket socket = listenToClient();
            if (null != socket) {

                TeacherServerThread teacherServerThread = new TeacherServerThread(socket);
                Thread thread = new Thread(teacherServerThread);
                thread.start();

                //putting teacher ip address and teacher object into map
                connectedTeacher.put(teacherServerThread.getTeacherIp(),teacherServerThread);
                System.out.println("INFO: Teacher is connected with address "+ teacherServerThread.getTeacherIp());

            }

        }


    }

}

@Override
public ServerSocket startServer() {
     //port number on which teacher server will  be run.
     int port=12345;

    try {
        // throw an exception if unable to bind at given port
        ServerSocket serverSocket = new ServerSocket(port);
        System.out.println("Teacher server socket started on port no :"+port);
        return serverSocket;

    } catch (IOException e) {

        logger.error("Unable to start Teacher Server socket");
        e.printStackTrace();

    }

    return null;

}

@Override

public Socket listenToClient() {

    if (this.serverSocket != null) {

        try {
            // throw an exception is unable to open socket
            Socket socket = this.serverSocket.accept();
            return socket;

        } catch (IOException e) {

            logger.error("Unable to open socket for teacher");
            e.printStackTrace();

        }
    } 
    else {

        logger.error("TeacherServerSocket has got null value please restart the server");

    }

    return null;
}





@Override
public Map getConnectedDevicesMap() {

return  TeacherServerSocket.connectedTeacher;

}

/**
 * This method will send message to connected teacher which comes form  student
 * @author rajeev
 * @param   message, which comes form student
 * @return  void
 *   * */
@Override
public void publishMessageToClient(String message) {
    if(TeacherServerSocket.connectedTeacher.size()>0){
        System.out.println("Total Connected Teacher: "+TeacherServerSocket.connectedTeacher.size());
        for (String teacherIp : TeacherServerSocket.connectedTeacher.keySet()) {

            TeacherServerThread teacherServerThread=TeacherServerSocket.connectedTeacher.get(teacherIp);
            teacherServerThread.publishMessageToTeacher(message);

        }
    }

}



@Override
public void stopServer() {

    if (this.serverSocket != null) {

        try {

            serverSocket.close();

        } catch (Exception e) {

            e.printStackTrace();

        }
    }

}


}

在多个客户端的独立线程中读取:

public class TeacherServerThread implements Runnable {


Logger logger=Logger.getLogger(TeacherServerThread.class);
Socket socket;
String teacherIp;

public TeacherServerThread(Socket socket) {
 this.socket=socket;
 this.teacherIp=socket.getInetAddress().toString();
}


@Override
public void run() {
    //starting reading
    ReadFromTeacherAndPublishToStudent messageReader=new ReadFromTeacherAndPublishToStudent();
    Thread thread=new Thread(messageReader);
    thread.start();
}





private class ReadFromTeacherAndPublishToStudent implements Runnable {

    @Override
    public void run() {
        String message=null;
        try {
            BufferedReader readTeacherData=new BufferedReader(new InputStreamReader(socket.getInputStream()));

            StudentServerSocket studentServerSocket=new StudentServerSocket();
            //sending message to student which is read by teacher
            while((message=readTeacherData.readLine())!=null){
                //System.out.println("Message found : "+message);
               //  studentServerSocket.publishMessageToClient(message); // do more stuff here 

            }
            // if message has null value then it mean socket is disconnected.
      System.out.println("INFO: Teacher with IP address : "+teacherIp+" is disconnected");
      TeacherServerScoket.connectedTeacher.remove(getTeacherIp());
        if(null!=socket){
            socket.close();
        }

    } catch (IOException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }




}

} //class



public void publishMessageToTeacher(String message){

    if(this.socket!=null){

        try {

        PrintWriter writeMessageToTeacher=new PrintWriter(this.socket.getOutputStream());
        writeMessageToTeacher.println(message);
        writeMessageToTeacher.flush();
        System.out.println(" Message published to teacher"+message);
        }catch(Exception e){
        logger.error(e.toString());
        logger.error("Exception In writing data to teacher");

        }


    }else {
        logger.error("Unable to publish message to teacher .Socket has Null value in publishMessageToTeacher");     
        System.out.println("ERROR: socket has null value can not publish to teacher");
    }



}

public String getTeacherIp()
{
    return teacherIp;

}
}

根据您的要求更改代码......

答案 2 :(得分:0)

看起来我的write()阻塞的原因是因为我愚蠢地关闭了Socket()并且我的输入流没有实现它。因此,没有数据被发送出去。代表我的愚蠢错误。