【计网实验】实验二 :网络基础编程实验(JAVA)

实验目的

通过本实验,学习采用Socket(套接字)设计简单的网络数据收发程序,理解应用数据包是如何通过传输层进行传送的。

实验过程

1、用TCP进行数据发送的简单程序

流套接字将TCP作为其端对端协议(底层使用IP协议),提供了一个可信赖的字节流服务。

程序概述:客户端与服务器之前能进行轮流信息交换,当各自回复"bye"则关闭自身,当客户端回复"ok"则关闭服务器。

客户端代码:

package com.mwx.tcp;

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

public class TCPClient {
    public static void main(String[] args) throws Exception {
        PrintWriter socout = null;
        InputStreamReader socin = null;
        Socket socket = null;
        try {
            //1.要知道服务器的地址,端口号
            InetAddress serverIP = InetAddress.getByName("10.69.51.117");
            int port = 9999;
            //2.创建soket
            socket = new Socket(serverIP, port);

            //3.发送消息
            //3.1 从键盘读取要发送的消息
            InputStreamReader sysin = new InputStreamReader(System.in); //输入字节流(从键盘键入)
            BufferedReader sysbuf = new BufferedReader(sysin); //将输入放到缓冲区中

            //3.2客户端接收来自于服务器的消息
            socin = new InputStreamReader(socket.getInputStream());
            BufferedReader socbuf = new BufferedReader(socin);

            //3.3客户端向服务器发送消息
            socout = new PrintWriter(socket.getOutputStream());

            //4.进行通信:

            //首先是客户端向服务器发送数据,所以先等键盘键入,即获取客户端要发送的内容
            String readline = sysbuf.readLine();
            while (!readline.equals("bye")) { //如果我们键入的是 bye ,则停止连接 (在while循环中进行,即可一次连接,多次通信)
                socout.println(readline); //将从键盘键入的内容发送给服务器
                socout.flush();//flush():刷新此输出流并强制写出所有缓冲的输出字节。
                //写入的时候数据是先写入内存,然后再从内从写入到文件中,之后才能从文件中读取。所以可能写入内存写完了,但是写入文件没写完,因此为了能够即时通信,需要flush()

                System.out.println("Client:" + readline);

                //客户端接收服务器的回复,将此回复打印出来
                System.out.println("Server:" + socbuf.readLine());//readLine()方法若还没接收到任何数据,则会停在此处等待

                //收到服务器的回复后,客户端继续键盘键入要发送给服务器的数据
                readline = sysbuf.readLine();
            }

        } catch (Exception e) { //打印异常
            System.out.println("Error:" + e);
        } finally {//close方法也可能会有异常,这里没有捕获处理,而是先抛出去了
            //完成通信后关闭io和socket
            socout.close();
            socin.close();
            socket.close();
            System.out.println("客户端已关闭");
        }

    }

}

服务器代码:

package com.mwx.tcp;


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

public class TCPServer {
    public static void main(String[] args) throws IOException {
        PrintWriter socout = null;
        InputStreamReader socin = null;
        ServerSocket server = null;
        try {
            //1.建立服务器
            server = new ServerSocket(9999);

            //2.监听,等待客户端连接过来
            Socket socket = server.accept();

            //3.通信

            //从键盘读取的
            InputStreamReader sysin = new InputStreamReader(System.in); //输入字节流(从键盘键入)
            BufferedReader sysbuf = new BufferedReader(sysin); //将输入放到缓冲区中

            //接收客户端消息
            socin = new InputStreamReader(socket.getInputStream());
            BufferedReader socbuf = new BufferedReader(socin);

            //发送给客户端
            socout = new PrintWriter(socket.getOutputStream());

            //首先打印出客户端发来的信息,从socket缓冲区读取  (如果客户端一开始就回复ok的话不会停止通信)
            System.out.println("Client:" + socbuf.readLine());
            //从键盘键入服务器要回复的内容
            String readline = sysbuf.readLine();
            while (!readline.equals("bye")) { //只要服务器键入的不是 bye,就继续通信
                //将readline发送给客户端
                socout.println(readline);
                socout.flush(); //刷新缓冲区
                //打印出服务器回复了啥
                System.out.println("Server:" + readline);

                String flag = socbuf.readLine();
                if (!flag.equals("ok")) //如果从客户端接收的字符不是 ok,则打印出来,若果不是则结束循环
                    System.out.println("Client:" + flag);
                else //如果客户端发来的信息是ok,则关闭与客户端的通信
                    break;

                readline = sysbuf.readLine();
            }
        } catch (Exception e) {
            System.out.println("Error:" + e);
        } finally {
            socout.close();
            socin.close();
            server.close();
            System.out.println("服务器已关闭");
        }

    }
}

运行方式:先启动服务器,再启动客户端;客户端先发送数据,服务器再回复。

运行截图:
【计网实验】实验二 :网络基础编程实验(JAVA)_第1张图片 【计网实验】实验二 :网络基础编程实验(JAVA)_第2张图片

2、采用UDP进行数据发送的简单程序

数据报套接字使用UDP协议(底层同样使用IP协议),提供了一个"尽力而为"(best-effort)的数据报服务,应用程序可以通过它发送最长65500字节的个人信息。

程序概述:可以从键盘键入,通过客户端给服务器发送消息,服务器收到之后向客户端回复“ok”。

客户端代码:

package com.mwx.udp;

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

public class UDPClient {
    public static void main(String[] args) throws IOException{
        //建立socket
        DatagramSocket socket = new DatagramSocket();

        //从键盘读取的
        InputStreamReader sysin = new InputStreamReader(System.in); //输入字节流(从键盘键入)
        BufferedReader sysbuf = new BufferedReader(sysin); //将输入放到缓冲区中

        try {
            //准备向服务器发送数据:
            String readline = sysbuf.readLine();
            //创建数据包,参数:数据、数据长度、目的IP、端口号
            DatagramPacket clientpacket = new DatagramPacket(readline.getBytes(),readline.getBytes().length,InetAddress.getByName("10.69.51.117"),9998);
            //发送数据
            socket.send(clientpacket);

            //接收从服务器返回来的数据:
            byte[] serverdata = new byte[1024]; //存放从服务端返回的数据
            DatagramPacket serverpacket = new DatagramPacket(serverdata,serverdata.length);
            socket.receive(serverpacket);
            System.out.println("Server:"+new String(serverpacket.getData()));

        } catch (Exception e) {
            System.out.println("error:"+e);
        }finally {
            socket.close();
            sysbuf.close();
            sysin.close();
            System.out.println("客户端已关闭。");
        }

    }
}

服务器代码:

package com.mwx.udp;

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

public class UDPServer {
    public static void main(String[] args) throws IOException {
        DatagramSocket socket = new DatagramSocket(9998);
        try{
            //接收客户端的数据
            byte[] clientdata = new byte[2014];
            //创建数据包
            DatagramPacket clientpacket = new DatagramPacket(clientdata,clientdata.length);
            //接收数据
            socket.receive(clientpacket);
            System.out.println("Client:"+new String(clientpacket.getData()));

            //向可客户端返回数据
            int port = clientpacket.getPort();//获取客户端的端口号
            String respose = "ok";
            DatagramPacket serverpacket = new DatagramPacket(respose.getBytes(),respose.getBytes().length, InetAddress.getByName("10.69.51.117"),port);
            socket.send(serverpacket);

        }catch(Exception e){
            System.out.println("error;"+e);
        }finally{
            socket.close();
            System.out.println("服务器已关闭。");
        }
    }
}

运行方式:先启动服务器,再启动客户端;客户端先发送数据,服务器再回复。

运行截图:
【计网实验】实验二 :网络基础编程实验(JAVA)_第3张图片 【计网实验】实验二 :网络基础编程实验(JAVA)_第4张图片

3、多线程 \ 线程池对比

多线程

程序概述:采取多线程编程,使服务器可以同时与多台客户端产生连接并通信。在这儿采取实现Runnable接口的方式实现多线程。

服务器线程:

package com.mwx.thread;

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

public class ServerThread implements Runnable{

    private  String clientName = "";
    private Socket socket = null;
    private InputStreamReader cin = null;
    private PrintWriter cout = null;
    boolean flag = true;

    //声明构造函数,接收客户端请求socket
    public ServerThread(Socket socket,String clientName){
        this.socket = socket;
        this.clientName = clientName;
    }

    @Override
    public void run() {
        try {

            System.out.println("任务开始~");
            cin = new InputStreamReader(socket.getInputStream());
            BufferedReader socbuf = new BufferedReader(cin);

            cout = new PrintWriter(new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())),true);

            //首先打印出客户端发来的信息,从socket缓冲区读取  (如果客户端一开始就回复ok的话不会停止通信)
            System.out.println(clientName+":" + socbuf.readLine());

            while (flag) { //只要服务器键入的不是 bye,就继续通信
                //发送给客户端
                cout.println("收到~");

                String flag = socbuf.readLine();
                if (!flag.equals("ok")) //如果从客户端接收的字符不是 ok,则打印出来,若果不是则结束循环
                    System.out.println(clientName+":" + flag);
                else //如果客户端发来的信息是ok,则关闭与客户端的通信
                    break;
            }


        }catch (Exception e){
            flag = false;

            e.printStackTrace();

        }finally {
            closeSourse(socket,cin,cout);
            System.out.println("服务器已关闭");
        }

    }

    public static void closeSourse(Socket socket,InputStreamReader cin,PrintWriter cout){
        if(socket != null) {
            try {
                socket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        if(cin != null){
            try {
                cin.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        if(cout != null){
            cout.close();
        }


    }

}

服务器:

package com.mwx.thread;

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

public class Server {
    public static void main(String[] args) {
        ServerSocket serverSocket = null;
        Socket socket = null;
        boolean flag = true;

        try{
            serverSocket = new ServerSocket(9898);
            System.out.println("服务器启动:"+serverSocket);
            while(flag){
                //阻塞式等待请求
                socket = serverSocket.accept();
                System.out.println("得到客户端地址:"+socket.getInetAddress());
                String clientName = socket.toString();
				//每当接收到一个客户端的连接请求,就创建一个线程去处理该客户端的请求
                new Thread(new ServerThread(socket,clientName)).start();
            }

        }catch (Exception e){
            flag = false;
            e.printStackTrace();
        }
    }
}

客户端:(和单线程时是一样的)

package com.mwx.thread;

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

public class Client {
    public static void main(String[] args) {
        PrintWriter cout = null;
        InputStreamReader cin = null;
        Socket socket = null;
        boolean flag = true;

        try{
            socket = new Socket("10.68.164.18",9898);
            System.out.println("Socket = "+socket);


            //从键盘读取要发送的消息
            InputStreamReader sysin = new InputStreamReader(System.in); //输入字节流(从键盘键入)
            BufferedReader sysbuf = new BufferedReader(sysin); //将输入放到缓冲区中

            //用来客户端向服务器发送消息
            cout = new PrintWriter(socket.getOutputStream());

            //用来客户端接收来自于服务器的消息
            cin = new InputStreamReader(socket.getInputStream());
            BufferedReader socbuf = new BufferedReader(cin);

            //首先是客户端向服务器发送数据,所以先等键盘键入,即获取客户端要发送的内容
            String readline = sysbuf.readLine();
            while (!readline.equals("bye")) { //如果我们键入的是 bye ,则停止连接 (在while循环中进行,即可一次连接,多次通信)
                cout.println(readline); //将从键盘键入的内容发送给服务器
                cout.flush();//flush():刷新此输出流并强制写出所有缓冲的输出字节。
                //写入的时候数据是先写入内存,然后再从内从写入到文件中,之后才能从文件中读取。所以可能写入内存写完了,但是写入文件没写完,因此为了能够即时通信,需要flush()

                System.out.println("Client:" + readline);

                //客户端接收服务器的回复,将此回复打印出来
                System.out.println("Server:" + socbuf.readLine());//readLine()方法若还没接收到任何数据,则会停在此处等待

                //收到服务器的回复后,客户端继续键盘键入要发送给服务器的数据
                readline = sysbuf.readLine();
            }
        }catch (Exception e){

            e.printStackTrace();
        }finally {
            closeSourse(socket,cin,cout);
            System.out.println("客户端已关闭");
        }

    }

    public static void closeSourse(Socket socket,InputStreamReader cin,PrintWriter cout){
        if(socket != null) {
            try {
                socket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        if(cin != null){
            try {
                cin.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        if(cout != null){
            cout.close();
        }
    }
}

运行方式:先启动服务器,然后分别启动两个客户端,从客户端发送请求,即可接受到服务器的响应。

运行截图:
【计网实验】实验二 :网络基础编程实验(JAVA)_第5张图片
【计网实验】实验二 :网络基础编程实验(JAVA)_第6张图片
【计网实验】实验二 :网络基础编程实验(JAVA)_第7张图片

线程池

程序概述:这个程序依然是实现一个服务器可并行处理多个客户端的请求,只是在创建线程的时候使用了线程池技术,线程池里存放的是已经创建好的线程,当需要创建线程时,就从线程池里取,当需要关闭线程时就将线程归还给线程池,这样可以避免频繁的创建和销毁线程。

服务器线程:(和多线程情况下是一样的,依然是实现Runnable接口)

package com.mwx.threadPool;


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

public class ServerThread implements Runnable{

    private Socket socket = null;
    private InputStreamReader cin = null;
    private PrintWriter cout = null;
    boolean flag = true;

    //声明构造函数,接收客户端请求socket
    public ServerThread(Socket socket){
        this.socket = socket;
    }

    @Override
    public void run() {
        try {

            System.out.println("任务开始~");
            cin = new InputStreamReader(socket.getInputStream());
            BufferedReader socbuf = new BufferedReader(cin);

            cout = new PrintWriter(new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())),true);

            //首先打印出客户端发来的信息,从socket缓冲区读取  (如果客户端一开始就回复ok的话不会停止通信)
            System.out.println("Client:" + socbuf.readLine());

            while (flag) { //只要服务器键入的不是 bye,就继续通信
                //发送给客户端
                cout.println("收到~");

                String flag = socbuf.readLine();
                if (!flag.equals("ok")) //如果从客户端接收的字符不是 ok,则打印出来,若果不是则结束循环
                    System.out.println("Client:" + flag);
                else //如果客户端发来的信息是ok,则关闭与客户端的通信
                    break;
            }


        }catch (Exception e){
            flag = false;

            e.printStackTrace();

        }finally {
            closeSourse(socket,cin,cout);
            System.out.println("服务器已关闭");
        }

    }

    public static void closeSourse(Socket socket,InputStreamReader cin,PrintWriter cout){
        if(socket != null) {
            try {
                socket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        if(cin != null){
            try {
                cin.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        if(cout != null){
            cout.close();
        }


    }

}

服务器:

package com.mwx.threadPool;

import com.mwx.thread.ServerThread;
import jdk.net.Sockets;

import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Server {
    public static void main(String[] args) {
        ServerSocket serverSocket = null;
        Socket socket= null;
        ExecutorService executorService = null;
        boolean flag = true;
		//创建容量为2的线程池
        executorService = Executors.newFixedThreadPool(2);

        try {
            serverSocket = new ServerSocket(9797);
            System.out.println("服务器启动:"+serverSocket);

            while(flag){
                //阻塞式等待请求
                socket = serverSocket.accept();
                System.out.println("得到客户端地址:"+socket.getInetAddress());
                String clientName = socket.toString();

                ServerThread serverThread = new ServerThread(socket,clientName);
                executorService.execute(serverThread);

            }


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

    }

}

客户端:(与前述一样)

package com.mwx.threadPool;


import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;

public class Client {
    public static void main(String[] args) {
        PrintWriter cout = null;
        InputStreamReader cin = null;
        Socket socket = null;
        boolean flag = true;

        try{
            socket = new Socket("10.63.185.101",9898);
            System.out.println("Socket = "+socket);


            //从键盘读取要发送的消息
            InputStreamReader sysin = new InputStreamReader(System.in); //输入字节流(从键盘键入)
            BufferedReader sysbuf = new BufferedReader(sysin); //将输入放到缓冲区中

            //用来客户端向服务器发送消息
            cout = new PrintWriter(socket.getOutputStream());

            //用来客户端接收来自于服务器的消息
            cin = new InputStreamReader(socket.getInputStream());
            BufferedReader socbuf = new BufferedReader(cin);

            //首先是客户端向服务器发送数据,所以先等键盘键入,即获取客户端要发送的内容
            String readline = sysbuf.readLine();
            while (!readline.equals("bye")) { //如果我们键入的是 bye ,则停止连接 (在while循环中进行,即可一次连接,多次通信)
                cout.println(readline); //将从键盘键入的内容发送给服务器
                cout.flush();//flush():刷新此输出流并强制写出所有缓冲的输出字节。
                //写入的时候数据是先写入内存,然后再从内从写入到文件中,之后才能从文件中读取。所以可能写入内存写完了,但是写入文件没写完,因此为了能够即时通信,需要flush()

                System.out.println("Client:" + readline);

                //客户端接收服务器的回复,将此回复打印出来
                System.out.println("Server:" + socbuf.readLine());//readLine()方法若还没接收到任何数据,则会停在此处等待

                //收到服务器的回复后,客户端继续键盘键入要发送给服务器的数据
                readline = sysbuf.readLine();
            }
        }catch (Exception e){

            e.printStackTrace();
        }finally {
            closeSourse(socket,cin,cout);
            System.out.println("客户端已关闭");
        }

    }

    public static void closeSourse(Socket socket,InputStreamReader cin,PrintWriter cout){
        if(socket != null) {
            try {
                socket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        if(cin != null){
            try {
                cin.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        if(cout != null){
            cout.close();
        }
    }
}

运行过程与截图均与多线程实现是一样的。

4、能实现文件上传的简单聊天程序

服务器

package com.mwx.chat;

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

public class Server {
    public static void main(String[] args) {
        try {
            ServerSocket serverSocket = new ServerSocket(6464);
            System.out.println("~~~~~~ 服务器已启动 ~~~~~~");

            //while(true){
                System.out.println("等待接收文件~");
                Socket socket = serverSocket.accept();
                System.out.println(socket.toString()+"已连接");

                new Thread(new ServerThread(socket)).start();
         //   }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

服务器线程

package com.mwx.chat;

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

public class ServerThread implements Runnable{

    private Socket socket = null;
    private InputStream inputStream ;

    public ServerThread(Socket socket){
        this.socket = socket;
        try {
            inputStream = socket.getInputStream();
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    @Override
    public void run() {
        //1.使用DataInputStream包装输入流
        DataInputStream dataInputStream = new DataInputStream(inputStream);

        try {
            String fileName = dataInputStream.readUTF();
            System.out.println("文件名为:"+fileName);

            //往某个位置写入文件
            FileOutputStream fileOutputStream = new FileOutputStream("C:"+ File.separator+fileName);
            int c = -1;
            while((c = dataInputStream.read()) != -1){
                fileOutputStream.write(c);
                fileOutputStream.flush();
            }

            System.out.println("文件上传成功!");
           close(socket,inputStream,dataInputStream);
        } catch (IOException e) {
            e.printStackTrace();
            close(socket,inputStream,dataInputStream);
        }

    }

    public static  void close(Socket socket,InputStream inputStream,DataInputStream dataInputStream){
        try {
            socket.close();
            inputStream.close();
            dataInputStream.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

客户端

package com.mwx.chat;

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

public class Client {
    public static void main(String[] args) {
        File file = new File("D:\\计网知识点大纲.png");

        try {
            Socket socket = new Socket("localhost",6464);

            OutputStream outputStream = socket.getOutputStream();

            DataOutputStream dataOutputStream = new DataOutputStream(outputStream);
            //向服务器端传文件名
            dataOutputStream.writeUTF(file.getName());
            dataOutputStream.flush();

            //向服务器端传文件,通过字节流
            //字节流先读取硬盘文件
            BufferedInputStream bufferedInputStream = new BufferedInputStream(new FileInputStream(file));

            int c = -1;
            while((c = bufferedInputStream.read()) != -1){
                //将读取的文件以字节方式写入DataOutputStream,之后传输到服务端
                dataOutputStream.write(c);
                dataOutputStream.flush();
            }
            System.out.println("文件已发送~");
            close(socket,outputStream,dataOutputStream,bufferedInputStream);
        } catch (IOException e) {
            e.printStackTrace();

        }

    }

    public static void close(Socket socket,OutputStream outputStream,DataOutputStream dataOutputStream, BufferedInputStream bufferedInputStream){
        try {
            socket.close();
            outputStream.close();
            dataOutputStream.close();
            bufferedInputStream.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}

运行截图:
【计网实验】实验二 :网络基础编程实验(JAVA)_第8张图片
【计网实验】实验二 :网络基础编程实验(JAVA)_第9张图片

参考

1、基于JAVA的Socket类的TCP网络编程
2、基于JAVA的socket类的UDP网络编程
3、【狂神说Java】网络编程实战讲解
4、Java基础-IO框架
5、【狂神说Java】多线程详解

你可能感兴趣的:(计算机网络实验,网络,java,socket)