概念

Socket 又被称为“套接字”。有一个层叫传输层,传输层的作用:将上三层的数据 和下三层的数据进行 转换
套接字:实际上就是咱们传输层用来传输数据的 一种载体 (通道),包括IP和端口

套接字编程

​Socket - 图1
使用其中的哪些类取决于程序所需处理的通讯协议。例如,基于TCP的应用程序可使用 Socket、ServerSocket 等类;基于UDP的应用程序则使用DatagramPacket、DatagramSocket、MulticastSocket 等类;基于 HTTP 和 FTP 等协议直接访问 URL 资源的应用程序可使用 URL、URLConnection 等类。

一个简单的实例


我们需要建立一个服务器类和一个客户端类,实现一个简单的功能:客户端向服务端发送消息

服务器类:

  1. /**
  2. * TCP/IP协议的服务端(代码)
  3. *
  4. * @author Administrator
  5. *
  6. */
  7. public class SocketServerStudy {
  8. public static void main(String[] args) {
  9. // TODO Auto-generated method stub
  10. System.out.println("主线程启动!!!!!");
  11. ServerSocket server = null;
  12. try {
  13. //创建Socket 的服务端,并指定8080 为通讯端口
  14. server = new ServerSocket(8080);
  15. //接收客户端(该方法是个阻塞式方法)
  16. Socket socket = server.accept();
  17. //输入流
  18. InputStream in = socket.getInputStream();
  19. //转换流
  20. InputStreamReader isr = new InputStreamReader(in,"UTF-8");
  21. //缓冲流
  22. BufferedReader br = new BufferedReader(isr);
  23. //边读边响应
  24. String str = "";
  25. while((str = br.readLine()) != null) {
  26. System.out.println("服务器接收的数据是:" + str);
  27. }
  28. } catch (IOException e) {
  29. // TODO Auto-generated catch block
  30. e.printStackTrace();
  31. }
  32. }
  33. }

客户端类:

public class SocketClientStudy {
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        //本机使用127.0.0.1  或者 localhost
        Socket socket = null;
         try {
             socket = new Socket("127.0.0.1", 8080);
            //建立传输流
            OutputStream os = socket.getOutputStream();
            OutputStreamWriter osw = new OutputStreamWriter(os, "UTF-8");
            BufferedWriter bw = new BufferedWriter(osw);
            //向服务端发送数据
            bw.write("这是一个TCP/IP的测试信息!!!!");
            bw.flush();
            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            bw.close();
            osw.close();
            os.close();
        } catch (UnknownHostException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}

总结:
1、此例子实现了单服务器与单客户端之间的连接
2、服务器使用的I/O流,本质上是由客户端类进行提供
3、在连接时,客户端需要明确服务器的IP地址和端口号,服务器只需要向操作系统申请一个端口号即可
4、本机的IP地址可以表示为:127.0.0.1或者localhost

案例—单服务器连接多客户端

要实现这个功能很简单,只需要把Socket socket = server.accept();代码和下方的代码加入一个While死循环即可,每连接上一个客户端,就会创建一个新的Socket实例,客户端和客户端之间不冲突

public class SocketServerStudy {
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        System.out.println("主线程启动!!!!!");
        ServerSocket server = null;
        try {
            //创建Socket 的服务端,并指定8080 为通讯端口
            server = new ServerSocket(8080);
            while(true) {
                //接收客户端(该方法是个阻塞式方法)
                Socket socket = server.accept();
                //输入流
                InputStream in = socket.getInputStream();
                //转换流
                InputStreamReader isr = new InputStreamReader(in,"UTF-8");
                //缓冲流
                BufferedReader br = new BufferedReader(isr);
                //边读边响应
                String str = "";
                while((str = br.readLine()) != null) {
                    System.out.println("服务器接收的数据是:" + str);
                }
            }
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}

案例—结合线程的知识,实现读写任务的分离


要实现这个功能,我们需要把读写的任务分别作为线程的任务进行封装,在服务器端只需要开启线程即可

服务器:

/**
 * TCP/IP协议的服务端(代码)
 * 
 * @author Administrator
 *
 */
public class SocketServerStudy {
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        System.out.println("主线程启动!!!!!");
        ServerSocket server = null;
        try {
            // 创建Socket 的服务端,并指定8080 为通讯端口
            server = new ServerSocket(8080);
            while (true) {
                // 接收客户端(该方法是个阻塞式方法)
                Socket socket = server.accept();
                //定义一个传输队列(FIFO)
                ArrayBlockingQueue<String> queue = new ArrayBlockingQueue<String>(1024);
                // 读
                Thread t1 = new Thread(new ServerReaderTask(socket,queue));
                t1.start();
                // 写
                Thread t2 = new Thread(new ServerWriterTask(socket,queue));
                t2.start();
            }
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}

读取任务:

/**
 * 服务端的 读取任务
 * 
 * @author Administrator
 *
 */
public class ServerReaderTask implements Runnable {
    /**
     * 代表连接通道
     */
    private Socket socket;
    /**
     * 数据传输通道
     */
    private ArrayBlockingQueue<String> queue;
    public ServerReaderTask(Socket socket, ArrayBlockingQueue<String> queue) {
        super();
        this.socket = socket;
        this.queue = queue;
    }
    @Override
    public void run() {
        // TODO Auto-generated method stub
        // 输入流
        InputStream in = null;
        InputStreamReader isr = null;
        BufferedReader br = null;
        try {
            if(!socket.isClosed() && socket.isConnected()) {
                // 获得客户端的IP地址
                String hostName = socket.getInetAddress().getHostName();
                System.out.println(hostName);
                in = socket.getInputStream();
                // 转换流
                isr = new InputStreamReader(in, "UTF-8");
                // 缓冲流
                br = new BufferedReader(isr);
                // 读取消息
                String str = "";
                while (!(str = br.readLine()).equals("END")) {
                    System.out.println("服务器接收[" + hostName + "]的消息:" + str);
                }
                queue.add("[" + hostName + "] 的消息,我已收到!!!");
            }
        } catch (Exception e) {
            // TODO Auto-generated catch block
            if(e instanceof SocketException) {
                System.out.println("该连接已被重置!!!!");
            }
        }
    }
}

写入任务:

/**
 * 服务端的 写入任务
 * 
 * @author Administrator
 *
 */
public class ServerWriterTask implements Runnable {
    /**
     * 代表连接通道
     */
    private Socket socket;
    /**
     * 数据传输通道
     */
    private ArrayBlockingQueue<String> queue;
    public ServerWriterTask(Socket socket, ArrayBlockingQueue<String> queue) {
        super();
        this.socket = socket;
        this.queue = queue;
    }
    @Override
    public void run() {
        // TODO Auto-generated method stub
        //建立传输流
        OutputStream os = null;
        OutputStreamWriter osw = null;
        BufferedWriter bw = null;
        try {
            if(!socket.isClosed() && socket.isConnected()) {
                os = socket.getOutputStream();
                osw = new OutputStreamWriter(os, "UTF-8");
                bw = new BufferedWriter(osw);
                while(!queue.isEmpty()) {
                    bw.write(queue.poll());
                    bw.newLine();
                }
                bw.flush();
            }
        } catch (Exception e) {
            // TODO Auto-generated catch block
            if(e instanceof SocketException) {
                System.out.println("该连接已被重置!!!!");
            }
        }
    }
}

客户端:

public class SocketClientStudy {
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        // 本机使用127.0.0.1 或者 localhost
        Socket socket = null;
        // 建立传输流
        OutputStream os = null;
        OutputStreamWriter osw = null;
        BufferedWriter bw = null;
        try {
            socket = new Socket("127.0.0.1", 8080);
            if (!socket.isClosed() && socket.isConnected()) {
                os = socket.getOutputStream();
                osw = new OutputStreamWriter(os, "UTF-8");
                bw = new BufferedWriter(osw);
                for(int i = 0; i < 10; i ++) {
                    System.out.println("客户端输出:" + i);
                    Thread.sleep(2000);
                    bw.write("长江,长江,我是黄河!!!!");
                    bw.newLine();
                }
                bw.write("END");
                bw.newLine();
            }
            bw.flush();
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } finally {
            if (!socket.isClosed() && socket.isConnected()) {
                try {
                    bw.close();
                    osw.close();
                    os.close();
                    socket.close();
                } catch (IOException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        }
    }
}