您可以同时写入套接字输入和输出流吗?

Posted

技术标签:

【中文标题】您可以同时写入套接字输入和输出流吗?【英文标题】:Can you write to a sockets input and output stream at the same time? 【发布时间】:2013-01-07 19:06:43 【问题描述】:

我有一个 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)
    


我已经注释了大部分实际代码,因为我只是想让它工作。我的写入功能在第一次写入时无限期阻塞。这可能是我的线程的问题吗?我唯一的想法是输出和输入流共享我的套接字对象,这可能会导致死锁或其他东西。请让我知道发生了什么。

【问题讨论】:

【参考方案1】:

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

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

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

【讨论】:

我认为既然输入和输出(两个独立的流)是独立的对象,同时对它们进行操作应该没关系。所以你说我还是应该担心同步?【参考方案2】:

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

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;



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

【讨论】:

我正在使用线程进行读写。一个线程读取,另一个线程写入。我也不能使用 PrintWriter 因为我必须发送原始字节数组,这就是我的音频数据所在。【参考方案3】:

我的 write() 似乎阻塞的原因是我愚蠢地关闭了 Socket() 而我的输入流没有意识到这一点。因此,永远不会发送任何数据。代表我犯了愚蠢的错误。

【讨论】:

以上是关于您可以同时写入套接字输入和输出流吗?的主要内容,如果未能解决你的问题,请参考以下文章

是否可以同时读取和写入 java.net.Socket?

我是不是需要锁定才能同时使用同一个应用程序两次写入套接字?

Java 套接字:您可以从一个线程发送并在另一个线程上接收吗?

异步写入套接字线程是不是安全?

在 C 或 C++ 中的同一个套接字上同时读取和写入

java套接字/输出流写入:它们阻塞吗?