网络编程练习

一、UDP

聊天室

UDP发送数据:数据来自于键盘录入,直到输入的数据是886,发送数据结束。

UDP接收数据:因为接收端不知道发送端什么时候停止发送,故采用死循环接收。

(1)一个人发送多次数据

发送端代码:

public class sendMessage {
    public static void main(String[] args) throws IOException {
        //1.找到快递公司
        DatagramSocket ds = new DatagramSocket();
        //2.封装数据
        //键盘录入数据
        Scanner sc = new Scanner(System.in);
        while (true) {
            String s = sc.next();
            if (s.equals("886")) {
                break;
            }
            byte[] bytes = s.getBytes();
            InetAddress ip = InetAddress.getByName("127.0.0.1");
            int port = 10086;
            DatagramPacket dp = new DatagramPacket(bytes, bytes.length, ip, port);
            //3.发送数据
            ds.send(dp);
            
        }
        //4.释放资源
        ds.close();
    }
}

接收端代码:

public class receiveMessage {
    public static void main(String[] args) throws IOException {
        //一定要绑定端口号
        //表示从哪个端口接收数据
        DatagramSocket ds = new DatagramSocket(10086);
        //创建一个包用来接收数据
        byte[] bytes = new byte[1024];
        DatagramPacket dp = new DatagramPacket(bytes, bytes.length);
        while (true) {
            //进行接收
            ds.receive(dp);
            //解析数据
            byte[] data = dp.getData();
            String str = new String(data);
            InetAddress address = dp.getAddress();
            int port = dp.getPort();
            System.out.println("接收到的数据为:" + str);
            System.out.println("由ip为" + address + ",端口号为" + port + "进行发送");
        }
    }
}

多个发送端发送数据: Edit Configurations --> Modify options -->Allow multiple instance,就可以一次运行多个发送端程序。

二、TCP

1、多发多收

客户端:多次发送数据
服务器:接收多次接收数据,并打印

(1)客户端代码:

public class Client {
    public static void main(String[] args) throws IOException {
        Socket socket = new Socket("127.0.0.1", 10086);
        OutputStream is = socket.getOutputStream();
        Scanner sc = new Scanner(System.in);
        while (true) {
            System.out.println("请输入信息:");
            String s = sc.next();
            is.write(s.getBytes());
            if (s.equals("886")) {
                break;
            }
        }
        socket.close();
    }
}

(2)服务器端代码:

public class Server {
    public static void main(String[] args) throws IOException {
        ServerSocket ss = new ServerSocket(10086);
        Socket socket = ss.accept();
        BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
        String str;
        while ((str = br.readLine()) != null) {
            System.out.println(str);
        }
        socket.close();
        ss.close();
    }
}

①关于服务端这段代码,使用缓冲字符流读取,客户端循环输入数据,但是只有在客户端发送886结束循环之后服务端才能接收到数据。

运行结果:

不建议使用缓冲流的readLine方法,如果使用需要特意在后面加换行符才可以否则也会阻塞?不知道。 

②但是如果使用字符流读取的话,就是一次输入结束之后就会进行读取。

2 、接收和反馈

客户端发送一条数据,服务器接收数据并打印,再给客户端回复消息,客户端接收服务端回复的消息并打印。

Tips:当客户端数据输入结束但并没有关闭连接时,其实服务端还一直在等待客户端的数据,就是阻塞在读取数据上;但是客户端关闭数据连接的通道也作为一种结束标记,所以服务端知道客户端数据发完了,就不会阻塞在读取数据上了。

总结:

在数据传输时,服务端会阻塞在读取数据上,只有看到结束标记时跳出(就是你客户端发完了,你要让我服务端知道你发完了啊)。客户端关闭数据连接的通道和socket.shutdownOutput()都作为一种结束标记。

服务端代码:

(1)当向客户端回复时,通过通道对象socket获取输出流。

public class Server {
    public static void main(String[] args) throws IOException {
        ServerSocket ss = new ServerSocket(10086);
        Socket socket = ss.accept();
        InputStreamReader isr = new InputStreamReader(socket.getInputStream());
        int b;
        while ((b = isr.read()) !=-1) {
            System.out.print((char)b);
        }
        OutputStream os = socket.getOutputStream();
        String s = "me too";
        os.write(s.getBytes());

        socket.close();
        ss.close();
    }
}

客户端代码: 

(1)当客户端数据发送结束之后,但是又不能关闭连接时,可以有一个结束标志来告诉服务端我的数据已经发送完毕了,通过socket.shutdownOutput();。

(2)当客户端接收来自服务端的回复时,通过通道对象socket获取输入流。

public class Client {
    public static void main(String[] args) throws IOException {
        Socket socket = new Socket("127.0.0.1", 10086);
        OutputStream is = socket.getOutputStream();
        Scanner sc = new Scanner(System.in);
        while (true) {
            System.out.println("请输入信息:");
            String s = sc.next();
            is.write(s.getBytes());
            if (s.equals("886")) {
                break;
            }
        }
        //关闭输入流,表示数据已经传输完毕
        socket.shutdownOutput();
        InputStreamReader isr = new InputStreamReader(socket.getInputStream());
        int b;
        while ((b = isr.read()) !=-1) {
            System.out.print((char)b);
        }
        socket.close();
    }
}
3、上传文件

客户端:将本地文件上传到服务器。接收服务器的反馈。
服务器:接收客户端上传的文件,上传完毕之后给出反馈。

(1)客户端代码:

public class Client {
    public static void main(String[] args) throws IOException {
        //客户端连接
        Socket socket = new Socket("127.0.0.1", 10086);
        //输出数据
        OutputStream os = socket.getOutputStream();
        //读取文件
        FileInputStream fis = new FileInputStream("name_10.txt");
        int b1;
        while ((b1 = fis.read()) != -1) {
            os.write(b1);
        }
        fis.close();
        //给出数据输入结束标记
        socket.shutdownOutput();
        //接收服务端的回复
        InputStreamReader isr = new InputStreamReader(socket.getInputStream());
        int b2;
        while ((b2 = isr.read()) !=-1) {
            System.out.print((char)b2);
        }
        //关闭连接
        socket.close();
    }
}

(2)服务端代码:

public class Server {
    public static void main(String[] args) throws IOException {
        ServerSocket ss = new ServerSocket(10086);
        Socket socket = ss.accept();
        InputStream is = socket.getInputStream();
        FileOutputStream fos = new FileOutputStream("name_copy.txt");
        int b;
        while ((b = is.read()) != -1) {
            fos.write(b);
        }
        OutputStream os = socket.getOutputStream();
        os.write("收到".getBytes());
        socket.close();
        ss.close();
    }
}
4、文件名重复

在3的代码中,上传到服务器的文件名字都是相同的,这样很不方便啊。

在Java中有一个UUID类,提供了一个静态方法randomUUID(),输出是以下这种格式的数据。大概明白了为什么下载文件时名字总是一些类似的数字什么的。

对3的代码进行修改:

5 、上传文件(多线程版)

想要服务器不停止,能接收很多用户上传的图片,
该怎么做呢?
提示:可以用循环或当多线程。但是循环不合理,最优解法是(循环+多线程)改写。

首先是循环的写法:

但是这种情况下很容易造成一个用户的文件没上传完毕,下一个用户就来了,容易错过第二个用户的上传需求,有问题。

那么如何解决呢?可以使用线程。

每一个用户的需求都使用一个单独的线程进行解决。

线程的代码如下:

public class MyThread extends Thread{
    private Socket socket;
    public MyThread(Socket socket){
        this.socket = socket;
    }
    @Override
    public void run() {
        try {
            InputStream is = socket.getInputStream();
            String name = UUID.randomUUID().toString().replace("-", "");
            FileOutputStream fos = new FileOutputStream(name + ".txt");
            int b;
            while ((b = is.read()) != -1) {
                fos.write(b);
            }
            OutputStream os = socket.getOutputStream();
            os.write("收到".getBytes());
        } catch (IOException e) {
            throw new RuntimeException(e);
        } finally {
            if (socket != null) {
                try {
                    socket.close();
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
        }
    }
}

服务端的代码如下:

当服务器监测到有用户建立连接时,用创建一个线程去处理它的需求。

public class Server {
    public static void main(String[] args) throws IOException {
        ServerSocket ss = new ServerSocket(10086);
        while (true) {
            Socket socket = ss.accept();
            new MyThread(socket).start();
        }
        //ss.close();
    }
}
6、上传文件(线程池版)

代码如下:

7、BS(接收浏览器的消息并打印)

客户端:不需要写
服务器:接收数据并打印。 

在浏览器的网址中输入127.0.0.1:10086

输出结果如下:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值