java-网络编程-UDP通信-TCP通信

文章目录

    • ==网络编程基础==
      • 网络编程概述
      • IP地址
      • InetAddress使用
      • 端口
      • TCP协议 和 UDP协议
    • ==UDP通信程序DatagramSocket、DatagramPacket==
      • 原理及概述
      • UDP发送数据
      • UDP接收数据
      • 案例:UDP通信程序练习
    • ==TCP通信==
      • 概念
      • TCP发送数据
      • TCP接收数据
      • ==TCP通信案例==
        • 案例1:客户端 发数据 接收服务器端反馈;服务器 收数据 给反馈
        • 案例2:客户端数据来自键盘 服务端数据接收到在控制台输出
        • 案例3:客户端(键盘录入) 服务端(写入文件)
        • 案例4:客户端(文件读出) 服务端(写入文件)
        • 案例5:客户端(数据文件+服务端反馈)服务端(数据文件+写反馈)
        • 案例6:客户端(数据文件+接收反馈) 服务端(数据文件+给出反馈+为客户端开辟线程)

网络编程基础

网络编程概述

计算机网络 是指地理位置不同的具有独立功能的多台计算机及其外部设备。
通过通信线路连接起来,在网络操作系统、网络管理软件及网络通信协议的管理和协调下,实现资源共享和信息传递的计算机系统。

网络编程 在网络通信协议下,实现网络互连的不同计算机上运行的程序间可以进行数据交换。

网络编程三要素 ①IP地址 ②端口 ③ 协议

  • IP地址:要想让网络中的计算机能够相互通信,必须为每台计算机指定一个标识号,通过这个标识号来识别要接收数据的计算机和识别发送数据的计算机。IP地址就是这个标识号,也是设备的标识。
  • 端口:网络的通信,本质上是两个应用程序的通信。每台计算上都有很多的应用程序,端口号就可以唯一标识设备中的应用程序。
  • 协议:通过计算机网络可以使多台计算机实现连接,位于同一个网络中的计算机在进行连接和通信的时候需要遵守一定的规则。在计算机网络中,这些连接和通信的规则被称为网络通信协议,它对数据的传输格式、传输速率、传输步骤做了统一规定,通信双方必须同时遵守才能完成数据交换。常见的网络通信协议是UDP协议和TCP协议。

IP地址

IP地址 是网络中设备的唯一标识

IP地址分为两大类:
IPv4:

  • 每个连接在网络上的主机分配一个32bit的地址。
  • 根据TCP/IP协议,IP地址采用二进制标识,32bit = 4个字节
  • 为了方便标识,IP地址被使用点分十进制表示,符号“.”分隔不同的字节。
  • 案例: 二进制 11000000 10101000 00000001 01000010 点分十进制 192.168.1.66

IPv6:

  • 互联网蓬勃发展,IP地址的需求日益增大。
  • 采用128bit地址空间,每16个字节一组,分成8组,采用16进制表示。

常用命令:

  • ipconfig 查看本机IP地址
  • ping IP地址:检查网络是否联通
  • 特殊IP地址:127.0.0.1 是回送地址,也可以表示本机地址,一般用来测试使用。

InetAddress使用

  • 在java.net包下,使用需要导包
  • 该类表示的是Internet协议地址
  • 它没有构造方法,且大部分方法都不是静态的(static),则它一定会提供一种静态的方式得到该类的对象。
  • 静态方法:
方法名 说明
static InetAddress getByName(String host) 确定主机名称的IP地址
主机名称可以是机器名称,也可以是IP地址
  • 常用方法:
方法名 说明
String getHostAddress() 返回文本显示中的IP地址字符串
String getHostName() 获取此IP地址的主机名

代码:

// package itiheima311
public class InetAddressDemo {
    public static void main(String[] args) throws UnknownHostException {
        // getByName
        InetAddress address = InetAddress.getByName("本地电脑主机名");

        String name = address.getHostName();
        String ip = address.getHostAddress();

        System.out.println("主机名:" + name);
        System.out.println("ip地址:" + ip);
		// 输出内容:
		// 主机名:xxxxxx
		// ip地址:xxxxxx
    }
}

注意

  • 本地电脑主机名在哪里查看?
    windows10系统下,我的电脑→右键→属性→设备名称 就是主机名

端口

端口: 设备上应用程序的唯一标识

端口号:

  • 用两个字节表示的整数,范围是0-65535
  • 0-1023之间的端口号用于一些知名的网络服务和应用,普通的应用程序需要使用1024以上的端口号。
  • 端口号被另一个服务或应用所占用,会导致当前程序启动失败。

TCP协议 和 UDP协议

协议: 计算机网络中,连接和通信的规则被称为网络通信协议

UDP协议: 用户数据报协议(User Datagram Protocal)

  • UDP是无连接通信协议,即在数据传输时,数据的发送端和接收端不建立逻辑连接。(=发送端不会确定接收端是否存在,接收端收到数据也不会向发送端反馈是否收到数据)
  • 使用UDP协议消耗资源小,通信效率高,通常用于音频、视频和普通数据的传输
  • 使用UDP协议发送数据,由于其是面向无连接的,不能保证数据的完整性,因此在传输重要数据时不建议使用UDP协议。

TCP协议: 传输控制协议(Transmission Control Protocol)

  • TCP协议是面向连接的通信协议,即传输数据之前,在发送端和接收端建立逻辑连接,然后再传输数据,它提供了两台计算机之间的可靠无差别的数据传输。
  • TCP连接中必须要明确客户端与服务器端,由客户端向服务器端发出连接请求,每次连接的创建都需要经过“三次握手”;
  • “三次握手”,TCP协议中,在发送数据的准备阶段,客户端与服务器之间的三次交互,以保证连接的可靠性;
    第一次握手:客户端向服务器端发出连接请求,等待服务器确认
    第二次握手:服务器端向客户端发送一个响应,通知客户端收到了连接请求
    第三次握手:客户端再次向服务器端发送确认信息,确认连接
  • TCP协议保证传输数据的安全,例如:上传文件、下载文件、浏览网页等。

UDP通信程序DatagramSocket、DatagramPacket

原理及概述

UDP通信原理

  • UDP协议是一种不可靠的网络协议,通信两端都创建了Socket对象,且只是发送或接收的对象;
  • UDP协议的通信双方而言,没有所谓客户端和服务器的概念;
  • java提供了DatagramSocket类作为基于UDP协议的Socket;
    DatagramSocket:表示用于发送和接收数据报包 的套接字;
    在java.net包下,使用需要导包
    DatagramPacket:表示数据报包

注意:

  • 接收端和发送端的代码启动顺序:先运行接收端程序,再运行发送端程序。
  • 关于端口号的指定:发送端是在DatagramPacket中指定的;接收端是在DatagramSocket中指定的。

常用方法及方法

方法名 说明
发送数据
DatagramSocket() 创建发送端的套接字
DatagramPacket(Bytes[] bytes,int length,InetAddress ip,int port) 创建数据包,把长度为length的字节数组发送到ip地址为InetAddress的主机的port端口
void send(DatagramPacket p)
所属类DatagramSocket
从此套接字发送数据
接收数据
DatagramSocket(int port) 创建接收端套接字,监控指定端口port
DatagramPacket(bytes[] buf , int length) 构造数据包,用于接收长度为length的字节数组
void receive(DatagramPacket p)
所属类DatagramSocket
从此套接字接收数据
byte[] getData()
所属类DatagramPacket
返回数据包中的数据缓冲区数据
int getLength()
所属类DatagramPacket
返回发送或者接收数据的长度

UDP发送数据

1 创建发送端的Socket对象(DatagramSocketDatagramSocket() 构造方法:构造数据报套接字并将其绑定到本地主机上的任何可用端口。
	//DatagramSocket ds = new DatagramSocket();
	
2 创建数据,并把数据打包
	DatagramPacketbyte[] buf,int length,InetAddress address,int port)
	构造一个数据包,发送长度为length的数据包到指定主机上的指定端口号(目的主机)
	//byte[] bys = "要传输数据".getBytes();
	//int len = bys.length();
	//InetAddress ip = new InetAddress("ip地址或主机名");
	//DatagramPacket dp = new DatagramPackt(bys,len,ip,port);
	
3 调用DatagramSocket对象的方法send发送数据
	void send (DatagramPacket p) 从此套接字发送数据报包
	//ds.send(dp);
	
4 关闭发送端
	void  close() 关闭此数据报套接字
	//ds.close();

代码:

// 311
public class SendDemo {
    public static void main(String[] args) throws IOException {
        DatagramSocket ds = new DatagramSocket();

        byte[] bys = "hello,udp,我来了".getBytes();
//       int length =  bys.length;
//        InetAddress address = InetAddress.getByName("ip地址");
//        int port = 10086;
        DatagramPacket  dp = new DatagramPacket(bys,bys.length,InetAddress.getByName("ip地址"),10086);
        ds.send(dp);

        ds.close();
    }
}

UDP接收数据

1 创建接收端的Socket对象(DatagramSocketDatagramSocket(int port):构造数据报套接字并将其绑定到本地主机上的指定端口
	//DatagramSocket ds = new DatagramSocket(端口号);
2 创建一个数据包,用于接收数据
	DatagramPacket(byte[] bys,int length):构造一个DatagramPacket用于接收长度为Length数据包
	//byte[] bys = new byte[1024];
	//DatagramPacket dp = new DatagramPacket(bys,bys.length);
3 调用DatagramSocket对象的方法接收数据
	Socket对象.receive(数据包)
	//ds.receive(dp);
4 解析数据包,并把数据在控制台显示
	byte[] getData() 返回数据缓冲区
	数据包.getData()——这个方法拿到的是数据缓冲区
	int getLength() 返回要发送的数据的长度或接收到的数据长度
	
	//byte[] data = dp.getData();
	//int datalen = dp.getLength();
	//String dataS = new String(data,0,datalen);
5 关闭接收端
	Socket.close();
	//ds.close();

代码:

// 311
public class ReceiveDemo {

    public static void main(String[] args) throws IOException {

        DatagramSocket ds = new DatagramSocket(10085);

        byte[] bys = new byte[1024];
        DatagramPacket dp = new DatagramPacket(bys,bys.length);

        ds.receive(dp);

        byte[] data = dp.getData();
        int len = dp.getLength();

        String dataString = new String(data,0,len);
        System.out.println("数据是:" +  dataString);

        ds.close();
    }
}

案例:UDP通信程序练习

要求:
UDP发送数据:数据来自于键盘录入,直到输入的数据是886,发送数据结束
UDP接收数据:因为接收端不知道发送端什么时候停止发送,故采用死循环接收

代码:

// 311-test3
public class SendDemo {
    public static void main(String[] args) throws SocketException, IOException {
/*要求:
UDP发送数据:数据来自于键盘录入,直到输入的数据是886,发送数据结束
UDP接收数据:因为接收端不知道发送端什么时候停止发送,故采用死循环接收
*/
        // 创建发送端的Socket
        DatagramSocket sd = new DatagramSocket();
        //键盘录入要发送的数据 使用的是字符缓冲流 
       BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
       String line;
       // 
       while((line = br.readLine()) != null){
           if("886".equals(line)){
               break;
           }
           byte[] bys = line.getBytes();
           int len = line.length();
           InetAddress address = InetAddress.getByName("ip地址");
           int port = 10086;
           // 发送数据的数据包
           DatagramPacket dp = new DatagramPacket(bys,len,address,port);

           // 数据发送
           sd.send(dp);
           // 数据是一行一行发送。
       }
        // 关闭发送流
        sd.close();
        br.close();
    }
}

// 接收端
public class recevieDemo {
    public static void main(String[] args) throws IOException {
        // 创建一个socket对象
        DatagramSocket ds = new DatagramSocket(10086);

        byte[] bys = new byte[1024];

        // 创建接收数据包
        DatagramPacket dp = new DatagramPacket(bys,bys.length);
        
        while(true) {
            // 接收数据
            ds.receive(dp);

            // 解析数据
            byte[] dataBys = dp.getData();
            int dataLen = dp.getLength();
            // 将字符数组数据转为字符串
            String sOut = new String(dataBys, 0, dataLen);

            // 控制台输出数据
            System.out.println("发送数据是:" + sOut);
        }
        // 关闭接收端
//        ds.close();
    }
}

TCP通信

概念

  • TCP通信协议是一种可靠的网络协议,它在通信的两端各建立一个Socket对象,从而在通信的两端形成网络虚拟链路,两端的程序就可以通过虚拟链路进行通信。
  • java对基于TCP的网络提供了良好的封装,使用Socket对象来表示两端的通信端口,并通过Socket产生IO流来进行网络通信;
  • java为客户端提供了Socket类,为服务端提供了ServerSocket类

发送数据和接收数据涉及到的类

方法名 说明
发送数据
Socket(InetAddress address,int port) 创建流套接字并连接到指定IP地址的指定端口号
Socket(String host,int port) 创建套接字并将其连接到指定主机上的指定端口号
OutputStream getOutputStream() 返回此套接字的输出流
InputStream getInputStream() 获取输入流
void write() 将数据写入字节输出流中
public void shutdoenOutput() Socket类提供了方法设置结束标识
接收数据
ServerSocket(int port) 创建绑定到指定端口的服务器套接字
Socket accept()
所属于ServerSocket类
侦听要连接到此套接字并接受它
OutputStream getOutputStream() 返回此套接字的输出流
InputStream getInputStream() 获取输入流
int read() 读取字节输入流数据,读一个字节、读一个字节数组

注意

2022/4/20
创建Socket对象 和 ServerSocket对象 的时候,需要抛出一个异常IOException

TCP发送数据

步骤涉及类信息说明:

  • Socket
  • 在java.net包下,使用需要导包
  • 该类实现了客户端套接字(=两台计算机之间通讯的端点)

常用方法

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

操作步骤:

1 创建客户端的Socket对象
	Socket(InetAddress address,int port):创建流套接字并连接到指定IP地址的指定端口号
	//Socket s = new Socket(InetAddress.getByName("IP地址"),端口号)
	Socket(String host,int port):创建套接字并将其连接到指定主机上的指定端口号
	//Socket s = new Socket("IP地址",端口号)
2 获取输出流 写数据
	OutputStream getOutputStream() :返回此套接字的输出流
	void write():写一个字节数组;写一个字节数组一部分;写一个int数据。
	//OutputStream os = s.getOutputStream();
	//os.write(要写的内容);
3 释放资源
	close();
	// s.close();

代码:

// itiheima311.test4
public class ClientDemo {
    public static void main(String[] args) throws IOException {
        // 客户端的Socket对象
        Socket s = new Socket("ip地址",端口号);
        // 输出流对象,写数据
        OutputStream os = s.getOutputStream();
        os.write("you are my pretty sunshine".getBytes());

        // 释放资源
        s.close();
    }
}

注意
直接运行client程序会报错的:因为TCP通信是需要三次握手,只有客户端没有服务端连接请求就会没有回复的响应,故此报错java.net.ConnectException: Connection refused: connect

TCP接收数据

步骤涉及的类:

  • ServerSocket
  • 在软件包java.net下,使用需要导包
  • 实现了服务器套接字

步骤:

1 创建服务器端的Socket对象(ServerSocketServerSocket(int port) 创建绑定到指定端口的服务器套接字
	//ServerSocket ss = new ServerSocket(端口号);
2 监听客户端连接,返回一个Socket对象
	Socket accept() : 侦听要连接到此套接字并接受它
	//Socket s = ss.accept();
3 获取输入流,读数据,并把数据显示在控制台
	InputStream getInputStream(); 获取输入流。
	// InputStream is = s.getInputStream();//获取输入流
	// byte[] bys = new byte[1024];//读数据 一次读一个字节数组
	// int len;
	// while((len = is.read(bys)) != -1){
		// System.out.println(new String(bys,0,len));//显示在控制台
	// 	}
4 释放资源
	close()
	// ss.close();

代码:

// itiheima311.test4
public class ServerDemo {
    public static void main(String[] args) throws IOException {
        // 创建服务器端的Socket对象
        ServerSocket ss = new ServerSocket(端口号);

        // 监听客户端连接
        Socket s = ss.accept();

        // 获取输入流 读数据 并把数据显示在控制台
        InputStream is = s.getInputStream();

        // 一次读一个字节数组
        byte[] bys = new byte[1024];
        int len;
        while((len = is.read(bys)) != -1){
            System.out.println(new String(bys,0,len));
        }
        // 释放资源
        is.close();
        ss.close();

    }
}

TCP通信案例

案例1:客户端 发数据 接收服务器端反馈;服务器 收数据 给反馈

客户端:发送数据,接受服务器反馈
服务器:接收数据,给出反馈

2022/7/27

客户端:
Socket(String host,int port) s
// 发送
s.getOutputStream gos
gos.write(字符串.getBytes()) // 输出字节流不能写字符串,必须是字节或字节数组

// 接收
getInputStream gis
gis.read(bytes[] buf) buf 
new String(buf,0,buf.length)

服务端:
ServerSocket(int port) ss
ss.accept() Scoket s
s.getInputStream() gis
gis.read(bytes[] buf) buf

s.getOutputStream() gos
gos.write() 给客户端写回信
// itiheima311.test5
public class ClientDemo {
    public static void main(String[] args) throws IOException {

        Socket s = new Socket("ip地址",端口号);
		// 客户端发送数据
        OutputStream os = s.getOutputStream();
        os.write("每个人的生命中,都有无比艰难的那一年,将人生变得更美好而辽阔".getBytes());

        // 接收服务端反馈
        InputStream is = s.getInputStream();
        byte[] bys = new byte[1024];
        /*int len;
        while((len = is.read(bys)) != -1){
            System.out.println("客户端:" + new String(bys,0,len));
        }*/
        int len = is.read(bys);
        String data = new String(bys,0,len);
        System.out.println("服务端回信:" + data);

        s.close();
    }
}

public class ServerDemo {
    public static void main(String[] args) throws IOException {

        ServerSocket ss = new ServerSocket(10023);

        Socket s = ss.accept();
		// 服务端接收客户端发送的数据	
        // 获取输入流,读数据,并把结果显示在控制台
        InputStream is = s.getInputStream();
        byte[] bys = new byte[1024];
       /* int len;
        while((len = is.read(bys)) != -1){
            System.out.println("服务器:" + new String(bys,0,len));
        }*/
       int len = is.read(bys);
       String data  = new String(bys,0,len);
        System.out.println("服务器接收到客户端数据:" + data);

        // 给客户端发送反馈信息
        OutputStream os = s.getOutputStream();
        os.write("数据已接收".getBytes());

        ss.close();
    }
}

案例2:客户端数据来自键盘 服务端数据接收到在控制台输出

客户端:数据来自于键盘录入,直到输入的数据是886,发送数据结束
服务端:接收到的数据在控制台输出

2022/7/28

发送端:
SocketString host,int port) s
BufferedReadernew InputStreamReaderSystem.in)) br
BufferedWriternew OutputStreamWriter(s.getOutputStream())) bw
String line;
while((line = br.readline()!= nullif("886".equals(line))
		break;
	bw.write(line);
	bw.newLine();
	bw.flush();
	
bw.close();
s.close();

接收端:
ServerSocket(int port) ss
ss.accept() Socket s
BufferedReader(new InputStreamReader(s.getInputStream())) br
String line;
whlie((line = br.readLine()) != null){
	System.out.println(line);
}
ss.close();

代码:

// itiheima311.test6
public class ClientDemo {
    public static void main(String[] args) throws IOException {
        /*
        客户端:数据来自于键盘录入,直到输入的数据是886,发送数据结束
        服务端:接收到的数据在控制台输出*/

        Socket s = new Socket("192.168.1.100",10023);
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));

        String line;
        while((line = br.readLine()) != null){
            if("886".equals(line)){
                break;
            }
           /*// 获取输出流对象
            OutputStream os = s.getOutputStream();
            os.write(line.getBytes());*/
           bw.write(line);
           bw.newLine();
           bw.flush();

        }

        s.close();
    }
}

public class ServerDemo {
    public static void main(String[] args) throws IOException {
    // 创建ServerSocket对象
        ServerSocket ss = new ServerSocket(10023);
	// 
        Socket s = ss.accept();

//        InputStream is = s.getInputStream();
        /*byte[] bys = new byte[1024];
        int len;
        while((len = is.read(bys)) != -1){
            String data = new String(bys,0,len);
            System.out.println("服务器接收到数据:"+ data);
        }*/
        // 使用字符缓冲输入流 读取数据
        BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream()));
        String line;
        while((line = br.readLine()) != null){
            System.out.println(line);
        }
        ss.close();
    }
}

案例3:客户端(键盘录入) 服务端(写入文件)

需求
客户端:数据来自于键盘录入,直到输入的数据是886,发送数据结束
服务器:接收到的数据写入文本文件

2022/7/28

发送端:
Socket(String host,int port) s
//接收再发送
BufferedReader(new InputStreamReader(System.in)) br
BufferedWriter(new OutputStreamWriter(s,getOutputStream())) bw
String line;
while((line = br.readLine()) != null){
	if("886".equals())
		break;
	bw.write(line);
	bw.newLine();
	bw.flush();
}
bw.close();
br.close();
s.close();

服务端:
ServerSocket(int port) ss
ss.accept() Socket s
// 读数据写入文件中
BufferReader(new InputStreamReader(s.getInputStream())) br
Bufferwriter(new FileWriter(String filename)) bw
String line;
while((line = br.readLine()) != null)
	bw.write(line);
	bw.newLine();
	bw.flush();
bw.close();
br.close();
ss.close();
public class CilentDemo {
    public static void main(String[] args) throws IOException {
        Socket s = new Socket("192.168.1.100",10023);

        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));

        String line;
        while((line = br.readLine()) != null){
            if("886".equals(line)){
                break;
            }
            bw.write(line);
            bw.newLine();
            bw.flush();
        }

        s.close();
    }
}

public class ServerDemo {
    public static void main(String[] args) throws IOException {
        // 创建服务器端的Socket对象
        ServerSocket ss = new ServerSocket(10023);

        // 监听客户端连接
        Socket s = ss.accept();

        // 接收数据
        BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream()));
        // 把数据写入文本文件
        BufferedWriter bw = new BufferedWriter(new FileWriter(".\\bw.txt"));
        String line;
        while((line = br.readLine()) != null){
            bw.write(line);
            bw.newLine();
            bw.flush();
        }

        ss.close();
        bw.close();
    }
}

案例4:客户端(文件读出) 服务端(写入文件)

需求
客户端:数据来自于文本文件
服务器:接收到的数据写入文本文件

2022/7/28

客户端:
Socket(String host,int port) s
// 文件读数据 写入socket的输出流
BufferReader(new FileReader(String name)) br
BufferWriter(new OutputStreamWriter(s.getOutputStream())) bw
String line;
while((line = br.readLine()) != null)
	bw.write(line);
	bw.newLine();
	bw.flush();
br.close();
bw.close();
s.close();


服务端:
ServerSocket(int port) ss
ss.accept() Socket s
// Socket读数据到输入流 数据写入文件中
BufferedReader(new InputStreamReader(s.getInputStream())) br
BufferedWriter(new FileWriter(String name)) bw
String line;
while((line = br.readLine()) != null)
	bw.write(line);
	bw.newLine();
	bw.flush();
br.close();
bw.close();
ss.close();

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

        Socket s = ss.accept();

        BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream()));
        BufferedWriter bw = new BufferedWriter(new FileWriter(".\\bw.txt"));

        String line ;
        while((line = br.readLine()) != null){
            bw.write(line);
            bw.newLine();
            bw.flush();
        }

        ss.close();
        bw.close();
    }
}

public class ClientDemo {
    public static void main(String[] args) throws IOException {
        // 创建客户端Socket对象
        Socket s = new Socket("192.168.1.100",10023);

        // 给文件写数据
/*        BufferedWriter bw = new BufferedWriter(new FileWriter(".\\br.txt"));
        bw.write("没有一劳永逸,永远要重新开始,重新进入动荡,重新寻找,重新赢得欢喜");
        bw.close();*/
        // 封装文本文件的数据
        BufferedReader br = new BufferedReader(new FileReader(".\\br.txt"));


        // 封装输出流写数据
        BufferedWriter bw1 = new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));

        String line;
        while((line = br.readLine()) != null){
            bw1.write(line);
            bw1.newLine();
            bw1.flush();
        }

        s.close();
        br.close();
    }
}

案例5:客户端(数据文件+服务端反馈)服务端(数据文件+写反馈)

需求
客户端:数据来自于文本文件,接收服务器反馈
服务器:接收到的数据写入文本文件,给出反馈

分析:
会出现程序卡住的问题,因为客户端上传程序没有明确的上传结束标识,服务器会持续等待一直在读数据。
原因:读数据的方式是阻塞式的
解决方法:自定义结束标记;使用shutdownOutput()方法

public class ClientDemo {
    public static void main(String[] args) throws IOException {
        Socket s = new Socket("192.168.1.100",10023);

        // 封装文本文件的数据
        BufferedReader br = new BufferedReader(new FileReader(".\\br.txt"));
        // 封装输出流写数据
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));

        String line;
        while((line = br.readLine()) != null){
            bw.write(line);
            bw.newLine();
            bw.flush();
        }
        // 自定义数据上传结束标识
       /* bw.write("886");
        bw.newLine();
        bw.flush();*/
        // 会产生一个问题,当要发送的文件中存在886字符串的时候,后续内容将不会继续发送。
        // Socket类提供了方法设置结束标识
        // public void shutdoenOutput()
        s.shutdownOutput();

        // 接收服务器反馈
        BufferedReader br1 = new BufferedReader(new InputStreamReader(s.getInputStream()));
        String data;
        while((data = br1.readLine()) != null){
            System.out.println("客户端收到是:" + data);
        }
        s.close();
        br.close();
    }
}


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

        Socket s = ss.accept();

        // 封装输入流数据
        BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream()));
        BufferedWriter bw = new BufferedWriter(new FileWriter(".\\bw.txt"));
        
        String line;
        while((line = br.readLine()) != null){
            /*if("886".equals(line)){
                break;
            }*/
            bw.write(line);
            bw.newLine();
            bw.flush();
        }
        // 服务器给出反馈
        BufferedWriter bw1 = new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));
        bw1.write("服务器已接收到数据");
        bw1.newLine();
        bw1.flush();

        // 释放资源
        bw.close();
        ss.close();

    }
}

案例6:客户端(数据文件+接收反馈) 服务端(数据文件+给出反馈+为客户端开辟线程)

需求
客户端:数据来自于文本文件,接收服务器反馈
服务器:接收到的数据写入文本文件,给出反馈,代码用线程进行封装,为每一个客户端开启一个线程

2022/7/28分析
服务端需要继承Runnable接口,实现多线程;

服务端:
public class ServerThread implements Runnable{
	private Socket s;
	public ServerThread(Socket s){
		this.s = s;
	}
	@Override
	public void run(){
		try{
		}catch(IOException e){
		}
	}
}
public class ServerDemo {
    public static void main(String[] args) throws IOException {
        // 创建服务器Socket对象
        ServerSocket ss = new ServerSocket(10023);

        // 通过while模拟服务器不关闭
        while(true) {
            // 监听客户端的连接,返回一个对应的Socket对象
            Socket s = ss.accept();
            // 为每一个客户端开启一个线程
            new Thread(new ServerThread(s)).start();
        }

    }
}

public class ServerThread implements Runnable {
    private Socket s;

    public ServerThread(Socket s) {
        // 最开始这里没有写 相当于没给方法传参数
        this.s = s;
    }

    @Override
    public void run() {
        try{
            // 接收数据写到文本文件
            BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream()));
//            BufferedWriter bw = new BufferedWriter(new FileWriter(".\\312bw.txt"));

            // 多个客户端上传多个程序,使用不同文件夹存储
            int count = 0;
            String path = ".bw[" + count + "].txt";
            // 判断文件名是否有重复
            File f = new File(path);
            while(f.exists()){
                count++;
                path = ".bw[" + count + "].txt";
                f = new File(path);
            }
            // 字符缓冲流对象
            BufferedWriter bw = new BufferedWriter(new FileWriter(f));


            String line;
            while((line = br.readLine()) != null){
                bw.write(line);
                bw.newLine();
                bw.flush();
            }

            // 给客户端反馈
            BufferedWriter bwServer = new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));
            bwServer.write("文件上传成功!");
            // 服务器反馈一直是null的原因是:这里没有执行换行和刷新缓存
            bwServer.newLine();
            bwServer.flush();

            // 释放资源
            s.close();
            bw.close();
        }catch(IOException e){
            e.printStackTrace();
        }
    }
}

public class ClientDemo {
    public static void main(String[] args) throws IOException {
        // 创建客户端Socket对象
        Socket s = new Socket("192.168.1.100",10023);

        // 封装文本文件
        BufferedReader br = new BufferedReader(new FileReader(".\\br.txt"));
        // 封装输出流数据
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));

        // 向字节流中写数据
        String line;
        while((line = br.readLine()) != null){
            bw.write(line);
            bw.newLine();
            bw.flush();
        }
        // 字符流输出过程结束标识符
        s.shutdownOutput();

        // 接收服务器反馈
        BufferedReader brClient = new BufferedReader(new InputStreamReader(s.getInputStream()));
       /* String information;
        while((information = brClient.readLine()) != null){
            System.out.println("客户端接收到的数据:" + information);
        }*/
       String data = brClient.readLine();
        System.out.println("服务器的反馈:" + data);

        // 释放资源
        br.close();
        s.close();
    }
}

你可能感兴趣的:(java学习,udp,tcp)