Java Socket编程与客户/服务器应用开发(三)

流式Socket API:

数据包Socket API支持离散数据单元(即数据包)交换,流式Socket API则提供了基于UNIX操作系统的流式IO的数据传输模式。
根据定义,流式Socket API仅支持面向连接通信


流式Socket为两个特定进程提供稳定的数据交换模型。数据流从一方连续写入,从另一方读出。流的特性允许以不同速度向流中写入或读取数据,但是一个流式Socket 不能用于同时与两个及其以上的进程通信。




在Java中,有两个类提供了流式Socket API:ServerSocket和Socket。

1)ServerSocket用于接受连接,称之为连接Socket。

2)Socket用于数据交换,称之为数据Socket。


采用该API,服务器进程建立一个连接Socket,随后侦听来自其他进程的连接请求。每次只接受一个连接请求。当连接被接受后,将为该连接创建一个数据Socket。服务器进程可通过数据Socket从数据流读取数据或向其中写入数据。一旦两进程之间的通信会话结束,数据Socket被关闭,服务器可通过连接Socket自由接收下一个连接请求。


客户进程创建一个Socket,随后通过服务器的连接Socket向服务器发送连接请求。一旦请求被接受,客户Socket与服务器数据Socket连接,以便客户可继续从数据流读取数据或向数据流写入数据。当两进程之间的通信会话结束后,数据Socket关闭。


ServerSocket 的 accept()方法是阻塞操作,如果没有正在等待的请求,服务器进程被挂起,直到连接请求到达。

Socket 的输入流中读取数据时,即InputStream的read()方法是阻塞操作,如果请求的所有数据没有全部到达该输入流中,客户进程将被阻塞,直到有足够数量的数据被写入数据流。


public class SocketServerConnectionAcceptor {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        try {
            //服务器监听端口
            int portNo=9527;
            ServerSocket connectionSocket = new ServerSocket(portNo);
            System.out.println("now ready accept a connection~");
            //阻塞监听连接
            Socket dataSocket=connectionSocket.accept();
            //可以从socket里获得发送方的地址和端口,以及接收方的地址和端口
            System.out.println(dataSocket.getInetAddress()+"  "+dataSocket.getPort()+"   "
                    +dataSocket.getRemoteSocketAddress()+"   "+dataSocket.getLocalPort()+"   "
                    +dataSocket.getLocalAddress());
            System.out.println("connection accepted:");

            OutputStream outputStream=dataSocket.getOutputStream();
            PrintWriter printWriter = new PrintWriter(new OutputStreamWriter(outputStream));
            for(int i=0;i<5;i++) {
                String msg=scanner.nextLine();
                printWriter.println(msg);
                printWriter.flush();
            }
            System.out.println("message sent!!");
            //先关掉数据Socket
            dataSocket.close();
            System.out.println("data socket closed!");
            //再关掉连接Socket
            connectionSocket.close();
            System.out.println("connection socket closed!");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}



public class SocketConnectionRequestor {
    public static void main(String []args) {
        try {
            //服务器地址和端口号
            InetAddress acceptorHost = InetAddress.getByName("127.0.0.1");
            int acceptorPort=9527;
            SocketAddress socketAddress = new InetSocketAddress(acceptorHost, acceptorPort);

            //数据Socket
            Socket mySocket = new Socket();
            //最大等待连接时长
            int timeoutPeriod=5000;
            mySocket.connect(socketAddress,timeoutPeriod);

            System.out.println("Connection request granted!!");
            //可以从socket里获得发送方的地址和端口,以及接收方的地址和端口
            System.out.println(mySocket.getInetAddress()+"  "+mySocket.getPort()+"   "
                    +mySocket.getRemoteSocketAddress()+"   "+mySocket.getLocalPort()+"   "
                    +mySocket.getLocalAddress());

            InputStream inputStream=mySocket.getInputStream();
            BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));

            System.out.println("waiting to read!!");

            for(int i=0;i<5;i++) {
                String msg=bufferedReader.readLine();
                System.out.println("Message received:");
                System.out.println("\t"+msg);
            }

            mySocket.close();
            System.out.println("data socket closed!!");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}



以上是服务器写数据,客户端读数据;其实完全可以双方都写、读。

当使用PrintWriter向 Socket流写入数据时,必须使用flush()方法调用来真正地填充与刷新该流,从而确保所有数据都可以在像Socket突然关闭等意外情形发生之前,尽可能快地从数据缓冲区中真正地写入数据流。


为允许将程序中的应用逻辑和服务逻辑分离,采用了隐藏数据Socket细节的子类。

public class MyStreamSocket{
   private Socket  socket;
   private BufferedReader input;
   private PrintWriter output;
   MyStreamSocket(String acceptorHost, int acceptorPort ) throws SocketException, IOException{
      socket = new Socket(acceptorHost, acceptorPort );
      setStreams();
   }
   MyStreamSocket(Socket socket)  throws IOException {
      this.socket = socket;
      setStreams();
   }
   private void setStreams( ) throws IOException{
      // 从输入流中获取inputStream
      InputStream inStream = socket.getInputStream();
      input = new BufferedReader(new InputStreamReader(inStream));
      OutputStream outStream = socket.getOutputStream();
      // 创建一个PrintWriter来操作字符流
      output = new PrintWriter(new OutputStreamWriter(outStream));
   }
   public void sendMessage(String message) throws IOException {	
      output.println(message);   
      //flush操作为了避免意外关闭socket而导致数据丢失
      output.flush();               
   } // end sendMessage
   public String receiveMessage( ) throws IOException {	
      // 从输入流读取一行
      return input.readLine( );
   } //end receiveMessage
   public void SocketClose() throws IOException{
      socket.close();
   }
} //end class

使用以上封装好的Socket操作:

public class ServerSocketConnectionAcceptor {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        try {
            int portNo=9527;
            ServerSocket serverSocket = new ServerSocket(portNo);
            System.out.println("now ready accept a connection~");
            MyStreamSocket dataSocket = new MyStreamSocket(serverSocket.accept());
            System.out.println("connection accepted:");
            for(int i=0;i<5;i++) {
                dataSocket.sendMessage(scanner.nextLine());
            }
            dataSocket.SocketClose();
            System.out.println("data socket closed!");
            serverSocket.close();
            System.out.println("connection socket closed!");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

 
public class SocketConnectionRequestor {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        try {
            int serverPortNo=9527;
            String serverHost = "127.0.0.1";
            MyStreamSocket dataSocket = new MyStreamSocket(serverHost,serverPortNo);
            System.out.println("connection request granted:");
            for(int i=0;i<5;i++) {
                System.out.println(dataSocket.receiveMessage());
            }
            dataSocket.SocketClose();
            System.out.println("data socket closed!");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}



以上是服务器为单线程,若加入多线程:

public class MyStreamSocket implements Runnable{
   private Socket  socket;
   private BufferedReader input;
   private PrintWriter output;
   MyStreamSocket(String acceptorHost, int acceptorPort ) throws SocketException, IOException{
      socket = new Socket(acceptorHost, acceptorPort );
      setStreams();
   }
   MyStreamSocket(Socket socket)  throws IOException {
      this.socket = socket;
      setStreams();
   }
   ......
   @Override
   public void run() {
      Scanner scanner = new Scanner(System.in);
      try {
         System.out.println("connection accepted:");
         for(int i=0;i<5;i++) {
            sendMessage(scanner.nextLine());
         }
         SocketClose();
         System.out.println("data socket closed!");
      } catch (Exception e) {
         e.printStackTrace();
      }
   }
} //end class



public class ServerSocketThreadConnectionAcceptor {
    public static void main(String[] args) {

        try {
            int portNo=9527;
            ServerSocket serverSocket = new ServerSocket(portNo);
            System.out.println("now ready accept a connection~");

            while (true) {
                Socket socket=serverSocket.accept();
                new Thread(new MyStreamSocket(socket)).start();
            }

//            serverSocket.close();
//            System.out.println("connection socket closed!");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}



  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值