Java之网络编程Socket

技术:
  • 网络编程Socket
  • 多线程Thread
  • 数据传递
具体实现:

网络编程的实质就是两个(或多个)设备(例如计算机)之间的数据传输。更具体的说,网络编程就是两个或多个程序之间的数据交换。就如我们平常的QQ聊天、打开网易APP刷新界面等,就会有新的数据从它们的服务器端里面获取出来。它们的APP就相当于客户端。还有我们打开一个浏览器,就可以得到一个对应的网页,如我们在浏览器输入一个www.baidu.com,就会显示一个百度的网页,实际上这个网页就是由一些基本数据组成的,而这些基本数据是由百度的服务器获取出来的。

  • 客户端(Client):给我们用户要用的信息就是客户端,如手机APP 浏览器 桌面QQ
  • 服务器端(Server):就是用来存储数据或者说是处理数据的地方


    38.PNG

    我们从客户端访问服务器端就是通过url地址来访问。这里可以知道服务器其实不存储数据,真正存储数据是由数据库来完成的。

  • URL:统一的资源定位(就是大家都用url来找到服务器里面的资源)
    我们通常用的url如:http://www.baidu.com/search?code=andriod&type=1
    在这里
  • http/https:传输协议 Tcp/IP协议
  • www.baidu.com :域名 表示一台网络中的电脑
  • ip地址:唯一标识一台网络中的计算机,每一台电脑都有自己独一的ip地址(ipv4 ipv6)
  • ip地址共有32位,四个部分每个部分8个字节。
    如:ip地址172.20.10.15 <-> www.baidu.com
    由于ip地址的数字有点多和长,不利于记忆。因此开发了域名系统(Domain Name System,DNS)用来将人类易于记忆的主机名(如www.12345.com)转换为数字Internet地址(如208.201.243.99)。当Java程序访问网络时,他们需要同时处理数字地址和相应的主机名。这些方法由java.net.InetAddress类提供。
  • search 对应的后台程序文件 php java jsp
  • ? 表示运行这个程序需要传递的参数
  • code=andriod code是服务器端规定的字段
  • & 如果有多个参数使用&符号链接
/**
* ip地址:唯一标识某一台电脑
 * 端口号:标识这台电脑上运行的某个程序(进程)80
 *
 * 数据传递:Tcp/IP协议
 * TCP:面向连接的 安全的  打电话
 * UDP:面向无连接的 不安全 但快
*/
39.PNG
  • 读取数据:服务器读取 客户端读取
  •      BufferedReader->InputStreamReader->socket.getInputStream();
    
  •      读取终端
    
  •      BufferedReader->InputStringReader->System.in
    
  • 输出:客户端输出 服务器端输出
  •      BufferedWriter->OutputStreamWriter->socket.getOutputStream();
    
  •      PrintStream->socket.getOutputStream();
    
注意:实现客户端和服务器端传递数据: 注意端口号必须相同 先运行Server 再运行Client。
//模拟客户端
class Client{
    public static void main(String[] args)throws IOException{
        //1.创建用于通信的socket
        //  指明和谁通信:ip地址  端口号
        Socket socket = new Socket("192.168.137.1",8989);
        //接收服务器端的数据
        BufferedReader br= new BufferedReader(new InputStreamReader(socket.getInputStream()));
        //socket.getInputStream();
        //向服务器端发送数据
        //socket.getOutputStream();
        String line = null;
        while ((line = br.readLine()) != null){
            System.out.println(line);
        }
        //客户端向服务器发送数据
        PrintStream ps = new PrintStream(socket.getOutputStream());
        ps.println("hello ");
        socket.shutdownOutput();
    }
}
//模拟服务器端
class Server{
    public static void main(String[] args)throws IOException {
      //1.创建服务器端serverSocket
        ServerSocket ss= new ServerSocket(8989);

        //2.获取连接的客户端的socket
        Socket clientSocket = ss.accept();

        //3.向客户端发送数据   对于字符的输入输出Reader  /  Writer
        //BufferedWriter br = new BufferedWriter(new OutputStreamWriter(clientSocket.getOutputStream()));
        PrintStream ps = new PrintStream(clientSocket.getOutputStream());
        ps.println("你已登陆成功");

        clientSocket.shutdownOutput();//告诉客户端输出完毕

        //4.服务器端接收客户端发来的数据
        BufferedReader br= new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
        String line = null;
        while ((line = br.readLine()) != null){
            System.out.println(line);
        }
    }
}

运行结果:


GIF.gif

这里就成功实现了客户端向服务器端输入“Hlleo”,服务器端向客户端输出“登陆成功”。实现了数据的相互传输。
我们上面这个程序代码不能实现从服务器端手动的输入输出。接下啦我们来实现服务器端的手动的输入。将输入的内容发送给客户端可以一直发。

class MyClinet{
    public static void main(String[] args){
        //连接服务器端的socket
        Socket socket = null;
        try {
             socket = new Socket("192.168.137.1",8888);
             //接收服务器端信息
            BufferedReader br=new BufferedReader(new InputStreamReader(socket.getInputStream()));
            String line =null;
            while ((line= br.readLine()) !=null){
                System.out.println(line);
            }
            //从键盘输入数据 发送给终端
        } catch (IOException e) {
            e.printStackTrace();
            System.out.println("网络出错  请重新输入");
        }finally {
            //关闭连接
            try {
                socket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}
class MyServer{
    public static void main(String[] args){
        //创建ServerSocket
        try (ServerSocket ss= new ServerSocket(8888)){
             //监听客户端
            Socket socket = ss.accept();

            //从终端接收数据
            BufferedReader keyin = new BufferedReader(new InputStreamReader(System.in));
            //获取向客户端输出数据的输出流
            PrintStream ps = new PrintStream(socket.getOutputStream());
            String line = null;
            while ((line=keyin.readLine())!= null){
                //发送给客户端
                ps.println(line);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
  • 手动收入内容 发送

  • socket扮演的是客户端

  • ServerSocket扮演的是服务器端

  • 客户端 1.文本 2.图片 3.视频 4.音频 服务器端 只要是数据都可在两者之间进行传递

    运行结果:
    GIF.gif6.gif

    在这里就实现了从服务器端的手动输入,将输入的内容发送给客户端。

    前面我们说了在客服端和服务器端可以实现 1.文本 2.图片 3.视频 4.音频等等的传递。

实现客户端向服务器端上传图片
/**
 * 客户端向服务器端发送图片
 * post上传
 */
class Client{
    public static void main(String[] args) throws IOException {
        //连接服务器 获取socket
        Socket socket = new Socket("192.168.137.1",8080);

        //创建服务器端对应的输入流 用于接收服务器端发来的数据
        BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
        System.out.println(br.readLine());

        //向服务器端发送文件(图片)
        //1.将文件写入到内存里
        String path ="C:\\Users\\DAMEI\\Desktop\\1.jpg";
        FileInputStream fis = new FileInputStream(path);

        //2.创建字节流 outputStream
        BufferedOutputStream bos = new BufferedOutputStream(socket.getOutputStream());
        byte[] buf = new byte[1024];
        int len = -1;
        while ((len = fis.read(buf)) != -1){
            bos.write(buf,0,len);
        }
        socket.shutdownOutput();
    }
}
class Server{
    public static Iterable sockets;

    public static void main(String[] args) throws IOException {
        //创建服务器端的ServerSoket
        ServerSocket ss = new ServerSocket(8080);

        //监听客户端连接
        //当有客户端来连接这个服务器 就可以得到对应的socket
        //当没有客户端来连接 服务器一直在这里等待
        Socket socket = ss.accept();

        //创建客户端对应的输出流 用于向这个客户端发送数据
        PrintStream ps = new PrintStream(socket.getOutputStream());
        ps.println("连接成功 可以发数据了!");

        BufferedInputStream bis = new BufferedInputStream(socket.getInputStream());
        String path ="D:\\android study\\yjx1\\src\\main\\java\\swu\\yjx\\day12media\\picture";
        FileOutputStream fos = new FileOutputStream(path);

        byte[] buf = new byte[1024];
        int len = -1;
        while((len = bis.read(buf)) != -1){
            fos.write(buf,0,len);
        }
    }
}

在这个程序代码里,我的电脑运行出来没有把图片给输出来,不知道是路径还是哪里错了,还得找找。基本上的代码就是这样的。
我们前面的实现只是图片和服务器手动的输入,并没有实现不仅能从客户端输入,也能从服务器端输入。

实现多线程对聊:
 * 客户端和服务器端都可以随意的发送内容
 * 从终端输入
 *
 * 实际就是客户端和服务器端在对聊
 * 客户端:
 *       主线程:接收终端的输入  将终端输入发送给服务器端
 *       子线程:接收服务器端发过来的数据
 *
 *服务器端:
 *        主线程:接收终端输入  将终端输入发送给客户端
 *        子线程:接收客户端发过来的数据

客服端:

class Client1{
    public static void main(String[] args) throws IOException {
        //连接socket
        Socket socket = new Socket("127.0.0.1",8888);

        //用一个子线程处理服务器端的数据
        new Thread(new ClientThread(socket)).start();

        //主线程处理终端输入 发送给服务器
        BufferedReader keyin = new BufferedReader(new InputStreamReader(System.in));
        PrintStream ps = new PrintStream(socket.getOutputStream());
        String line= null;

        while ((line=keyin.readLine())!=null){
            ps.println(line);
        }


    }
}

/**
 * 创建一个子线程处理客户端接收服务器端的数据
 */
class ClientThread implements Runnable{
    private Socket socket;

    //保存操作的socket对象
    public ClientThread(Socket socket){
        this.socket = socket;
    }

    @Override
    public void run() {
        BufferedReader br = null;
        try {
            //获取服务器短的输入流对象
            br = new BufferedReader(new InputStreamReader(socket.getInputStream()));

            //读取数据
            String line = null;
            while((line = br.readLine()) != null){
                System.out.println(line);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }finally {

            try {
                if(br != null) {
                    br.close();
                }
                if(socket != null){
                    socket.close();
                }
            } catch (IOException e) {
                System.out.println("网络出错,请重新登录!");
                System.exit(-1);
            }

        }

    }
}

服务器端:

class Server1{
    public static void main(String[] args) throws IOException {
        ServerSocket ss= new ServerSocket(8888);

        //获取客户端的socket
        Socket socket = ss.accept();

        //创建子线程 处理客户端输入信息
        new ServerThread(socket).start();

        //终端输入流对象
        BufferedReader keyin = new BufferedReader(new InputStreamReader(System.in));
        //客户端输出流对象
        PrintStream ps = new PrintStream(socket.getOutputStream());
        //读取终端的输入 将输入对象发送客户端
        //主线程
        String line = null;
        while ((line = keyin.readLine()) !=null){
            ps.println(line);
        }
    }
    }
/**
 *创建一个子线程用于处理服务器端接收客户端的数据
 * */
class ServerThread extends Thread{
    private  Socket socket;
    public ServerThread(Socket socket){
        this.socket=socket;
    }

    @Override
    public void run() {
        BufferedReader br = null;
        try {
           br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            String line =null;
            while ((line= br.readLine())!= null){
                System.out.println(line);
            }
        } catch (IOException e) {
            System.out.println("网络异常 请重新登陆");
            System.exit(-1);
        }finally {
            //关闭输入输出流
            //关闭对应的socket连接
            try {
                if (br!=null){
                br.close();
                }
                if (socket!=null){
                    socket.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

运行结果:


GIF.gif7.gif
实现群聊 Client和ClientThread代码和对聊不变

主要在Server里添加数组和监听客户端连接
服务器端:

class Server{
    //保存每一个连接过来的socket对象
    public static ArrayList sockets =new ArrayList<>();

    public static void main(String[] args) throws IOException {
        ServerSocket ss= new ServerSocket(6666);

        //不停的等待客户端来连接
        while (true) {
            Socket socket = ss.accept();
            //当有客户端连接过来了就保存
            sockets.add(socket);

            //开启一个线程处理每个客户端的输入
            new ServerThread(socket).start();


        }
    }
}
/**
 *创建一个子线程用于处理服务器端接收客户端的数据
 * */
class ServerThread extends Thread{
    private  Socket socket;
    public ServerThread(Socket socket){
        this.socket=socket;
    }

    @Override
    public void run() {
        BufferedReader br = null;
        try {
            br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            String line =null;
            while ((line= br.readLine())!= null){
                System.out.println(line);
            }
        } catch (IOException e) {
            System.out.println("网络异常 请重新登陆");
            System.exit(-1);
        }finally {
            //关闭输入输出流
            //关闭对应的socket连接
            try {
                if (br!=null){
                    br.close();
                }
                if (socket!=null){
                    socket.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

客户端:

class Client2{
    public static void main(String[] args) throws IOException {
        //连接socket
        Socket socket = new Socket("192.168.137.1",6666);

        //用一个子线程处理服务器端的数据
        new Thread(new ClientThread(socket)).start();

        //主线程处理终端输入 发送给服务器
        BufferedReader keyin = new BufferedReader(new InputStreamReader(System.in));
        PrintStream ps = new PrintStream(socket.getOutputStream());
        String line= null;

        while ((line=keyin.readLine())!=null){
            ps.println(line);
        }


    }
}

/**
 * 创建一个子线程处理客户端接收服务器端的数据
 */
class ClientThread implements Runnable{
    private Socket socket;
    //构造方法 保存数据对象
    public ClientThread(Socket socket){
        this.socket=socket;
    }

    @Override
    public void run() {
        BufferedReader br = null;
        try {
            br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            //读取数据
            String line = null;
            while ((line=br.readLine())!=null){
                //群发消息
                //遍历Server维护的数组
                for(Socket socket: Server.sockets){
                    //向socket对应的客户端发送消息
                    PrintStream ps = new PrintStream(socket.getOutputStream());
                }
                System.out.println(line);
            }
        } catch (IOException e) {
            System.out.println("网络异常 请重新登陆");
            e.printStackTrace();
        }finally {
            try {
                if (br != null)
                {br.close();
                } if (socket != null){
                    socket.close();}
            }
            catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

总结:以上就是Java中的基本的网络编程大概。在这里我们不仅实现了图片的上传、手动的从服务器端输入,还实现了客户端和服务器端的对聊及群聊。在这里也运用到了很多我们以前所讲到的东西。如:

  • 多线程Thread
  • while和for循环
  • run方法
  • 继承extends
  • 接口implements
  • 处理异常throws
  • 构造方法
    等等。

你可能感兴趣的:(Java之网络编程Socket)