day20--网络

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/feiyanaffection/article/details/81712761

Day20-网络

1. 线程(续)

juc 中的大部分类是通过无锁并发实现的(没有用synchronized)

CAS 机制 compare And swap 比较并交换

synchronized 可以称之为悲观锁
cas 体现的是乐观锁
首先不会给共享资源加锁,而是做一个尝试
先拿到旧值,查看旧值是否跟共享区域的值相等
如果不等,那么说明别的线程改动了共享区域的值,我的修改失败
如果相等,那么就让我的修改成功
如果修改失败,没关系,重新尝试

    int var5;
       // 修改失败,没关系,重新尝试 自旋
        do {
           // 获取共享区域的最新值
            var5 = this.getIntVolatile(var1, var2); // 10
                    // 比较并交换                                      最新值   最新值+1
        } while(! this.compareAndSwapInt(var1, var2, var5, var5 + var4));

        return var5;

1. 重入锁 ReentrantLock

.lock() 加锁
.unlock() 解锁
例子:

static int i = 0;

public static void main(String[] args) throws InterruptedException {
    ReentrantLock rl = new ReentrantLock();

    Thread t1 = new Thread(() -> {
        for (int j = 0; j < 5000; j++) {
            try {
                rl.lock(); // 加锁
                i++;
            } finally {
                rl.unlock(); // 保证解锁一定被执行
            }
        }
    });

    Thread t2 = new Thread(() -> {
        for (int j = 0; j < 5000; j++) {
            try {
                rl.lock(); // 加锁
                i--;
            } finally {
                rl.unlock(); // 保证解锁一定被执行
            }
        }
    });

    t1.start();
    t2.start();
    t1.join();
    t2.join();
    System.out.println(i);
}

synchronized 性能上比较 ReentrantLock 在高并发下低,ReentrantLock的内存占用会高一些

2. CountDownLatch

countdown 倒计时

当希望多个线程执行完毕后,再接着做下一步操作时,
例子:

public static void main(String[] args) throws InterruptedException {
    CountDownLatch cdl = new CountDownLatch(3);// 构造方法需要指定倒计时的数字
    new Thread(()->{
        System.out.println("线程1开始运行"+new Date());
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("线程1准备完成"+new Date());
        cdl.countDown();
    }).start();
    new Thread(()->{
        System.out.println("线程2开始运行"+new Date());
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("线程2准备完成"+new Date());
        cdl.countDown();
    }).start();
    new Thread(()->{
        System.out.println("线程3开始运行"+new Date());
        try {
            Thread.sleep(1500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("线程3准备完成"+new Date());
        cdl.countDown();
    }).start();

    // 主线程等待,直到倒计时为0
    System.out.println("主线程等待");
    cdl.await();
    System.out.println("ready go....");
}

一个应用例子:模拟10个玩家加载进度

public static void main(String[] args) throws InterruptedException {
    CountDownLatch latch = new CountDownLatch(10);
    String[] all = new String[10];

    for (int j = 0; j < 10; j++) {
        int x = j;
        new Thread(()->{
            Random r = new Random();
            for (int i = 0; i <= 100; i++) {
                try {
                    Thread.sleep(r.nextInt(100));
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (all){
                    all[x]=(i+"%");
                    System.out.print("\r"+Arrays.toString(all));
                }
            }
            latch.countDown();

        }).start();
    }

    latch.await();
    System.out.println("\nend...");
}

3. 循环栅栏

// CyclicBarrier   可循环的 屏障(栅栏)
// 当满足CyclicBarrier设置的线程个数时,继续执行,没有满足则等待
CyclicBarrier cb = new CyclicBarrier(2); // 个数为2时才会继续执行

new Thread(()->{
    System.out.println("线程1开始.."+new Date());
    try {
        cb.await(); // 当个数不足时,等待
    } catch (InterruptedException e) {
        e.printStackTrace();
    } catch (BrokenBarrierException e) {
        e.printStackTrace();
    }
    System.out.println("线程1继续向下运行..."+new Date());
}).start();

new Thread(()->{
    System.out.println("线程2开始.."+new Date());
    try {
        Thread.sleep(2000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    try {
        cb.await(); // 2 秒后,线程个数够2,继续运行
    } catch (InterruptedException e) {
        e.printStackTrace();
    } catch (BrokenBarrierException e) {
        e.printStackTrace();
    }
    System.out.println("线程2继续向下运行..."+new Date());
}).start();

与倒计时锁的区别:倒计时锁只能使用一次,倒计时结束这个对象就没用了。
而循环栅栏可以重复利用。

4. 信号量

Semaphore s = new Semaphore(3); // 限制了能同时运行的线程上限
for (int i = 0; i < 10; i++) {
    new Thread(() -> {
        try {
            s.acquire(); // 获得此信号量
            System.out.println("我是线程" + Thread.currentThread().getName());
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            s.release(); // 释放信号量
        }

    }).start();
}

2. 网络模型与协议

enter description here
OSI 七层模式 : 应用层,表示层,会话层,传输层,网络层,链路层,物理层
这里写图片描述
五层模型: 应用层, 传输层,网络层,链路层,物理层
四层模型 : 应用层, 传输层,网络层,链路层

应用层:http(超文本传输协议) ftp(文件传输协议) stmp (邮件发送协议) pop3(邮件接收协议), ssh ( 安全shell,用于远程登录)

传输层: tcp(安全可靠的协议) udp(不可靠)

网络层:ip

windows下可以使用 ipconfig来查看ip地址
linux 下可以使用 ifconfig来查看ip地址

ip 地址的作用是用来定位到网络上的另一台计算机
port 端口 mysql 3306
oracle 1521
sqlserver 1433
redis 6379
tomcat 8080
apache(http的服务) 80
ftp 21
ssh 22
qq客户端 8000

端口号的作用是用来标记,要访问对方的哪个程序

传输层协议:
tcp协议:
TCP 协议的特点是: TCP 协议是一个有连接、可靠的协议。所谓有连接,指的是在进行 TCP通信之前,两个需要通信的主机之间要首先建立一条数据通道,就好像打电话进行交流之前,首先要让电话接通一样。所谓可靠,指的是 TCP 协议能够保证: 1、发送端发送的数据不会丢失; 2、接收端接受的数据包的顺序,会按照发送端发送的包的顺序接受。也就是说, TCP协议能够保证数据能够完整无误的传输。

udp协议:
与 TCP 协议相比, UDP 是一个无连接,不可靠的协议。 即:数据的发送方只负责将数据发送出去,数据的接受方只负责接受数据。发送方和接收方不会相互确认数据的传输是否成功。
相对于 TCP 而言, UDP 有一个优点:效率较高。因此,当我们在对数据传输的正确率
不太关心,但是对传输效率要求较高的情况下,可以采用 UDP 协议。典型的使用 UDP 协议的是网络语音以及视频聊天应用。

3. java中的网络编程

Socket API 对tcp、udp协议做了封装,能够连接到对方主机,收发数据

tcp的例子

建立连接
服务器端:

public static void main(String[] args) throws IOException {
    // 可以与客户端的socket建立连接  端口号一般使用4位以上的数字
    ServerSocket ss = new ServerSocket(5555);
    System.out.println("ready...等待客户端连接");
    // 端点
    Socket socket = ss.accept();// 等待客户端连接我服务器方法,直到有客户端连接
}

客户端:

public static void main(String[] args) throws IOException {
    // 本机ip地址为 127.0.0.1
    // 它的别名     localhost
    // 端点
    Socket socket =
            new Socket("127.0.0.1", 5555);
}

传输数据
这里写图片描述
1.CS模型一
server端:

package socket;

import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;

// 服务器端 Server
public class Server {

    public static void main(String[] args) throws IOException {
        // 可以与客户端的socket建立连接  端口号一般使用4位以上的数字
        ServerSocket ss = new ServerSocket(5555);
        System.out.println("ready...等待客户端连接");
        // 端点
        Socket socket = ss.accept();// 等待客户端连接我服务器方法,直到有客户端连接
        InputStream is = socket.getInputStream();
        byte[] buf = new byte[1024];
        while(true) {
            int len = is.read(buf);
            if(len == -1){
                break;
            }
            System.out.println(new String(buf,0,len));
        }
        socket.close();
    }
}

Client端:

package socket;

import java.io.IOException;
import java.io.InputStream;
import java.net.Socket;
import java.util.Scanner;

// 客户端 client
// 改进:从控制台输入信息
public class Client {

    public static void main(String[] args) throws IOException {
        // 本机ip地址为 127.0.0.1
        // 它的别名     localhost
        // 端点
        Socket socket =
                new Socket("127.0.0.1", 5555);
//        socket.getOutputStream().write("hello".getBytes());

        // 接收消息的线程
        new Thread(()->{
            try {
                InputStream is = socket.getInputStream();
                while(true) {
                    byte[] buf = new byte[1024];
                    int len = is.read(buf);
                    if(len == -1) {
                        break;
                    }
                    String content = new String(buf, 0, len);
                    System.out.println(content);
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }).start();

        Scanner scanner = new Scanner(System.in);
        while(true) {
            // 从控制台反复读入内容
            String line = scanner.nextLine();
            socket.getOutputStream().write(line.getBytes());
        }



//        socket.close();
    }
}

2.CS模型二:利用线程池处理多线程任务请求
server端:

package socket;

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

// 要支持多个客户端连接
public class Server2 {

    public static void main(String[] args) throws IOException {
        ServerSocket ss = new ServerSocket(5555);
        ExecutorService threadPool = Executors.newCachedThreadPool();
        System.out.println("ready... 等待客户端连接");
        while(true) {
            Socket socket = ss.accept(); // 每连接一个新的用户调用一次accept
            // 让线程池中的线程来处理这次请求
            threadPool.submit(()->{
                try {
                    InputStream is = socket.getInputStream();
                    SocketAddress address = socket.getRemoteSocketAddress();
                    byte[] buf = new byte[1024];
                    while(true) {
                        int len = is.read(buf);
                        if(len==-1){
                            break;
                        }
                        System.out.println(address+" "+new String(buf,0,len));
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            });
        }
    }
}

3.CS模型三:利用线程池处理多线程任务请求,并且记录每个客户的信息
server端:

package socket;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketAddress;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

// 要支持多个客户端连接, 要记录所有的客户端socket,并给客户端socket发送信息
public class Server3 {

    public static void main(String[] args) throws IOException {
        ServerSocket ss = new ServerSocket(5555);
        ExecutorService threadPool = Executors.newCachedThreadPool();
        // 集合用来保存所有的客户端socket
        ConcurrentHashMap<Socket, SocketAddress> map = new ConcurrentHashMap<>();

        System.out.println("ready... 等待客户端连接");
        while(true) {
            Socket socket = ss.accept(); // 每连接一个新的用户调用一次accept
            map.put(socket, socket.getRemoteSocketAddress()); // 将客户socket存入map
            // 让线程池中的线程来处理这次请求
            threadPool.submit(()->{
                try {
                    InputStream is = socket.getInputStream();
                    SocketAddress address = socket.getRemoteSocketAddress();
                    byte[] buf = new byte[1024];
                    while(true) {
                        int len = is.read(buf);
                        if(len==-1){
                            break;
                        }
                        String content = address + " " + new String(buf, 0, len);// 获取某个客户端发过来的消息内容
//                        System.out.println(address+" "+new String(buf,0,len));
                        // 遍历map集合
                        for (Socket clientSocket : map.keySet()) {
                            // 向客户端socket写入消息内容
                            OutputStream os = clientSocket.getOutputStream();
                            os.write(content.getBytes());
                        }
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            });
        }
    }
}

4.基于UDP的单身寂寞狗自言自语聊天
发送端sender:

public class ChatSender implements Runnable {

    @Override
    public void run() {
        try {
            DatagramSocket socket = new DatagramSocket();

            BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));

            String line = null;
            DatagramPacket packet = null;
            System.out.print("root@~$:");
            while((line = reader.readLine()) != null) {
                packet = new DatagramPacket(line.getBytes(), 0, line.getBytes().length,
                            InetAddress.getLocalHost(), 9090);
                socket.send(packet);
            }

            socket.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}

接收端sender:
public class ChatReceive implements Runnable {

@Override
public void run() {
    try {
        DatagramSocket socket = new DatagramSocket(9090);

        byte[] buf = new byte[1024];
        DatagramPacket packet = new DatagramPacket(buf, buf.length);

        boolean flag = true;

        while(flag) {
            socket.receive(packet);

            System.out.println(packet.getAddress().getHostAddress() + " 对我说:" +
                        new String(buf, 0, packet.getLength()));
        }

        socket.close();

    } catch (Exception e) {
        e.printStackTrace();
    }
}

}

```Main测试类```

public class ChatMain {

public static void main(String[] args) {
    ChatReceive receive = new ChatReceive();
    new Thread(receive).start();

    ChatSender sender = new ChatSender();
    new Thread(sender).start();

}

}

“`
这里写图片描述

5.最牛逼的聊天室实现
server端:

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import java.util.Arrays;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Server {
    final static ConcurrentHashMap<Socket, String> map = new ConcurrentHashMap<>();
    public static void main(String[] args) throws IOException {
        ServerSocket ss = new ServerSocket(5555);
        ExecutorService executor = Executors.newCachedThreadPool();
        System.out.println("Waiting for connecting...");
        while(true) {
            Socket s = ss.accept();
            executor.execute(()->{
                receive(s);
            });
        }

    }

    private static void dispatcher(Socket socket, int cmd , String content) throws IOException {
        switch (cmd) {
            case 0:
                map.put(socket, content);
                System.out.println("Client["+map.get(socket)+"] is connected...");
                break;
            case 1: {
                String resp = Arrays.toString(map.values().toArray());
                send(socket, resp);
                break;
            }
            case 2: {
                String resp = "["+map.get(socket)+"] say:" + content;
                for (Socket s : map.keySet()) {
                    send(s, resp);
                }
                break;
            }
            case 3:
                String[] array = content.split(" ");
                String name = array[0];
                String resp = "["+map.get(socket)+"] say:" + array[1];
                for( Socket s : map.keySet()) {
                    String n = map.get(s);
                    if(n.equals(name)) {
                        send(s, resp);
                    }
                }
                break;
            case 4:
                System.out.println(content);
                break;
        }
    }

    private static void send(Socket socket, String content) throws IOException {
        byte[] bytes = content.getBytes("utf-8");
        int length = bytes.length;
        OutputStream os = socket.getOutputStream();
        os.write(length >> 8);
        os.write(length);
        os.flush();
        os.write(bytes);
        os.flush();
    }

    private static void receive(Socket socket)  {
        try{
            InputStream is = socket.getInputStream();
            while(true) {
                // 先高后低
                int r1 = is.read();
                if(r1 == -1) {
                    break;
                }
                int r2 = is.read();
                int length = (r1 << 8) + r2;
                int cmd = is.read();
                byte[] bytes = new byte[length];
                is.read(bytes);
                dispatcher(socket, cmd, new String(bytes, "utf-8"));
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            System.out.println("Client["+map.get(socket)+"] has disconnected...");
            if(socket != null) {
                map.remove(socket);
                if(!socket.isClosed()) {
                    try {
                        socket.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
}

client端:

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.net.SocketException;
import java.util.Scanner;

public class Client {
    private static volatile boolean quit = false;
    public static void main(String[] args) throws IOException {
        Socket s = new Socket("127.0.0.1", 5555);
        Scanner scanner = new Scanner(System.in);
        System.out.println("Prepare a name for you:");
        String nick = scanner.nextLine();
        if (nick.length() == 0) {
            nick = String.valueOf(s.getLocalPort());
        }
        register(s, nick);
        new Thread(()->{
            try {
                receive(s);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }).start();
        System.out.println("ok, 可以聊天了,您的昵称是:[" + nick + "]");
        System.out.println("================================");
        System.out.println("=========1.查看聊天室用户[:u]");
        System.out.println("=========2.群发[:a 内容]");
        System.out.println("=========3.私聊[:p 对方昵称 内容]");
        System.out.println("=========4.测试[:t 内容]");
        System.out.println("=========5.退出[:q]");
        System.out.println("================================");


        while (!quit) {
            String content = scanner.nextLine();
            if (content.equals(":u")) {
                send(s, 1, "");
            } else if (content.startsWith(":a")) {
                String[] a = content.split(" ");
                if (a.length < 2) {
                    System.out.println("格式不正确,请重新输入");
                    continue;
                }
                send(s, 2, a[1]);
            } else if (content.startsWith(":p")) {
                String[] a = content.split(" ");
                if (a.length < 3) {
                    System.out.println("格式不正确,请重新输入");
                    continue;
                }
                send(s, 3, content.replaceFirst(":p ", ""));
            } else if (content.startsWith(":t")) {
                String[] a = content.split(" ");
                if (a.length < 2) {
                    System.out.println("格式不正确,请重新输入");
                    continue;
                }
                send(s, 4, a[1]);
            } else if (content.equals(":q")) {
                if(!s.isClosed()) {
                    s.close();
                }
            } else if(quit) {

            } else {
                System.out.println("格式不正确,请重新输入");
            }
        }
    }

    private static void register(Socket socket, String nick) throws IOException {
        send(socket, 0, nick);
    }

    private static void send(Socket socket, int cmd, String content) throws IOException {
        byte[] bytes = content.getBytes("utf-8");
        int length = bytes.length;
        OutputStream os = socket.getOutputStream();
        os.write(length >> 8);
        os.write(length);
        os.write(cmd);
        os.flush();
        os.write(bytes);
        os.flush();
    }

    private static void receive(Socket socket) throws IOException{
        try{
            InputStream is = socket.getInputStream();
            while(!quit) {
                // 先高后低
                int r1 = is.read();
                if(r1 == -1) {
                    break;
                }
                int r2 = is.read();
                int length = (r1 << 8) + r2;
                byte[] bytes = new byte[length];
                is.read(bytes);
                System.out.println(new String(bytes, "utf-8"));
            }
        } catch (SocketException e) {
            System.out.println("service has disconnected, please press any key continue...");
            quit = true;
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if(socket != null) {
                if(!socket.isClosed()) {
                    try {
                        socket.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
}

猜你喜欢

转载自blog.csdn.net/feiyanaffection/article/details/81712761