Socket编程

什么是Socket?

Socket是连接运行在网络上的两个程序间的双向通讯的端点。

Socket进行网络通信的过程

1.服务器程序将一个套接字绑定到一个特定的端口,并通过此套接字等待和监听客户的连接请求。
2.客户程序根据服务器程序所在的主机名和端口号发出连接请求。

服务器在端口监听到有客户端发来的请求,如果有连接请求,那么接受连接请求并获得一个新的绑定到不同端口地址的套接字(不可能有两个程序同时占用一个端口),那么便可以调用线程去处理该请求,服务器继续在该接口监听其他客户端的请求。

 客户和服务器通过读写套接字进行通讯。
 这里写图片描述
 这里写图片描述
其中:

  服务器端的套接字ServerSocket类通过传入监听的端口号创建。

  accept()方法监听向该端口发来的连接并接收连接。它将会阻塞直到连接被建立好。连接建立好后它会返回一个Socket对象。

  连接建立好后,服务器端和客户端的输入流和输出流就互为彼此,即一端的输出流是另一端的输入流。

总结:使用ServerSocket和Socket实现服务器端和客户端的Socket通信

  (1)建立Socket连接

  (2)获得输入/输出流

  (3)读/写数据

  (4)关闭输入/输出流

  (5)关闭Socket
  

服务端接受两个客户端的连接请求,并且通过线程池去处理客户端的连接请求。由于使用了线程池,那么系统可以接受很多客户端的请求而且保持较好的性能

Server端:

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ServerDemo {
    public static void main(String[] args) throws IOException {
        ServerSocket ss = new ServerSocket(9999);
        Socket socket = null;
        //利用静态方法创建线程池,无需自己再设定复杂的参数
        ExecutorService es = Executors.newCachedThreadPool();
        while ((socket = ss.accept())!= null){
            es.execute(new SocketTask(socket));
        }
    }
}

服务器端接受请求后是如何处理的?

import java.io.*;
import java.net.Socket;

public class SocketTask implements Runnable {
    private Socket socket;
    public SocketTask(Socket socket){
        this.socket = socket;
    }
    @Override
    public void run() {
        BufferedReader br = null;
        String input = "";
        OutputStream os = null;
        try {
            br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            input = br.readLine();
            System.out.println("接收到消息:"+input);
            os = socket.getOutputStream();
            os.write("Hello Client!\r".getBytes());
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            try {
                os.flush();
                os.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

Client 1:

import java.io.*;
import java.net.Socket;

public class Client {
    public static void main(String[] args) throws IOException {
        Socket socket = new Socket("127.0.0.1",9999);

        OutputStream os = socket.getOutputStream();
        // 末尾必须加终止符,否则另一端的bufferedreader.readline()方法会处于阻塞状态,直到流关闭!!!!
        //非常关键,如果不加则无法通信
        os.write("I am Dream\r".getBytes()); 
        os.flush();
        InputStream is = socket.getInputStream();
        String input = new BufferedReader(new InputStreamReader(is)).readLine();
        System.out.println("接受消息:"+input);
        socket.close();
    }
}

Client 2:

import java.io.*;
import java.net.Socket;

public class Client {
    public static void main(String[] args) throws IOException {
        Socket socket = new Socket("127.0.0.1",9999);

        OutputStream os = socket.getOutputStream();
        os.write("I am Thia\r".getBytes());
        os.flush();
        InputStream is = socket.getInputStream();
        String input = new BufferedReader(new InputStreamReader(is)).readLine();
        System.out.println("接受消息:"+input);
        socket.close();
    }
}

一个服务端和多个客户端实现双向通信,服务端维护一个基于端口号——套接字的哈希表,服务端每监听到有客户端发来的请求,便将连接建立好生成的套接字及通信的端口号放入哈希表。在服务端向客户端发送消息的时候,遍历哈希表,将消息发送至所有的客户端。(一对一的双向通信比较简单,不需要维护建立连接的客户端的套接字哈希表)
Server端实现:

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;
import java.util.Map;

public class Server1 {
    static Map<Integer,Socket> socketMap = new HashMap<>();
    public static void main(String[] args) throws IOException {
        ServerSocket ss = new ServerSocket(9999);
        while (true){
            Socket socket = ss.accept();
            socketMap.put(socket.getPort(),socket);
            new RecvThread(socket).start();
            new SendThread(socketMap).start();
        }
    }
}
class SendThread extends Thread{
    private Map<Integer,Socket> socketMap;
    public SendThread(Map<Integer,Socket> socketMap){
        this.socketMap = socketMap;
    }
    @Override
    public void run() {
            try {
                while (true) {

                    BufferedReader reader = new BufferedReader(
                            new InputStreamReader(System.in));
                    String input = reader.readLine();
                    //!!!!重要
                    input += "\r";
                    for (Map.Entry<Integer,Socket> entry :socketMap.entrySet()) {
                        Socket socket = entry.getValue();
                        OutputStream os = socket.getOutputStream();
                        os.write(input.getBytes());
                        os.flush();
                    }
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
    }
}
class RecvThread extends Thread{
    private Socket socket;
    public RecvThread(Socket socket){
        this.socket = socket;
    }
    @Override
    public void run() {
            try {
                InputStream is = socket.getInputStream();
                while (true) {
                    BufferedReader br = new BufferedReader(new InputStreamReader(is));
                    String input = br.readLine();
                    System.out.println("接受消息来自端口" + socket.getPort() + " " + input);
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
    }
}

Client端:

import java.io.*;
import java.net.Socket;

public class Client {
    public static void main(String[] args) throws IOException {
        Socket socket = new Socket("127.0.0.1",9999);
        Thread sendThread = new Thread(new Runnable() {
            @Override
            public void run() {
                    try {
                        OutputStream os = socket.getOutputStream();
                        while (true) {
                            BufferedReader reader = new BufferedReader(
                                    new InputStreamReader(System.in));
                            String input = null;
                            input = reader.readLine();
                            //!!!!必须加
                            input+="\r";
                            os.write(input.getBytes());
                            os.flush();
                        }
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
        });
        sendThread.start();
        Thread recvThread = new Thread(new Runnable() {
            @Override
            public void run() {
                    try {
                        InputStream is = socket.getInputStream();
                        while (true) {
                            BufferedReader br = new BufferedReader(new InputStreamReader(is));
                            //readLine()是阻塞方法,直到读到内容并且遇到终止符(“\r”、“\n”、“\r\n”等等)或者到达流的末尾(返回Null)才返回
                            String input = br.readLine();
                            System.out.println(input);
                        }
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
        });
        recvThread.start();
    }
}

多个客户端都运行没有问题

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值