java中网络编程

UDP通信

Java提供了DatagramSocket类作为基于UDP协议的Socket

构造方法

方法说明
DatagramSocket()创建数据报套接字并将其绑定到本机地址上的任何可用端口
DatagramPacket(byte[] buf,int len,InetAddress add,int port)创建数据包,发送长度为len的数据包到指定主机的指定端口

相关方法

方法名说明
void send(DatagramPacket p)发送数据报包
void close()关闭数据报套接字
void receive(DatagramPacket p)从此套接字接受数据报包

代码示例

发送端

import java.io.IOException;
import java.net.*;
import java.util.Scanner;
public class Client {

    public static void main(String[] args) throws IOException {
        //创建socket
        DatagramSocket datagramSocket = new DatagramSocket();
        Scanner scanner = new Scanner(System.in);
        while (true){
            String line = scanner.nextLine();
            if("886".equals(line)){
                break;
            }
            //打包数据包
            DatagramPacket datagramPacket = new DatagramPacket(line.getBytes(), line.length(), InetAddress.getByName("127.0.0.1"), 10000);
            //发送数据
            datagramSocket.send(datagramPacket);
        }
        //关闭发送端
        datagramSocket.close();

    }


}

接收端

import java.io.IOException;
import java.lang.reflect.Array;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;
import java.util.Arrays;

public class Server {
    public static void main(String[] args) throws IOException {
        //创建接收端socket
        DatagramSocket datagramSocket = new DatagramSocket(10000);
        //接收缓存
        byte[] bytes=new byte[1024];
        DatagramPacket datagramPacket = new DatagramPacket(bytes, bytes.length);
        while (true){
            //清空数组
            Arrays.fill(bytes, (byte) 0);
            //接收数据
            datagramSocket.receive(datagramPacket);
            System.out.println("数据:"+new String(datagramPacket.getData(), 0, datagramPacket.getLength()));
        }

        //关闭
//        datagramSocket.close();
    }
}

TCP

java为客户端提供了Socket类,为服务器端提供了ServerSocket类

  • 构造方法

    方法名说明
    Socket(InetAddress address,int port)创建流套接字并将其连接到指定IP指定端口号
    Socket(String host, int port)创建流套接字并将其连接到指定主机上的指定端口号
  • 相关方法

    方法名说明
    InputStream getInputStream()返回此套接字的输入流
    OutputStream getOutputStream()返回此套接字的输出流
  • 构造方法

    方法名说明
    ServletSocket(int port)创建绑定到指定端口的服务器套接字
  • 相关方法

    方法名说明
    Socket accept()监听要连接到此的套接字并接受它

代码示例
client
注意缓冲流要刷新或关闭对方才能获取到数据

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

public class Client {

    public static void main(String[] args) throws IOException {
        //创建客户端socke
        Socket socket = new Socket("127.0.0.1",10000);
        //获取输出流发送数据
        OutputStream outputStream = socket.getOutputStream();
        //使用转换流解决中文乱码
        BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(outputStream, "utf-8"));
        bufferedWriter.write("hello,在干嘛?");
        bufferedWriter.flush();
        //处理业务时如果没有发送结束标志,服务端会在read时读不到结束标志而一直等待接收数据,可以使用shutdownOutput来写结束标志
        socket.shutdownOutput();//仅仅关闭输出流.并写一个结束标记,对socket没有任何影响
        //获取输入流读取数据
        InputStream inputStream = socket.getInputStream();
        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream,"utf-8"));
        int b=0;
        while ((b=bufferedReader.read())!=-1){
            System.out.println((char)b);
        }
        System.out.println("client接收数据");
        //关流的的时候会发送一个结束标志
        bufferedWriter.close();
        bufferedReader.close();
        socket.close();
    }

}


server

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


public class Server {
    public static void main(String[] args) throws IOException {
        //创建服务器端socket
        ServerSocket serverSocket = new ServerSocket(10000);
        //等待连接并返回连接socket
        Socket accept = serverSocket.accept();
        //获取输入流读取数据
        InputStream inputStream = accept.getInputStream();
        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream,"utf-8"));
        int b=0;
        while ((b=bufferedReader.read())!=-1){
            System.out.println((char)b);
        }
        //获取输出流回写数据
        OutputStream outputStream = accept.getOutputStream();
        BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(outputStream, "utf-8"));
        bufferedWriter.write("hello,你是哪位?");
        bufferedWriter.newLine();
        bufferedWriter.flush();
        System.out.println("server发送数据");
        //关闭
        bufferedReader.close();
        bufferedWriter.close();
        accept.close();
        serverSocket.close();
    }
}

使用线程池实现文件上传功能
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",10000);
        //是本地的流,用来读取本地文件的
        BufferedInputStream bisFile = new BufferedInputStream(new FileInputStream("test\\ba1.jpg"));
        //网络流,写到服务器
        OutputStream os = socket.getOutputStream();
        BufferedOutputStream bosSocket = new BufferedOutputStream(os);
        int b;
        while((b = bisFile.read())!=-1){
            bosSocket.write(b);//通过网络写到服务器中
        }
        bosSocket.flush();
        //给服务器一个结束标记,告诉服务器文件已经传输完毕
        socket.shutdownOutput();

        //接收服务器的消息
        BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream(),"utf-8"));
        String line;
        while((line = br.readLine()) !=null){
            System.out.println(line);
        }
        //关闭
        bisFile.close();
        socket.close();
    }


}

thread

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

public class ThreadSocket implements Runnable {
    private Socket acceptSocket;

    public ThreadSocket(Socket accept) {
        this.acceptSocket = accept;
    }

    @Override
    public void run() {
        BufferedOutputStream bos = null;
        try {
            //网络中的流,从客户端读取数据的
            BufferedInputStream bisSocket = new BufferedInputStream(acceptSocket.getInputStream());
            //本地的IO流,把数据写到本地中,实现永久化存储
            bos = new BufferedOutputStream(new FileOutputStream("test\\ServerDir\\" + UUID.randomUUID().toString() + ".jpg"));

            int b;
            while((b = bisSocket.read()) !=-1){
                bos.write(b);
            }
            //接收文件成功发送消息
            BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(acceptSocket.getOutputStream()));
            bw.write("上传成功");
            bw.newLine();
            bw.flush();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if(bos != null){
                try {
                    bos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (acceptSocket != null){
                try {
                    acceptSocket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

server

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class Server {
    public static void main(String[] args) throws IOException {
        ServerSocket ss = new ServerSocket(10000);
        //创建线程池
        ThreadPoolExecutor pool = new ThreadPoolExecutor(
                3,//核心线程数量
                10,   //线程池的总数量
                60,   //临时线程空闲时间
                TimeUnit.SECONDS, //临时线程空闲时间的单位
                new ArrayBlockingQueue<>(5),//阻塞队列
                Executors.defaultThreadFactory(),//创建线程的方式
                new ThreadPoolExecutor.AbortPolicy()//任务拒绝策略
        );

        while (true) {
            Socket accept = ss.accept();
            ThreadSocket ts = new ThreadSocket(accept);
            pool.submit(ts);
        }
    }
}

NIO

非阻塞IO实现
client

import java.io.*;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;

public class Client {

    public static void main(String[] args) throws IOException {
        //1.打开通道
        SocketChannel socketChannel = SocketChannel.open();
        //2.指定IP和端口号
        socketChannel.connect(new InetSocketAddress("127.0.0.1",10000));
        //3.写出数据
        ByteBuffer byteBuffer = ByteBuffer.wrap("死亡如风,常伴吾身".getBytes("utf-8"));
        socketChannel.write(byteBuffer);
        // 手动写入结束标记,防止服务端读取不到结束标志而阻塞,从而不能循环处理新连接
//        socketChannel.shutdownOutput();
        System.out.println("数据已经写给服务器");
        // 4.读取服务器写回的数据
        ByteBuffer byteBuffer2 = ByteBuffer.allocate(1024);
        int len;
        while((len = socketChannel.read(byteBuffer2)) != -1){
            //因为要循环从buff读数据,所以要切换读写模式,切换为读模式
            byteBuffer2.flip();
            System.out.println(new String(byteBuffer2.array(),0,len,"utf-8"));
            //数据读写完毕,切换为写模式,因为循环要读数据到buff中,所以要切换为写
            byteBuffer2.clear();
        }
        //4.释放资源
        socketChannel.close();
    }


}

server

import java.io.*;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.ByteBuffer;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class Server {
    public static void main(String[] args) throws IOException {
//        1.打开一个服务端通道
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
//        2.绑定对应的端口号
        serverSocketChannel.bind(new InetSocketAddress(10000));
//        3.通道默认是阻塞的,需要设置为非阻塞
        //如果传递true 表示通道设置为阻塞通道...默认值
        //如果传递false 表示通道设置为非阻塞通道
        serverSocketChannel.configureBlocking(false);
        while (true) {
//        5.如果有客户端来连接了,则在服务端通道内部,再创建一个客户端通道,相当于是客户端通道的延伸
            //此时已经设置了通道为非阻塞
            //所以在调用方法的时候,如果有客户端来连接,那么会创建一个SocketChannel对象.
            //如果在调用方法的时候,没有客户端来连接,那么他会返回一个null
            SocketChannel socketChannel = serverSocketChannel.accept();
            if(socketChannel != null){
                System.out.println("此时有客户端来连接了");
                //设置客户端连接通道为非阻塞的,从而read就不会因为没有结束标记而阻塞,设置非阻塞后read读完数据len返回0
                socketChannel.configureBlocking(false);
//        6.客户端将缓冲区通过通道传递给服务端,就到了这个延伸通道socketChannel里面
//        7.服务端创建一个空的缓冲区装数据并输出
                ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
                //获取传递过来的数据,并把他们放到byteBuffer缓冲区中.
                //返回值:
                //正数: 表示本次读到的有效字节个数.
                //0   : 表示本次没有读到有效字节.
                //-1  : 表示读到了末尾
//                int len = socketChannel.read(byteBuffer);
//                System.out.println(new String(byteBuffer.array(),0,len,"utf-8"));
                int len;
                //针对于缓冲区来讲
                //如果 从添加数据 ----> 获取数据 flip
                //如果 从获取数据 ----> 添加数据 clear
                while((len = socketChannel.read(byteBuffer))>0){
                    //切换为读
                    byteBuffer.flip();
                    System.out.println(new String(byteBuffer.array(),0,len,"utf-8"));
                    //切换为写
                    byteBuffer.clear();
                }
                //会写数据到客户端
                System.out.println("接收数据完毕,准备开始往客户端回写数据");
                ByteBuffer byteBuffer2 = ByteBuffer.wrap("我蹭踏足山巅也曾跌落谷底!!!".getBytes("utf-8"));
                socketChannel.write(byteBuffer2);
                //8.释放资源
                socketChannel.close();
            }
        }
    }


}

阻塞IO:相当于你点了外卖后都是一直在门口等外卖,直到外卖送过来
非阻塞IO:相当于你点了外卖后,不在一直傻等,而是干一下别的事情,就跑到门口看看外卖到了没有。一直这样循环,直到我取到外卖,才从这个循环中跳出来

多路IO

多路IO:相当于你点外卖,请了一个门卫看着,外卖没来之前可以自己干自己的事情,如果外卖到了门卫会通知你去拿,

多路IO步骤

  1. 打开一个服务端通道(open)
  2. 绑定对应的端口号
  3. 通道默认是阻塞的,需要设置为非阻塞
  4. 打开一个选择器(门卫大爷)
  5. 将选择器绑定服务端通道,并监视服务端是否准备好
  6. 如果有客户端来连接了,大爷会遍历所有的服务端通道,谁准备好了,就让谁来连接
    连接后,在服务端通道内部,再创建一个客户端延伸通道
  7. 如果客户端把数据传递过来了,大爷会遍历所有的延伸通道,谁准备好了,谁去接收数据

client

import java.io.*;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;

public class Client {

    public static void main(String[] args) throws IOException {
        //1.打开通道
        SocketChannel socketChannel = SocketChannel.open();
        //2.指定IP和端口号
        socketChannel.connect(new InetSocketAddress("127.0.0.1",10000));
        //3.写出数据
        ByteBuffer byteBuffer = ByteBuffer.wrap("死亡如风,常伴吾身".getBytes("utf-8"));
        socketChannel.write(byteBuffer);
        // 手动写入结束标记,防止服务端读取不到结束标志而阻塞,从而不能循环处理新连接
//        socketChannel.shutdownOutput();
        System.out.println("数据已经写给服务器");
        // 4.读取服务器写回的数据
        ByteBuffer byteBuffer2 = ByteBuffer.allocate(1024);
        int len;
        while((len = socketChannel.read(byteBuffer2)) != -1){
            //因为要循环从buff读数据,所以要切换读写模式,切换为读模式
            byteBuffer2.flip();
            System.out.println(new String(byteBuffer2.array(),0,len,"utf-8"));
            //数据读写完毕,切换为写模式,因为循环要读数据到buff中,所以要切换为写
            byteBuffer2.clear();
        }
        //4.释放资源
        socketChannel.close();
    }


}

server

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;

public class Server {
    public static void main(String[] args) throws IOException {
        //1.打开服务端通道
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
        //2.让这个通道绑定一个端口
        serverSocketChannel.bind(new InetSocketAddress(10000));
        //3.设置通道为非阻塞
        serverSocketChannel.configureBlocking(false);
        //4.打开一个选择器
        //Selector --- 选择器
//        SelectionKey --- 绑定通道后返回那个令牌
        //      SelectableChannel --- 可以使用选择器的通道
        Selector selector = Selector.open();
        //5.绑定选择器和服务端通道
        //将Channel注册到给定的Selector sel上,并指定注册的事件类型(SelectionKey.OP_READ,OP_WRITE,OP_CONNECT,OP_ACCEPT等)
        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);

        while(true){
            //选择器会监视客户端通道的状态.
            //6.返回值就表示此时有多少个客户端来连接
            //阻塞直到注册在Selector中的Channel 发送可读写事件(或其他注册事件)
            int count = selector.select();
            if(count != 0){
                System.out.println("有事件发生了");
                //7.会遍历所有的服务端通道.看谁准备好了,谁准备好了,就让谁去连接.
                //获取所有服务端通道的令牌,并将它们都放到一个集合中,将集合返回.
                Set<SelectionKey> selectionKeys = selector.selectedKeys();
                Iterator<SelectionKey> iterator = selectionKeys.iterator();
                while(iterator.hasNext()){
                    //selectionKey 依次表示每一个服务端通道的令牌
                    SelectionKey selectionKey = iterator.next();
                    if(selectionKey.isAcceptable()){
                        //可以通过令牌来获取到了一个已经就绪的服务端通道
                        ServerSocketChannel ssc = (ServerSocketChannel) selectionKey.channel();
                        //客户端的延伸通道
                        SocketChannel socketChannel = ssc.accept();
                        //将客户端延伸通道设置为非阻塞的
                        socketChannel.configureBlocking(false);
                        socketChannel.register(selector,SelectionKey.OP_READ);
                        //当客户端来连接的时候,所有的步骤已经全部执行完毕.
                    }else if(selectionKey.isReadable()){
                        //当前通道已经做好了读取的准备(延伸通道)
                        SocketChannel socketChannel = (SocketChannel) selectionKey.channel();
                        ByteBuffer byteBuffer1 = ByteBuffer.allocate(1024);
                        int len;
                        while((len = socketChannel.read(byteBuffer1)) > 0){
                            byteBuffer1.flip();
                            System.out.println(new String(byteBuffer1.array(),0,len));
                            byteBuffer1.clear();
                        }
                        //给客户端的回写数据
                        socketChannel.write(ByteBuffer.wrap("你装啥呢!!!".getBytes("utf-8")));
                        socketChannel.close();
                    }
                    iterator.remove();
                }
            }
        }
    }


}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值