计算机网络编程

news2024/12/26 10:57:14

网络编程

Java 是第一大编程语言和开发平台。它有助于企业降低成本、缩短开发周期、推动创新以及改善应用服务。如今全球有数百万开发人员运行着超过 51 亿个 Java 虚拟机, Java 仍是企业和开发人员的首选开发平台。
  

课程内容的介绍

1. 计算机网络基础
2. Socket ServerSocket
3. TCP Socket 通信模型
4. UDP 编程
  

一、计算机网络基础

1.什么是计算机网络
把分布在不同地理区域的计算机与专门的外部设备用通信线路互连成一个规模大 、功能强的网络系统,从而使众多的计算机可以方便地互相传递信息,共享硬件、软件、数据信息等资源。

  
2.了解网络通信协议
2.1.网络通信协议
要使计算机连成的网络能够互通信息,需要对数据传输速率、传输代码、代码结构、传输控制步骤、出错控制等制定一组标准,这一组共同遵守的通信标准就是网络通信协议,不同的计算机之间必须使用相同的通讯协议才能进行通信。

  
2.2 常见的网络通信协议
TCP/IP, 使用最为广泛的通讯协议。
TCP/IP 是英文 Transmission Control Protocol/Internet Protocol 的缩写,意思是 传输控制协议 / 网际协议”
  

    
2.2.1三次握手
TCP 协议建立连接需要三次会话 ( 握手 )

   
1. 第一次握手,客户端连接服务器。
2. 第二次握手,服务器接收了客户端的连接请求,服务器发送给客户端的确认消息。
3. 第三次握手,客户端获取到了服务器的确认信息,知道了服务已经知道我要连接他了,但是服务器还不知道客户端已经知道了,所以客户端发送给服务器消息说我们已经接受到了你的确认信息。
经过这三次握手,那么客户端和服务器都知道了要连接。
  
2.2.2四次挥手
TCP 协议中如果客户端要断开连接那么需要进行四次挥手操作。

  
1. 第一次,客户端发送请求关闭的消息给服务器。
2. 第二次,服务器接受到了客户端的消息,服务器发送消息给客户端确认(我收到了你的关闭请求,但是我数据还没有传完,等我传会告诉你的)。
3. 第三次,服务器给客户端要传输的数据以及传完了,服务器发送消息给客户端数据传完了,客户端你可以断开连接了。
4. 第四次,客户端发送消息给服务器,我要断开了,你也断开吧。
  
3.IP和端口号
3.1 什么是IP
网络中每台计算机的一个标识号,是一个逻辑地址, 127.0.0.1 localhost 代表本机地址。
IP 地址由四段组成,每个字段是一个字节, 8 位,最大值是 255 ,IP地址由两部分组成,即 网络地址 主机地址 。网络地址表示其属于互联网的哪一个网络,主机地址表示其属于该网络中的哪一台主机。二者是主从关系。IP 地址的四大类型标识的是网络中的某台主机。IPv4的地址长度为 32 位,共 4 个字节,但实际中我们用 点分十进制 记法。 192.168.1.1
   
3.2 分类
IP 地址根据网络号和主机号来分,分为 A B C 三类及特殊地址 D E 0 和全 1 的都保留不用。
A (1.0.0.0-126.0.0.0) (默认子网掩码: 255.0.0.0 0xFF000000 )第一个字节为网络号,后三个字节为主机号。该类IP 地址的最前面为 “0” ,所以地址的网络号取值于 1~126 之间。一般用于大型网络。
B (128.0.0.0-191.255.0.0) (默认子网掩码: 255.255.0.0 0xFFFF0000 )前两个字节为网络号,后两个字节为主机号。该类IP 地址的最前面为 “10” ,所以地址的网络号取值于 128~191 之间。一般用于中等规模网络。
C (192.0.0.0-223.255.255.0) (子网掩码: 255.255.255.0 0xFFFFFF00 )前三个字节为网络号,最后一个字节为主机号。该类IP 地址的最前面为 “110” ,所以地址的网络号取值于 192~223 之间。一般用于小型网络。
D :是多播地址。该类 IP 地址的最前面为 “1110” ,所以地址的网络号取值于 224~239 之间。一般用于多路广播用户[1]
E :是保留地址。该类 IP 地址的最前面为 “1111” ,所以地址的网络号取值于 240~255 之间。
  

     
Java 中对 IP 的操作。
package com.bobo.ip;

import java.net.InetAddress;

public class IpDemo01 {

    /**
     * Java中对IP的封装操作
     * @param args
     */
    public static void main(String[] args) throws Exception {
        System.out.println(InetAddress.getLocalHost());
        System.out.println(InetAddress.getByName("127.0.0.1"));

    }
}
    
科普:为什么局域网中的 ip 地址都是 192.168 开头的?
私有地址
A 类地址: 10.0.0.0 - 10.255.255.255
B 类地址: 172.16.0.0 - 172.31.255.255
C 类地址: 192.168.0.0 - 192.168.255.255
    

3.3 IPv4IPv6
目前的全球因特网所采用的协议族是TCP/IP协议族。IP是TCP/IP协议族中网络层的协议,是TCP/IP协议族的核心协议。目前IP协议的版本号是4(简称为IPv4),发展至今已经使用了30多年。
IPv4的地址位数为32位,也就是最多有2的32次方的电脑可以联到Internet上。
近十年来由于互联网的蓬勃发展,IP位址的需求量愈来愈大,使得IP位址的发放愈趋严格,各项资料显示全球IPv4位址可能在2005至2008年间全部发完。
  
什么是IPv6?
IPv6是下一版本的互联网协议,也可以说是下一代互联网的协议,它的提出最初是因为随着互联网的迅速发展,IPv4定义的有限地址空间将被耗尽,地址空间的不足必将妨碍互联网的进一步发展。
为了扩大地址空间,拟通过IPv6重新定义地址空间。IPv6采用128位地址长度,几乎可以不受限地提供地址。按保守方法估算IPv6实际可分配的地址,整个地球的每平方米面积上仍可分配1000多个地址。在IPv6的设计过程中除了一劳永逸地解决了地址短缺问题以外,还考虑了在IPv4中解决不好的其它问题,主要有端到端IP连接、服务质量(QoS)、安全性、多播、移动性、即插即用等。
  
IPv6与IPv4相比有什么特点和优点?
更大的地址空间。IPv4中规定IP地址长度为32,即有2^32-1个地址;而IPv6中IP地址的长度为128,即有2^128-1个地址。
更小的路由表。IPv6的地址分配一开始就遵循聚类(Aggregation)的原则,这使得路由器能在路由表中用一条记录(Entry)表示一片子网,大大减小了路由器中路由表的长度,提高了路由器转发数据包的速度。
增强的组播(Multicast)支持以及对流的支持(Flow-control)。这使得网络上的多媒体应用有了长足发展的机会,为服务质量(QoS)控制提供了良好的网络平台。
加入了对自动配置(Auto-configuration)的支持。这是对DHCP协议的改进和扩展,使得网络(尤其是局域网)的管理更加方便和快捷。
更高的安全性.在使用IPv6网络中用户可以对网络层的数据进行加密并对IP报文进行校验,这极大的增强了网络安全。
  
3.4 端口号
具有网络功能的应用软件的标识号。
特点:
1. 端口是一个软件结构,被客户程序或服务程序用来发送和接收数据,一台服务器有 256*256 个端口。
2. 0-1023 是公认端口号,即已经公认定义或为将要公认定义的软件保留的。
3. 1024-65535 是并没有公共定义的端口号,用户可以自己定义这些端口的作用。
4. 端口与协议有关: TCP UDP 的端口互不相干。
  

二、网络通信

1.SocketServerSocket
1.1 什么是Socket
两个应用程序可以通过一个双向的网络通信连接实现数据的交换,这个双向的链路的一端我们称为一个Socket
  
1.2 Socket案例
1.2.1 基本连接操作
   
服务端的创建
package com.bobo.socket01;

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

public class TestServerSocket {

    /**
     * Socket的第一个案例
     *
     * @param args
     */
    public static void main(String[] args) {

        try {
            // 创建一个ServerScoket对象  如果服务器关闭了,那么我们是连接不上的
            ServerSocket ss = new ServerSocket(8888);
            System.out.println("服务器启动了....");
            // 接受客户端的访问
            while(true){
                Socket s = ss.accept(); // 这是一个阻塞的方法,等待客户端的访问。
                System.out.println(s.getInetAddress().getHostAddress() + " : 客户端来访问了");
                s.close();
            }

        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
  
客户端的创建
package com.bobo.socket01;

import java.net.Socket;
import java.rmi.server.ExportException;

public class TestClientSocket {

    /**
     * Socket 客户端程序
     * @param args
     */
    public static void main(String[] args) {
        try{
            // 127.0.0.1 / localhost 表示的当前的主机
            // 获取一个Socket对象 指定要访问的服务器 ip+端口 可以定位到要访问的是哪台计算机上的服务
            Socket socket = new Socket("192.168.8.71",8888);
            System.out.println("客户端开始链接了...");
            socket.close();
        }catch (Exception e){
            e.printStackTrace();
        }

    }
}
  
当前计算机的描述
1. 127.0.0.1
2. localhost
3. 当前电脑分配的 IP(192.168.8.71)
  
1.2.2 客户端发送消息给服务器
通过字节输入输出流实现。
  
服务端代码
package com.bobo.socket02;

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

public class TestServerSocket {

    /**
     * 服务端 接收客户端的消息
     * @param args
     */
    public static void main(String[] args) {
        try {
            ServerSocket ss = new ServerSocket(8899);
            while(true){
                // 等待获取客户端的连接
                Socket s = ss.accept();
                System.out.println(s.getInetAddress().getHostAddress()+"连接了...");
                // 获取一个字节输入流,获取客户端传递的信息
                InputStream inputStream = s.getInputStream();
                byte[] b = new byte[1024];
                int num = 0 ;
                while((num = inputStream.read(b)) != -1){
                    System.out.println(new String(b,0,num));
                }
                // 关闭相关的资源
                inputStream.close();
                s.close();
            }
        }catch (Exception e){
            e.printStackTrace();
        }

    }
}
     
客户端代码
package com.bobo.socket02;

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

public class TestClientSocket {

    /**
     * 客户端
     *    发送消息到服务器
     * @param args
     */
    public static void main(String[] args) {
        try {
            Socket socket = new Socket("localhost",8899);
            // 通过socket对象获取一个字节输出流
            OutputStream outputStream = socket.getOutputStream();
            outputStream.write("服务器,你好啊,我是XXXXX".getBytes());
            outputStream.close();
            socket.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
  
效果:

    
用缓冲流来操作。
  
服务端:
package com.bobo.socket03;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;

public class TestServerSocket {

    /**
     * 服务端 接收客户端的消息
     * @param args
     */
    public static void main(String[] args) {
        try {
            ServerSocket ss = new ServerSocket(8899);
            while(true){
                // 等待获取客户端的连接
                Socket s = ss.accept();
                System.out.println(s.getInetAddress().getHostAddress()+"连接了...");
                BufferedReader bufferedReader = new BufferedReader(
                        new InputStreamReader(s.getInputStream()));
                System.out.println(bufferedReader.readLine());
                bufferedReader.close();
                s.close();
            }
        }catch (Exception e){
            e.printStackTrace();
        }

    }
}
     
客户端代码
package com.bobo.socket03;

import java.io.BufferedWriter;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.Socket;

public class TestClientSocket {

    /**
     * 客户端
     *    发送消息到服务器
     * @param args
     */
    public static void main(String[] args) {
        try {
            Socket socket = new Socket("localhost",8899);
            // 通过socket对象获取一个字节输出流
            BufferedWriter bufferedWriter = new BufferedWriter(
                    new OutputStreamWriter(socket.getOutputStream()));
            bufferedWriter.write("哈哈哈哈哈....");
            bufferedWriter.newLine(); // 必须要显示的调用
            bufferedWriter.flush();
            bufferedWriter.close();
            socket.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
  
输出效果

  
1.2.3 服务端发送消息给客户端
  
服务端代码:
package com.bobo.socket04;

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

public class TestServerSocket {

    /**
     * 服务端
     *    发送消息给客户端
     * @param args
     */
    public static void main(String[] args) {
        try {
            ServerSocket ss = new ServerSocket(8989);
            while(true){
                Socket s = ss.accept();
                System.out.println(s.getInetAddress().getHostAddress()+" 连接了");
                // 服务器发送消息给客户端
                BufferedWriter bufferedWriter = new BufferedWriter(
                        new OutputStreamWriter(s.getOutputStream()));
                bufferedWriter.write(s.getInetAddress().getHostAddress()+" 你好啊,你的连接我已经收到了");
                bufferedWriter.newLine();
                bufferedWriter.flush();
                bufferedWriter.close();
                s.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
    
客户端代码
package com.bobo.socket04;

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

public class TestClientSocket {

    /**
     * 客户端 接收服务的消息
     * @param args
     */
    public static void main(String[] args) {
        try {
            Socket s  = new Socket("localhost",8989);
            System.out.println("客户端启动了...");
            BufferedReader bufferedReader = new BufferedReader(
                    new InputStreamReader(s.getInputStream()));
            System.out.println(bufferedReader.readLine());
            bufferedReader.close();
            s.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
   
输出结果:

    
1.2.4 课堂案例
客户端接收键盘输入信息,然后将该信息发送给服务器,服务器读取客户端发送来的信息。
    
服务端代码
package com.bobo.socket05;

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

public class TestServerSocket {

    /**
     * 服务端
     *     接收客户端的消息
     * @param args
     */
    public static void main(String[] args) {
        try {
            ServerSocket ss = new ServerSocket(9999);
            while(true){
                Socket s = ss.accept();
                BufferedReader bufferedReader = new BufferedReader(
                        new InputStreamReader(s.getInputStream()));
                System.out.println(s.getInetAddress().getHostAddress()+":"+bufferedReader.readLine());
                bufferedReader.close();
                s.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
    
客户端代码
package com.bobo.socket05;

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

public class TestClientSocket {

    /**
     * 客户端接收键盘输入信息,然后将该信息发送给服务器,服务器读取客户端发送来的信息
     * @param args
     */
    public static void main(String[] args) {
        try {
            Socket socket = new Socket("localhost",9999);
            System.out.println("客户端启动了,请输入要发送的消息:");
            // 接收键盘输入
            BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));

            // 客户端发送消息
            BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
            bufferedWriter.write(bufferedReader.readLine());
            bufferedWriter.newLine();
            bufferedWriter.flush();
            bufferedWriter.close();
            bufferedReader.close();
            socket.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
  
效果

  
1.2.5 一对一聊天实现
实现一个客户端发送消息给服务器,然后服务器获取到消息后再返还消息给客户端,一对一通信的效果。
    
服务端代码:
package com.bobo.socket06;

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

public class TestServerSocket {

    /**
     * 服务端
     * @param args
     */
    public static void main(String[] args) {
        try {
            ServerSocket ss = new ServerSocket(8080);
            while(true){
                Socket s = ss.accept();
                // 服务端通过键盘录入的方式 返回消息给客户端
                BufferedReader input = new BufferedReader(new InputStreamReader(System.in));
                // 读取客户端传递的消息
                BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream()));
                // 返回消息给客户端
                BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));
                // 实现频繁读取写入的效果
                while(true){
                    System.out.println(s.getInetAddress().getHostAddress()+":" + br.readLine()) ;
                    // 返回信息给客户端q
                    bw.write(input.readLine());
                    bw.newLine();
                    bw.flush();
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
      
客户端代码:
package com.bobo.socket06;

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

public class TestClientSocket {

    /**
     * 客户端
     *    实现和服务器一对一的聊天
     *
     * @param args
     */
    public static void main(String[] args) {
        try {
            Socket socket = new Socket("localhost",8080);
            // 接收键盘输入
            BufferedReader input = new BufferedReader(new InputStreamReader(System.in));
            // 读取信息
            BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            // 写入信息
            BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));

            while(true){
                bw.write(input.readLine());
                bw.newLine();
                bw.flush();
                System.out.println("服务器说:" + br.readLine());
            }

        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
  
效果:

  
效果虽然是实现了,但是总的体验还是不是很好,因为是在读写在一个线程中,就造成了读写操作的阻塞。
  
1.2.6 自由聊天的实现
在上面的案例中客户端和服务器的读写操作同一个线程中进行的,所以会造成消息的阻塞,那么我们可以结合前面讲过的多线程的知识来解决当前的问题。将读的操作交给一个线程,将写的操作交给一个线程,那么读写之间就没有阻塞现象了。具体实现如下:
    
读操作的线程:
package com.bobo.socket07.thread;

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

/**
 * 专门用来负责读取数据的线程
 *     线程只需要获取到对应的Socket对象
 *     就可以获取Socket中的数据及向Socket中写入数据
 */
public class ReadSocketThread implements Runnable{

    private Socket socket;

    public ReadSocketThread(Socket socket) {
        this.socket = socket;
    }

    @Override
    public void run() {
        try {
            BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            while(true){
                String str = br.readLine();
                System.out.println(str);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
    
写操作的线程
package com.bobo.socket07.thread;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.Socket;

/**
 * 专门用来发送数据的线程
 */
public class WriterSocketThread implements Runnable {

    private Socket socket;

    public WriterSocketThread(Socket socket) {
        this.socket = socket;
    }

    @Override
    public void run() {
        try{
            // 获取键盘数据
            BufferedReader input = new BufferedReader(new InputStreamReader(System.in));
            // 输出信息
            BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
            while(true){
                String str = input.readLine();
                bw.write(str);
                bw.newLine();
                bw.flush();
            }
        }catch (Exception e){

        }
    }
}
    
服务端代码
package com.bobo.socket07;


import com.bobo.socket07.thread.ReadSocketThread;
import com.bobo.socket07.thread.WriterSocketThread;

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

public class TestServerSocket {

    /**
     * 服务器
     *    客户端和服务器实现自由聊天
     * @param args
     */
    public static void main(String[] args) {
        try {
            ServerSocket ss = new ServerSocket(7891);
            while(true){
                Socket s = ss.accept();
                // 读客户端数据和写客户端数据都交给子线程去处理
                // 读取数据的线程
                new Thread(new ReadSocketThread(s)).start();
                // 写入数据的线程
                new Thread(new WriterSocketThread(s)).start();
                // 读写操作分别交给两个不同的线程来处理,就不会出现 读取必须要等待输入完成后再执行的情况
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
  
客户端代码
package com.bobo.socket07;

import com.bobo.socket07.thread.ReadSocketThread;
import com.bobo.socket07.thread.WriterSocketThread;

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

public class TestClientSocket {

    /**
     * 实现客户端和服务自由聊天
     * @param args
     */
    public static void main(String[] args) {
        try {
            Socket socket = new Socket("localhost",7891);
            // 读取数据的线程
            new Thread(new ReadSocketThread(socket)).start();
            new Thread(new WriterSocketThread(socket)).start();

        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
  
效果

    
1.2.7 有退出的自由聊天
上面的案例除了显示的把服务关掉之外程序会一直运行,那么如果我们要显示的断开聊天,我们只需要在读写线程中添加断开的逻辑即可。
    
读取数据的线程
package com.bobo.socket08.thread;

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

public class ReadSocketThread implements Runnable{

    private Socket socket;

    public ReadSocketThread(Socket socket) {
        this.socket = socket;
    }

    @Override
    public void run() {
        try {
            BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            while(true){
                String str = br.readLine();
                System.out.println(socket.getInetAddress().getHostAddress()+":" + str);
                // 添加退出聊天的逻辑
                if("exit".equals(str)){ // 表示退出
                    System.out.println(socket.getInetAddress().getHostAddress()+"退出了...");
                    br.close();
                    break; // 结束循环,终止线程
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
    
写入数据的线程
package com.bobo.socket08.thread;

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

public class WriterSocketThread implements Runnable{

    private Socket socket;

    public WriterSocketThread(Socket socket) {
        this.socket = socket;
    }

    @Override
    public void run() {

        try {
            BufferedReader input = new BufferedReader(new InputStreamReader(System.in));
            BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
            while(true){
                String str = input.readLine();
                bw.write(str);
                bw.newLine();
                bw.flush();
                // 如果我发送的是 exit 的话,那么读到这个信息的 会退出,此时发送也应该要退出
                if("exit".equals(str)){
                    // 读取数据的线程也要退出
                    System.out.println(socket.getInetAddress().getHostAddress()+"退出了");
                    bw.close();
                    input.close();
                    break;
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
  

三、TCP Socket 通信模型

1.通信原理

   
2.课堂案例
实现 TCP 通信模型实现客户端 1 1 的通信,实现案例。
读写的线程和上面案例是一样的,就不再复制了。
    
客户端代码
package com.bobo.socket09;

import com.bobo.socket09.thread.ReadSocketThread;
import com.bobo.socket09.thread.WriterSocketThread;

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

public class TestClientSocket {

    /**
     * 实现客户端对客户端的一对一聊天
     * @param args
     */
    public static void main(String[] args) {
        try {
            Socket socket = new Socket("localhost",7799);
            new Thread(new ReadSocketThread(socket)).start(); // 启动读取信息的线程
            new Thread(new WriterSocketThread(socket)).start();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
    
服务器代码
package com.bobo.socket09;

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;

public class TestServerSocket {

    /**
     * ServerSocket
     *    模拟客户端和客户端一对一的聊天
     * @param args
     */
    public static void main(String[] args) {
        // 记录以及建立连接的Socket
        List<Socket> list = new ArrayList<>();
        try {
            ServerSocket ss = new ServerSocket(7799);
            for(int i = 0 ; i < 2 ; i++){
                Socket s = ss.accept();
                // 客户端建立连接后 将连接对应的Socket对象保存起来
                list.add(s);
            }
            // 第一个Socket接收到信息之后 应该将信息在第二个Socket中输出
            // 第二个Socket接收到信息之后 应该将信息在第一个Socket中输出
            new Thread(new ServerThread(list.get(0),list.get(1))).start();
            new Thread(new ServerThread(list.get(1),list.get(0))).start();

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

/**
 * ServerSocket端的线程
 *   专门处理信息的中转
 */
class ServerThread implements  Runnable{

    private Socket s1 ; // 发送者
    private Socket s2 ; // 接收者

    public ServerThread(Socket s1, Socket s2) {
        this.s1 = s1;
        this.s2 = s2;
    }

    @Override
    public void run() {
        try {
            while(true){
                BufferedReader br = new BufferedReader(new InputStreamReader(s1.getInputStream()));
                // 获取 发送者发送的消息
                String str = br.readLine();
                // 将消息转发给另一个客户端
                BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(s2.getOutputStream()));
                bw.write(str);
                bw.newLine();
                bw.flush();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
  
IDEA 中默认是不支持同一个主方法执行多次的,要并发执行需要如下设置。

    
3.文件上传下载
文件上传:客户端将 File 传输给服务器, File 会保存在服务中。
文件下载:客户端从服务器获取 File
   
第一种实现的方式:
package com.bobo.socket10;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;

public class TestServerSocket {

    /**
     * ServerSocket
     *    接收客户端传递的文件
     * @param args
     */
    public static void main(String[] args) {
        try {
            ServerSocket ss = new ServerSocket(9999);
            while(true){
                Socket s = ss.accept();
                // 读取文件
                BufferedInputStream bi = new BufferedInputStream(s.getInputStream());
                // 将上传的文件保存到服务指定的位置
                BufferedOutputStream bo = new BufferedOutputStream(new FileOutputStream("D:/aaa.txt"));
                // 又是一个文件复制
                byte[] b = new byte[1024*1024];
                int num = 0;
                while((num = bi.read(b)) != -1){
                    bo.write(b,0,num);
                }
                bi.close();
                bo.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
package com.bobo.socket10;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.Socket;

public class TestClientSocket {


    /**
     * Client
     *    发送文件到服务器中  上传操作
     * @param args
     */
    public static void main(String[] args) {
        try {
            Socket socket = new Socket("localhost",9999);
            // 先读取某个文件
            BufferedInputStream bi = new BufferedInputStream(new FileInputStream("d:/IO/1.mp4"));
            // 将读取的文件写入到Socket中
            BufferedOutputStream bo = new BufferedOutputStream(socket.getOutputStream());
            // 文件的复制
            byte[] b = new byte[1024*1024];
            int num = 0 ;
            //bo.write("1.mp4".getBytes());
            while((num = bi.read(b)) != -1){
                bo.write(b,0,num);
            }
            bo.close();
            bi.close();
        } catch (IOException e) {
            e.printStackTrace();
        }

    }
}
  
这种实现方式我们发现服务端可以获取到客户端传递的数据,但是并不清楚传递的是什么类型的数据,名称叫什么也不知道,所以处理起来比较麻烦。
package com.bobo.socket11;

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

public class TestClientSocket {

    /**
     * 客户端
     * @param args
     */
    public static void main(String[] args) {
        try {
            Socket socket = new Socket("localhost",9988);
            File file = new File("d:/IO/1.mp4");
            // 读取文件
            BufferedInputStream bi = new BufferedInputStream(new FileInputStream(file));
            // 文件写入Socket ObjectOutputStream
            ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream());
            // 先写入文件名称
            oos.writeUTF(file.getName());
            oos.writeLong(file.length());
            // 文件的传输
            byte[] b = new byte[1024*1024];
            int num = 0;
            while((num = bi.read(b)) != -1){
                oos.write(b,0,num);
            }
            // 关闭资源
            oos.close();
            bi.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
package com.bobo.socket11;

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

public class TestServerSocket {

    /**
     * 服务端
     * @param args
     */
    public static void main(String[] args) {
        try {
            ServerSocket ss = new ServerSocket(9988);
            while(true){
                Socket s = ss.accept();
                // 读取文件 ObjectInputStream
                ObjectInputStream bis = new ObjectInputStream(s.getInputStream());
                // 获取文件名称
                String fileName = bis.readUTF();
                // 获取文件的大小
                long length = bis.readLong();
                System.out.println("文件名称:" + fileName + "文件大小:" + length);

                // 保存文件到服务器
                BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(new File("d:/", fileName)));
                byte[] b = new byte[1024*1024];
                int num = 0;
                int i = 1 ;
                while((num = bis.read(b)) != -1){
                    bos.write(b,0,num);
                    System.out.println("保存的进入:" + (1024*1024*i)/length * 100 + "%" );
                    i++;
                }
                bis.close();
                bos.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
   

四、了解UDP

1.UDP协议
UDP(User Datagram Protocol) 用户数据报协议, UDP TCP 位于同一层 - 传输层,但它对于数据包的顺序错误或重发没有TCP 来的可靠。
TCP: 相当于打电话。
UDP: 相当于发电报。
  
UDP 是一种面向无连接的通信协议。

UDP 向应用程序提供了一种发送封装的原始 IP 数据报的方法,并且发送时无需建立连接,不保证可靠数据的传输。
    
2.TCP协议和UDP协议的差别

  
面向无连接的数据传输,不可靠的,但效率高。
一次发送的数据不能超过 64KB

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1303615.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Network 灰鸽宝典【目录】

目前已有文章 11 篇 Network 灰鸽宝典专栏主要关注服务器的配置&#xff0c;前后端开发环境的配置&#xff0c;编辑器的配置&#xff0c;网络服务的配置&#xff0c;网络命令的应用与配置&#xff0c;windows常见问题的解决等。 文章目录 canvas理论基础canvas高级应用示例canv…

Java 线程的基本概念

创建和运行线程 方法一&#xff0c;直接使用 Thread // 创建线程对象 Thread t new Thread() {public void run() {// 要执行的任务}};// 启动线程 t.start();例如&#xff1a; // 构造方法的参数是给线程指定名字&#xff0c;推荐 Thread t1 new Thread("t1") …

软件测试:基础概念

目录 ​编辑 一、前言 二、软件测试的原则和方法论 1.测试的原则 2.测试的方法论 2. 软件测试策略 2.1 单元测试 2.2 集成测试 2.3 系统测试 2.4 用户验收测试 3. 软件测试的最佳实践 3.1 自动化测试 3.2 持续集成 3.3 边界值测试 三、软件测试的技术和实践 1.…

深度学习——第4.3章 深度学习的数学基础

第4章 深度学习的数学基础 目录 4.7 指数函数和对数函数 4.7 指数函数和对数函数 深度学习经常会用到Sigmoid函数和Softmax函数&#xff0c;这些函数是通过包含exp(x)的指数函数创建的。后面我们需要求解这些函数的导数。 4.7.1 指数 指数是一个基于“乘以某个数多少次”&a…

远程工作:自由职业者如何成功赚钱

前言 在这个不断进步的数字化时代&#xff0c;远程工作已经从一个可选的边缘工作方式&#xff0c;成长为主流职业趋势的一部分。特别是自从全球疫情改变了我们的生活和工作方式以来&#xff0c;远程工作的概念不再是遥不可及的理想&#xff0c;而是已经成为许多人日常工作的现…

12 位多通道国产芯片ACM32F403/F433 系列,支持 MPU 存储保护功能,应用于工业控制,智能家居等产品中

ACM32F403/F433 芯片的内核基于 ARMv8-M 架构&#xff0c;支持 Cortex-M33 和 Cortex-M4F 指令集。芯片内核 支持一整套DSP指令用于数字信号处理&#xff0c;支持单精度FPU处理浮点数据&#xff0c;同时还支持Memory Protection Unit &#xff08;MPU&#xff09;用于提升应用的…

PCB-修改pcb封装后,规则重新检查出现的提示解决

规则检查 前提 前提 要在.pcbdoc页面点击design 点击 run design rule check 3.出现提示 4.意思是对某些器件修改过&#xff0c;进行重新铺铜即可 重新铺铜快捷键&#xff1a;TGA,如下是重新铺铜后的结果

气温波动 C语言xdoj45

问题描述 最近一段时间气温波动较大。已知连续若干天的气温&#xff0c;请给出这几天气温的最大波动值是多少&#xff0c;即在这几天中某天气温与前一天气温之差的绝对值最大是多少。 输入说明 输入数据分为两行。 第一行包含了一个整数n&#xff0c;表示给出了连续n天…

订单接入支付宝流程实战与优化

概述 了解支付宝支付能力接入方式。电商项目如何对支付流程进行设计及优化。基于 RocketMQ 事务消息实现的订单确认机制&#xff0c;来完成订单超时回退功能。 支付宝接入流程简介 国内目前有支付牌照的公司总共只有两百来家&#xff0c;比如支付宝、云闪付、和包支付、翼支…

el-table的复选框占满全格

el-table的复选框格子很小每次点击都点不到&#xff0c;又不想设置行点击&#xff0c;因为每次复制内容都会选中&#xff0c;实现效果是点击el-table的复选框单元格就可以选中 <template><div style"width: 60vw; margin: 10px;"><el-table :data&quo…

金蝶云星空审核使用了基础资料的属性字段报错:表达式计算出错

文章目录 金蝶云星空审核使用了基础资料的属性字段报错&#xff1a;表达式计算出错报错内容解决方案 金蝶云星空审核使用了基础资料的属性字段报错&#xff1a;表达式计算出错 报错内容 表达式计算出错: F_XXXX_MaterialId.FIsSNManage true and F_XXXX_ProductLibraryId 0 …

Cython笔记:基本使用方法

1 Cython 介绍 Cython 是一种编程语言&#xff0c;用于编写 C 扩展的 Python 模块。它结合了 Python 的易用性和 C 的执行效率&#xff0c;旨在提高特定类型的任务的性能&#xff0c;特别是涉及大量数值计算的任务。 1.2 几个特征 1.2.1 Python 语法的基础 Cython 的大部分语…

市场全局复盘 20231208

一、板块成交额排名&#xff1a; 资金流入前三个板块K 线&#xff1a; 行业成交额排名&#xff1a; 个股资金流入排名&#xff1a; select 成交额排名 ,近日指标提示 ,短线主题 ,涨停分析,CODE,名称,DDE大单净量,现价,量比,连板天,周涨停,月涨停,年涨停天,连涨天,…

【Nacos】在Windows环境下搭建Nacos服务与Python开发示例

在Windows环境下搭建Nacos服务与Python开发示例 Nacos是一个开源的动态服务发现、配置和服务管理平台。本文将介绍在Windows环境下搭建Nacos服务&#xff0c;并提供一个简单的Python示例&#xff0c;演示如何通过Nacos实现服务注册与发现。 官方提示&#xff1a;Nacos定义为一…

软件开发流程分析

软件开发流程分析 相关概念1 原型设计2 产品设计3 交互设计4 代码实现详细步骤 相关概念 前端&#xff1a;自研API&#xff0c;调用第三放API 后端&#xff1a;自研API&#xff0c;第三方API 数据库&#xff1a;Mysql&#xff0c;数据采集&#xff0c;数据迁移 服务器&#xf…

优势怪代码

#include <stdio.h> int main() { int t; scanf("%d", &t); // 读取测试用例的数量 while (t--) { // 对每个测试用例进行处理 int n, max1 0, max2 0, k 0; scanf("%d", &n); // 读取数组的大小 in…

瑞萨RZ/G2L核心板Linux Log目录文件详解

为了排除系统问题&#xff0c;监控系统健康状况以及了解系统与应用程序的交互方式&#xff0c;我们需要了解各log文件的作用&#xff0c;以G2L中yocto文件系统为例&#xff0c;在系统/var/log/目录下会存放记录系统中各个部分的log文件作用如下&#xff1a; 1. 文件详情 下图…

decomposition-based multi-objective algorithm4SPDPTW

关键词 文章概述 研究背景 多目标选择性接送和配送问题&#xff08;PDPs&#xff09;&#xff1a;研究涉及多目标选择性接送和配送问题&#xff0c;这些问题传统上从单一目标角度进行探讨&#xff0c;以寻找最具盈利性的请求集合&#xff0c;同时遵守一系列限制条件。 经济和…

新工科:数据科学与大数据技术实验中心解决方案,赋能高校新工科数智人才培养

随着数字经济蓬勃发展&#xff0c;数字化产业和产业数字化成为就业增长新动能。据人瑞人才与德勤调研显示&#xff0c;未来3年&#xff0c;数字产业化企业最需要运营人员和开发人员&#xff08;包括大数据开发工程师、数据建模开发工程师等&#xff09;&#xff0c;其次是数据分…

一、微前端目标、前端架构的前生今世、微前端架构优势和劣势、软件设计原则与分层

1、目标 2、前端架构的前世今生 ① 初始&#xff1a;无架构&#xff0c;前端代码内嵌到后端应用中 ② 后端 MVC 架构&#xff1a;将视图层、数据层、控制层做分离 缺点&#xff1a;重度依赖开发环境&#xff0c;代码混淆严重&#xff08;在调试时&#xff0c;需要启动后端所有…