Java 知识点整理-20.网络编程

本文深入讲解网络编程的基础概念,包括IP地址、端口号、协议等核心要素,详细解析UDP和TCP协议的工作原理与应用场景,通过丰富的示例代码展示如何实现UDP和TCP的通信过程,涵盖数据发送与接收、多线程优化、图形化界面设计等内容。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

补充的一些快捷键

网络编程概述

网络编程三要素之IP概述

网络编程三要素之端口号概述

网络编程三要素之协议概述

Socket通信原理图解

UDP传输

UDP传输优化

UDP传输多线程

UDP聊天图形化界面

UDP聊天发送功能

UDP聊天记录功能

UDP聊天清屏功能

UDP聊天震动功能

UDP聊天快捷键和代码优化

UDP聊天生成jar文件

TCP协议

TCP协议代码优化

服务端是多线程的

网络编程练习一

网络编程练习二


补充的一些快捷键

Alt + Shift + Z 可以给选中的区域快速加try catch。网上给的解释是 重构的后悔药(Undo)

Alt + Shift + L 抽取局部变量

选中文件按 Alt + Shift + R 即可改名

选中局部变量按 Ctrl + 1 选择 Convert local variable tofield 可以将局部变量生成成员变量。


网络编程概述

计算机网络

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

网络编程

就是用来实现网络互连的不同计算机上运行的程序间可以进行数据交换。


网络编程三要素之IP概述

Windows10 控制面板 - 网络和共享中心 - 更改适配器设置 - 右键WLAN属性 - 选中Internet 协议版本 4 (TCP/IPv4)点属性 - 这个位置放的就是IP地址,可以在这里设置

查看IPv6地址:控制面板 - 网络和共享中心 - 更改适配器设置 - 右键WLAN属性 - 选中Internet 协议版本 6 (TCP/IPv6)点属性 - 这里可以设置IPv6地址

每个设备在网络中的唯一标识。

每台网络终端在网络中都有一个独立的地址,我们在网络中传输数据就是使用这个地址。 

命令:

ipconfig:查看本机IP 192.168.12.42

ping:测试连接 192.168.40.62

本地回路地址:127.0.0.1 (做网络编程练习,没有网(外网、局域网)的情况下,自己在家做,就可以用这个IP地址,自己给自己发一些数据) 255.255.255.255是广播地址 (应用:聊天室)

IPv4:4个字节组成,4个0 - 255。大概42亿(256的四次方),30亿都在北美,亚洲4亿。2011年初已经用尽。 

IPv6:8组,每组4个16进制数。大概排列方式:八组太长,中间全是零的可以省略,然后两个冒号就连接在一起了。但如果有两组零,不能前后一起省略,因为这样做会无法得知前后到底有几个零。

1a2b:0000:aaaa:0000:0000:0000:aabb:1f2f

1a2b::aaaa:0000:0000:0000:aabb:1f2f

1a2b:0000:aaaa::aabb:1f2f

1a2b:0000:aaaa::0000:aabb:1f2f

1a2b:0000:aaaa:0000::aabb:1f2f


网络编程三要素之端口号概述

每个程序在设备上的唯一标识。

每个网络程序都需要绑定一个端口号,传输数据的时候除了确定发到哪台机器上(IP地址),还要明确发到哪个程序(端口)。

端口号范围从0 - 65535。

编写网络应用就需要绑定一个端口号,尽量使用1024以上的,1024以下的基本上都被系统程序占用了。

常用端口

mysql:3306

oracle:1521

web:80

tomcat:8080

QQ:4000

feiQ:2425


网络编程三要素之协议概述

为计算机网络中进行数据交换而建立的规则、标准或约定的集合。

UDP

面向无连接,数据不安全,速度快。不区分客户端与服务端。类似于发短信的过程,不用担心对方在还是不在,有可能数据发丢了。

TCP

面向连接(三次握手),数据安全,速度略低。分为客户端和服务端。

三次握手

客户端先向服务端发起请求,服务端响应请求,传输数据。(你瞅啥;瞅你咋地;来,咱俩唠唠)


Socket通信原理图解

Socket套接字概述:socket插座的意思

网络上具有唯一标识的IP地址和端口号组合在一起才能构成唯一能识别的标识符套接字。

通信的两端都需要有Socket。

网络通信其实就是Socket间的通信。

数据在两个Socket间通过IO流传输。

Socket在应用程序中创建,通过一种绑定机制与驱动程序建立关系,告诉自己所对应的IP和port端口号。可以将通信的两端理解为港口,Socket理解为码头,IO流理解为船,船在两个码头将来回传递数据。


UDP传输

发送Send

创建DatagramSocket, 随机端口号

创建DatagramPacket, 指定数据, 长度, 地址, 端口。相当于一个集装箱,将要发的货都装在箱子里,再发出去就行了

使用DatagramSocket发送DatagramPacket

关闭DatagramSocket

演示:

import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
/**
 *发送端
 */
public class Demo1_Send {
	public static void main(String[] args) throws Exception {	//UnknownHostException不知道主机异常
		String str = "what are you 弄啥呢?";
		//创建Socket相当于创建码头
		DatagramSocket socket = new DatagramSocket();		//可以不指定端口号,因为他可以是随机端口号
		//创建Packet相当于集装箱
		DatagramPacket packet = new DatagramPacket(str.getBytes(), str.getBytes().length, InetAddress.getByName("127.0.0.1"), 6666);
		//发货,将数据发出去
		socket.send(packet);
		//关闭码头,因为其底层是IO流
		socket.close();
	}
}

接收Receive

创建DatagramSocket, 指定端口号

创建DatagramPacket, 指定数组, 长度

使用DatagramSocket接收DatagramPacket

关闭DatagramSocket

从DatagramPacket中获取数据

演示:

import java.net.DatagramPacket;
import java.net.DatagramSocket;
/**
 * 接收端
 */
public class Demo1_Receive {
	public static void main(String[] args) throws Exception {
		//创建Socket相当于创建码头
		DatagramSocket socket = new DatagramSocket(6666);
		//创建Packet相当于创建集装箱
		DatagramPacket packet = new DatagramPacket(new byte[1024], 1024);
		//接货,接收数据
		socket.receive(packet);
		
		//获取数据,无论数据是否有效
		byte[] arr = packet.getData();
		//获取有效的字节个数
		int len = packet.getLength();
		System.out.println(new String(arr, 0, len));
		socket.close();
	}
}

运行程序时,先开接收端,再开发送端,不然可能接收不到。


UDP传输优化

接收方获取ip和端口号

String ip = packet.getAddress().getHostAddress();

int port = packet.getPort();

接收端演示:

import java.net.DatagramPacket;
import java.net.DatagramSocket;
/**
 * 接收端
 */
public class Demo2_Receive {
	public static void main(String[] args) throws Exception {
		//创建Socket相当于创建码头
		DatagramSocket socket = new DatagramSocket(6666);
		//创建Packet相当于创建集装箱
		DatagramPacket packet = new DatagramPacket(new byte[1024], 1024);
		
		while(true) {
			//不断地接货,接收数据
			socket.receive(packet);
			
			//不断地获取数据,无论数据是否有效
			byte[] arr = packet.getData();
			//不断地获取有效的字节个数
			int len = packet.getLength();
			//不断地获取ip地址
			String ip = packet.getAddress().getHostAddress();
			//不断地获取端口号
			int port = packet.getPort();
			
			System.out.println(ip + ":" + port + ":" + new String(arr, 0, len));
		}
	}
}

发送端演示:

import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.util.Scanner;
/**
 *发送端
 */
public class Demo2_Send {
	public static void main(String[] args) throws Exception {	//UnknownHostException不知道主机异常
		//创建键盘录入对象
		Scanner sc = new Scanner(System.in);
		//创建Socket相当于创建码头
		DatagramSocket socket = new DatagramSocket();
		
		while(true) {
			//不断地获取键盘录入的字符串
			String line = sc.nextLine();
			//不断地对字符串做判断,直到输入字符串quit
			if("quit".equals(line)) {
				break;
			}
			//不断地创建Packet相当于集装箱
			DatagramPacket packet = new DatagramPacket(line.getBytes(), line.getBytes().length, InetAddress.getByName("127.0.0.1"), 6666);
			//不断地发货,将数据发出去
			socket.send(packet);
		}
		//关闭码头,因为其底层是IO流
		socket.close();
	}
}

UDP传输多线程

利用多线程实现发送和接收在一个窗口完成。

演示:

import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.util.Scanner;

public class Demo3_MoreThread {
	public static void main(String[] args) throws InterruptedException {
		new Receive().start();
		Thread.sleep(500);		//让线程睡一会,防止Send线程先开启,保证Receive线程完全开启
		new Send().start();
	}
}

class Receive extends Thread {
	public void run() {
		try {
			//创建Socket相当于创建码头
			DatagramSocket socket = new DatagramSocket(6666);
			//创建Packet相当于创建集装箱
			DatagramPacket packet = new DatagramPacket(new byte[1024], 1024);
			
			while(true) {
				//不断地接货,接收数据
				socket.receive(packet);
				
				//不断地获取数据,无论数据是否有效
				byte[] arr = packet.getData();
				//不断地获取有效的字节个数
				int len = packet.getLength();
				//不断地获取ip地址
				String ip = packet.getAddress().getHostAddress();
				//不断地获取端口号
				int port = packet.getPort();
				
				System.out.println(ip + ":" + port + ":" + new String(arr, 0, len));
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

class Send extends Thread {
	public void run() {
		try {
			//创建键盘录入对象
			Scanner sc = new Scanner(System.in);
			//创建Socket相当于创建码头
			DatagramSocket socket = new DatagramSocket();
			
			while(true) {
				//不断地获取键盘录入的字符串
				String line = sc.nextLine();
				//不断地对字符串做判断,直到输入字符串quit
				if("quit".equals(line)) {
					break;
				}
				//不断地创建Packet相当于集装箱
				DatagramPacket packet = new DatagramPacket(line.getBytes(), line.getBytes().length, InetAddress.getByName("127.0.0.1"), 6666);
				//不断地发货,将数据发出去
				socket.send(packet);
			}
			//关闭码头,因为其底层是IO流
			socket.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

UDP聊天图形化界面

组件:

一个Frame。

两个Panel。

南部的Panel:一个TextField 四个Button。

中间的Panel:两个TextArea。

Font类的字段:

public static final int PLAIN 普通样式常量。 

Font类的构造方法:

public Font(String name, int style, int size) 根据指定名称、样式和磅值大小,创建一个新 Font。 名称即字体名称,样式即字形(常规、斜体、粗体、粗体 斜体),磅值大小及大小。

演示:

import java.awt.BorderLayout;
import java.awt.Button;
import java.awt.Color;
import java.awt.Font;
import java.awt.Frame;
import java.awt.Panel;
import java.awt.TextArea;
import java.awt.TextField;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
/**
 * GUI聊天
 */
public class Demo4_GUIChat extends Frame{
	public Demo4_GUIChat() {	//一创建子类对象,构造方法中的代码自动运行
		init();		//将代码抽取成一个方法,防止构造方法中的代码过于臃肿
		southPanel();
		centerPanel();
		event();
	}

	public void event() {
		this.addWindowListener(new WindowAdapter() {
			public void windowClosing(WindowEvent e) {
				System.exit(0);
			}
		});
	}

	public void centerPanel() {
		Panel center = new Panel();					//创建中间的Panel
		TextArea viewText = new TextArea();			//显示的文本区域
		TextArea sendText = new TextArea(5, 1);		//发送的文本区域 参数设置行和列,即高和宽
		//Panel默认流式布局,而我们需要将两个文本区域一个放在Panel的南边,一个放在Panel的中间,应该用边界布局
		center.setLayout(new BorderLayout());		//设置为边界布局管理器
		center.add(sendText, BorderLayout.SOUTH);	//发送的文本区域放在南边
		center.add(viewText, BorderLayout.CENTER);	//显示的文本区域放在中间
		viewText.setEditable(false);				//设置显示的文本区域不可以编辑
		viewText.setBackground(Color.WHITE);		//设置背景颜色 Color类中同一颜色有大写字段和小写字段两种,其实是一个东西。底层代码中,小写的静态常量被RGB三原色作为参数的Color对象赋值,大写的静态常量直接被小写的静态常量赋值。
		sendText.setFont(new Font("xxx", Font.PLAIN, 15));
		viewText.setFont(new Font("xxx", Font.PLAIN, 15));
		this.add(center, BorderLayout.CENTER);		//将Panel放在Frame的中间
	}

	public void southPanel() {
		Panel south = new Panel();					//创建南边的Panel
		TextField tf = new TextField(15);			//创建文本字段存储ip地址 参数设置列,即宽
		Button send = new Button("发 送");				//创建发送按钮
		Button log = new Button("记 录");				//创建记录按钮
		Button clear = new Button("清 屏");			//创建清屏按钮
		Button shake = new Button("震 动");			//创建震动按钮
		south.add(tf);
		south.add(send);
		south.add(log);
		south.add(clear);
		south.add(shake);
		this.add(south, BorderLayout.SOUTH); 		//将Panel放在Frame的南边
	}

	public void init() {
		this.setLocation(500, 50);
		this.setSize(400, 600);
		this.setVisible(true);
	}
	
	public static void main(String[] args) {
		new Demo4_GUIChat();	//创建一个Frame的子类对象,即创建一个窗体出来
		
	}
}

UDP聊天发送功能

演示:

import java.awt.BorderLayout;
import java.awt.Button;
import java.awt.Color;
import java.awt.Font;
import java.awt.Frame;
import java.awt.Panel;
import java.awt.TextArea;
import java.awt.TextField;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
 * GUI聊天
 */
public class Demo4_GUIChat2 extends Frame{
	private TextField tf;
	private Button send;
	private Button log;
	private Button clear;
	private Button shake;
	private TextArea viewText;
	private TextArea sendText;
	private DatagramSocket socket;

	public Demo4_GUIChat2() {	//一创建子类对象,构造方法中的代码自动运行
		init();		//将代码抽取成一个方法,防止构造方法中的代码过于臃肿
		southPanel();
		centerPanel();
		event();
	}

	public void event() {
		this.addWindowListener(new WindowAdapter() {
			public void windowClosing(WindowEvent e) {
				socket.close();						//在退出java虚拟机之前关流
				System.exit(0);
			}
		});
		
		send.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				try {
					send();
				} catch (IOException e1) {
					e1.printStackTrace();
				}
			}
		});
	}
	
	private void send() throws IOException {		//send属于在主线程里跑 main() - 构造方法 - event() - send()
		String message = sendText.getText();		//获取发送区域的内容
		String ip = tf.getText();					//获取ip地址

		DatagramPacket packet = 
				new DatagramPacket(message.getBytes(), message.getBytes().length, InetAddress.getByName(ip), 9999);
		socket.send(packet); 						//发送数据
		
		String time = getCurrentTime();				//获取当前时间
		viewText.append(time + " 我对:" + ip + " 说\r\n" + message + "\r\n\r\n"); 	//将信息添加到显示区域中
		sendText.setText("");						//清除发送区域的内容
	}

	private String getCurrentTime() {
		Date d = new Date();						//创建当前日期对象
		SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss");
		return sdf.format(d);						//将时间格式化
	}

	public void centerPanel() {
		Panel center = new Panel();					//创建中间的Panel
		viewText = new TextArea();
		sendText = new TextArea(5, 1);
		//Panel默认流式布局,而我们需要将两个文本区域一个放在Panel的南边,一个放在Panel的中间,应该用边界布局
		center.setLayout(new BorderLayout());		//设置为边界布局管理器
		center.add(sendText, BorderLayout.SOUTH);	//发送的文本区域放在南边
		center.add(viewText, BorderLayout.CENTER);	//显示的文本区域放在中间
		viewText.setEditable(false);				//设置显示的文本区域不可以编辑
		viewText.setBackground(Color.WHITE);		//设置背景颜色 Color类中同一颜色有大写字段和小写字段两种,其实是一个东西。底层代码中,小写的静态常量被RGB三原色作为参数的Color对象赋值,大写的静态常量直接被小写的静态常量赋值。
		sendText.setFont(new Font("xxx", Font.PLAIN, 15));
		viewText.setFont(new Font("xxx", Font.PLAIN, 15));
		this.add(center, BorderLayout.CENTER);		//将Panel放在Frame的中间
	}

	public void southPanel() {
		Panel south = new Panel();					//创建南边的Panel
		tf = new TextField(15);
		tf.setText("127.0.0.1");
		send = new Button("发 送");
		log = new Button("记 录");
		clear = new Button("清 屏");
		shake = new Button("震 动");
		south.add(tf);
		south.add(send);
		south.add(log);
		south.add(clear);
		south.add(shake);
		this.add(south, BorderLayout.SOUTH); 		//将Panel放在Frame的南边
	}

	public void init() {
		this.setLocation(500, 50);
		this.setSize(400, 600);
		try {
			socket = new DatagramSocket();			//一初始化就创建码头
		} catch (SocketException e) {
			e.printStackTrace();
		}
		new Receive().start();						//一初始化就开启接收,避免两条线程争抢执行权
		this.setVisible(true);
	}
	
	//内部定义一个内部类做接收端,方便调用外部类的方法(无论公有的还是私有的)
	private class Receive extends Thread {			//接收和发送需要同时执行,所以定义成多线程的
		public void run() {
			try {
				socket = new DatagramSocket(9999);
				DatagramPacket packet = new DatagramPacket(new byte[8192], 8192);
				
				while(true) {
					socket.receive(packet); 			//接收信息
					byte[] arr = packet.getData();		//获取字节数据
					int len = packet.getLength();		//获取有效的字节数据长度
					String message = new String(arr, 0, len);
					
					String time = getCurrentTime();		//获取当前时间
					String ip = packet.getAddress().getHostAddress();	//获取ip地址
					viewText.append(time + " " + ip + " 对我说:\r\n" + message + "\r\n\r\n");
				}
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}
	
	public static void main(String[] args) {
		new Demo4_GUIChat2();	//创建一个Frame的子类对象,即创建一个窗体出来
	}
}

UDP聊天记录功能

演示:

import java.awt.BorderLayout;
import java.awt.Button;
import java.awt.Color;
import java.awt.Font;
import java.awt.Frame;
import java.awt.Panel;
import java.awt.TextArea;
import java.awt.TextField;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.BufferedWriter;
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
 * GUI聊天
 */
public class Demo4_GUIChat3 extends Frame{
	private TextField tf;
	private Button send;
	private Button log;
	private Button clear;
	private Button shake;
	private TextArea viewText;
	private TextArea sendText;
	private DatagramSocket socket;
	private BufferedWriter bw;

	public Demo4_GUIChat3() {	//一创建子类对象,构造方法中的代码自动运行
		init();		//将代码抽取成一个方法,防止构造方法中的代码过于臃肿
		southPanel();
		centerPanel();
		event();
	}

	public void event() {
		this.addWindowListener(new WindowAdapter() {
			public void windowClosing(WindowEvent e) {
				try {
					socket.close();						//在退出java虚拟机之前关流
					bw.close();
				} catch (IOException e1) {
					e1.printStackTrace();
				}
				System.exit(0);
			}
		});
		
		send.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				try {
					send();
				} catch (IOException e1) {
					e1.printStackTrace();
				}
			}
		});
		
		log.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				try {
					logFile();
				} catch (IOException e1) {
					e1.printStackTrace();
				}
			}
		});
	}
	
	private void logFile() throws IOException {
		bw.flush();									//刷新缓冲区
		FileInputStream fis = new FileInputStream("config.txt");
		ByteArrayOutputStream baos = new ByteArrayOutputStream();	//在内存中创建缓冲区
		
		int len;
		byte[] arr = new byte[8192];
		while((len = fis.read(arr)) != -1) {
			baos.write(arr, 0, len);
		}
		
		String str = baos.toString();				//将内存中的内容转换成了字符串
		viewText.setText(str);
		
		fis.close();
	}
	
	private void send() throws IOException {		//send属于在主线程里跑 main() - 构造方法 - event() - send()
		String message = sendText.getText();		//获取发送区域的内容
		String ip = tf.getText();					//获取ip地址

		DatagramPacket packet = 
				new DatagramPacket(message.getBytes(), message.getBytes().length, InetAddress.getByName(ip), 9999);
		socket.send(packet); 						//发送数据
		
		String time = getCurrentTime();				//获取当前时间
		String str = time + " 我对:" + ip + " 说\r\n" + message + "\r\n\r\n";	//Alt + Shift + L 抽取局部变量
		viewText.append(str); 						//将信息添加到显示区域中
		bw.write(str);
		sendText.setText("");						//清除发送区域的内容
	}

	private String getCurrentTime() {
		Date d = new Date();						//创建当前日期对象
		SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss");
		return sdf.format(d);						//将时间格式化
	}

	public void centerPanel() {
		Panel center = new Panel();					//创建中间的Panel
		viewText = new TextArea();
		sendText = new TextArea(5, 1);
		//Panel默认流式布局,而我们需要将两个文本区域一个放在Panel的南边,一个放在Panel的中间,应该用边界布局
		center.setLayout(new BorderLayout());		//设置为边界布局管理器
		center.add(sendText, BorderLayout.SOUTH);	//发送的文本区域放在南边
		center.add(viewText, BorderLayout.CENTER);	//显示的文本区域放在中间
		viewText.setEditable(false);				//设置显示的文本区域不可以编辑
		viewText.setBackground(Color.WHITE);		//设置背景颜色 Color类中同一颜色有大写字段和小写字段两种,其实是一个东西。底层代码中,小写的静态常量被RGB三原色作为参数的Color对象赋值,大写的静态常量直接被小写的静态常量赋值。
		sendText.setFont(new Font("xxx", Font.PLAIN, 15));
		viewText.setFont(new Font("xxx", Font.PLAIN, 15));
		this.add(center, BorderLayout.CENTER);		//将Panel放在Frame的中间
	}

	public void southPanel() {
		Panel south = new Panel();					//创建南边的Panel
		tf = new TextField(15);
		tf.setText("127.0.0.1");
		send = new Button("发 送");
		log = new Button("记 录");
		clear = new Button("清 屏");
		shake = new Button("震 动");
		south.add(tf);
		south.add(send);
		south.add(log);
		south.add(clear);
		south.add(shake);
		this.add(south, BorderLayout.SOUTH); 		//将Panel放在Frame的南边
	}

	public void init() {
		this.setLocation(500, 50);
		this.setSize(400, 600);
		try {
			socket = new DatagramSocket();			//一初始化就创建码头
			bw = new BufferedWriter(new FileWriter("config.txt", true));
		} catch (Exception e) {
			e.printStackTrace();
		}
		new Receive().start();						//一初始化就开启接收,避免两条线程争抢执行权
		this.setVisible(true);
	}
	
	//内部定义一个内部类做接收端,方便调用外部类的方法(无论公有的还是私有的)
	private class Receive extends Thread {			//接收和发送需要同时执行,所以定义成多线程的
		public void run() {
			try {
				socket = new DatagramSocket(9999);
				DatagramPacket packet = new DatagramPacket(new byte[8192], 8192);
				
				while(true) {
					socket.receive(packet); 			//接收信息
					byte[] arr = packet.getData();		//获取字节数据
					int len = packet.getLength();		//获取有效的字节数据长度
					String message = new String(arr, 0, len);
					
					String time = getCurrentTime();		//获取当前时间
					String ip = packet.getAddress().getHostAddress();	//获取ip地址
					String str = time + " " + ip + " 对我说:\r\n" + message + "\r\n\r\n";
					viewText.append(str);
					bw.write(str);
				}
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}
	
	public static void main(String[] args) {
		new Demo4_GUIChat3();	//创建一个Frame的子类对象,即创建一个窗体出来
	}
}

UDP聊天清屏功能

演示:

import java.awt.BorderLayout;
import java.awt.Button;
import java.awt.Color;
import java.awt.Font;
import java.awt.Frame;
import java.awt.Panel;
import java.awt.TextArea;
import java.awt.TextField;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.BufferedWriter;
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
 * GUI聊天
 */
public class Demo4_GUIChat4 extends Frame{
	private TextField tf;
	private Button send;
	private Button log;
	private Button clear;
	private Button shake;
	private TextArea viewText;
	private TextArea sendText;
	private DatagramSocket socket;
	private BufferedWriter bw;

	public Demo4_GUIChat4() {	//一创建子类对象,构造方法中的代码自动运行
		init();		//将代码抽取成一个方法,防止构造方法中的代码过于臃肿
		southPanel();
		centerPanel();
		event();
	}

	public void event() {
		this.addWindowListener(new WindowAdapter() {
			public void windowClosing(WindowEvent e) {
				try {
					socket.close();						//在退出java虚拟机之前关流
					bw.close();
				} catch (IOException e1) {
					e1.printStackTrace();
				}
				System.exit(0);
			}
		});
		
		send.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				try {
					send();
				} catch (IOException e1) {
					e1.printStackTrace();
				}
			}
		});
		
		log.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				try {
					logFile();
				} catch (IOException e1) {
					e1.printStackTrace();
				}
			}
		});
		
		clear.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				viewText.setText("");
			}
		});
	}
	
	private void logFile() throws IOException {
		bw.flush();									//刷新缓冲区
		FileInputStream fis = new FileInputStream("config.txt");
		ByteArrayOutputStream baos = new ByteArrayOutputStream();	//在内存中创建缓冲区
		
		int len;
		byte[] arr = new byte[8192];
		while((len = fis.read(arr)) != -1) {
			baos.write(arr, 0, len);
		}
		
		String str = baos.toString();				//将内存中的内容转换成了字符串
		viewText.setText(str);
		
		fis.close();
	}
	
	private void send() throws IOException {		//send属于在主线程里跑 main() - 构造方法 - event() - send()
		String message = sendText.getText();		//获取发送区域的内容
		String ip = tf.getText();					//获取ip地址

		DatagramPacket packet = 
				new DatagramPacket(message.getBytes(), message.getBytes().length, InetAddress.getByName(ip), 9999);
		socket.send(packet); 						//发送数据
		
		String time = getCurrentTime();				//获取当前时间
		String str = time + " 我对:" + ip + " 说\r\n" + message + "\r\n\r\n";	//Alt + Shift + L 抽取局部变量
		viewText.append(str); 						//将信息添加到显示区域中
		bw.write(str);
		sendText.setText("");						//清除发送区域的内容
	}

	private String getCurrentTime() {
		Date d = new Date();						//创建当前日期对象
		SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss");
		return sdf.format(d);						//将时间格式化
	}

	public void centerPanel() {
		Panel center = new Panel();					//创建中间的Panel
		viewText = new TextArea();
		sendText = new TextArea(5, 1);
		//Panel默认流式布局,而我们需要将两个文本区域一个放在Panel的南边,一个放在Panel的中间,应该用边界布局
		center.setLayout(new BorderLayout());		//设置为边界布局管理器
		center.add(sendText, BorderLayout.SOUTH);	//发送的文本区域放在南边
		center.add(viewText, BorderLayout.CENTER);	//显示的文本区域放在中间
		viewText.setEditable(false);				//设置显示的文本区域不可以编辑
		viewText.setBackground(Color.WHITE);		//设置背景颜色 Color类中同一颜色有大写字段和小写字段两种,其实是一个东西。底层代码中,小写的静态常量被RGB三原色作为参数的Color对象赋值,大写的静态常量直接被小写的静态常量赋值。
		sendText.setFont(new Font("xxx", Font.PLAIN, 15));
		viewText.setFont(new Font("xxx", Font.PLAIN, 15));
		this.add(center, BorderLayout.CENTER);		//将Panel放在Frame的中间
	}

	public void southPanel() {
		Panel south = new Panel();					//创建南边的Panel
		tf = new TextField(15);
		tf.setText("127.0.0.1");
		send = new Button("发 送");
		log = new Button("记 录");
		clear = new Button("清 屏");
		shake = new Button("震 动");
		south.add(tf);
		south.add(send);
		south.add(log);
		south.add(clear);
		south.add(shake);
		this.add(south, BorderLayout.SOUTH); 		//将Panel放在Frame的南边
	}

	public void init() {
		this.setLocation(500, 50);
		this.setSize(400, 600);
		try {
			socket = new DatagramSocket();			//一初始化就创建码头
			bw = new BufferedWriter(new FileWriter("config.txt", true));
		} catch (Exception e) {
			e.printStackTrace();
		}
		new Receive().start();						//一初始化就开启接收,避免两条线程争抢执行权
		this.setVisible(true);
	}
	
	//内部定义一个内部类做接收端,方便调用外部类的方法(无论公有的还是私有的)
	private class Receive extends Thread {			//接收和发送需要同时执行,所以定义成多线程的
		public void run() {
			try {
				socket = new DatagramSocket(9999);
				DatagramPacket packet = new DatagramPacket(new byte[8192], 8192);
				
				while(true) {
					socket.receive(packet); 			//接收信息
					byte[] arr = packet.getData();		//获取字节数据
					int len = packet.getLength();		//获取有效的字节数据长度
					String message = new String(arr, 0, len);
					
					String time = getCurrentTime();		//获取当前时间
					String ip = packet.getAddress().getHostAddress();	//获取ip地址
					String str = time + " " + ip + " 对我说:\r\n" + message + "\r\n\r\n";
					viewText.append(str);
					bw.write(str);
				}
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}
	
	public static void main(String[] args) {
		new Demo4_GUIChat4();	//创建一个Frame的子类对象,即创建一个窗体出来
	}
}

UDP聊天震动功能

演示:

import java.awt.BorderLayout;
import java.awt.Button;
import java.awt.Color;
import java.awt.Font;
import java.awt.Frame;
import java.awt.Panel;
import java.awt.TextArea;
import java.awt.TextField;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.BufferedWriter;
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
 * GUI聊天
 */
public class Demo4_GUIChat4 extends Frame{
	private TextField tf;
	private Button send;
	private Button log;
	private Button clear;
	private Button shake;
	private TextArea viewText;
	private TextArea sendText;
	private DatagramSocket socket;
	private BufferedWriter bw;

	public Demo4_GUIChat4() {	//一创建子类对象,构造方法中的代码自动运行
		init();		//将代码抽取成一个方法,防止构造方法中的代码过于臃肿
		southPanel();
		centerPanel();
		event();
	}

	public void event() {
		this.addWindowListener(new WindowAdapter() {
			public void windowClosing(WindowEvent e) {
				try {
					socket.close();						//在退出java虚拟机之前关流
					bw.close();
				} catch (IOException e1) {
					e1.printStackTrace();
				}
				System.exit(0);
			}
		});
		
		send.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				try {
					send();
				} catch (IOException e1) {
					e1.printStackTrace();
				}
			}
		});
		
		log.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				try {
					logFile();
				} catch (IOException e1) {
					e1.printStackTrace();
				}
			}
		});
		
		clear.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				viewText.setText("");
			}
		});
		
		shake.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				try {
					send(new byte[]{-1}, tf.getText());
				} catch (IOException e1) {
					e1.printStackTrace();
				}
			}
		});
	}
	
	private void shake() {
		int x = this.getLocation().x;				//获取横坐标位置
		int y = this.getLocation().y;				//获取纵坐标位置
		
		for(int i = 0; i < 5; i++) {
			try {
				this.setLocation(x + 20, y + 20);
				Thread.sleep(20);
				this.setLocation(x + 20, y - 20);
				Thread.sleep(20);
				this.setLocation(x - 20, y + 20);
				Thread.sleep(20);
				this.setLocation(x - 20, y - 20);
				Thread.sleep(20);
				this.setLocation(x, y);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
	
	private void logFile() throws IOException {
		bw.flush();									//刷新缓冲区
		FileInputStream fis = new FileInputStream("config.txt");
		ByteArrayOutputStream baos = new ByteArrayOutputStream();	//在内存中创建缓冲区
		
		int len;
		byte[] arr = new byte[8192];
		while((len = fis.read(arr)) != -1) {
			baos.write(arr, 0, len);
		}
		
		String str = baos.toString();				//将内存中的内容转换成了字符串
		viewText.setText(str);
		
		fis.close();
	}
	
	private void send(byte[] arr, String ip) throws IOException {
		DatagramPacket packet = 
				new DatagramPacket(arr, arr.length, InetAddress.getByName(ip), 9999);
		socket.send(packet); 						//发送数据
	}
	
	private void send() throws IOException {		//send属于在主线程里跑 main() - 构造方法 - event() - send()
		String message = sendText.getText();		//获取发送区域的内容
		String ip = tf.getText();					//获取ip地址
		
		send(message.getBytes(), ip);
		
		String time = getCurrentTime();				//获取当前时间
		String str = time + " 我对:" + ip + " 说\r\n" + message + "\r\n\r\n";	//Alt + Shift + L 抽取局部变量
		viewText.append(str); 						//将信息添加到显示区域中
		bw.write(str);
		sendText.setText("");						//清除发送区域的内容
	}

	private String getCurrentTime() {
		Date d = new Date();						//创建当前日期对象
		SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss");
		return sdf.format(d);						//将时间格式化
	}

	public void centerPanel() {
		Panel center = new Panel();					//创建中间的Panel
		viewText = new TextArea();
		sendText = new TextArea(5, 1);
		//Panel默认流式布局,而我们需要将两个文本区域一个放在Panel的南边,一个放在Panel的中间,应该用边界布局
		center.setLayout(new BorderLayout());		//设置为边界布局管理器
		center.add(sendText, BorderLayout.SOUTH);	//发送的文本区域放在南边
		center.add(viewText, BorderLayout.CENTER);	//显示的文本区域放在中间
		viewText.setEditable(false);				//设置显示的文本区域不可以编辑
		viewText.setBackground(Color.WHITE);		//设置背景颜色 Color类中同一颜色有大写字段和小写字段两种,其实是一个东西。底层代码中,小写的静态常量被RGB三原色作为参数的Color对象赋值,大写的静态常量直接被小写的静态常量赋值。
		sendText.setFont(new Font("xxx", Font.PLAIN, 15));
		viewText.setFont(new Font("xxx", Font.PLAIN, 15));
		this.add(center, BorderLayout.CENTER);		//将Panel放在Frame的中间
	}

	public void southPanel() {
		Panel south = new Panel();					//创建南边的Panel
		tf = new TextField(15);
		tf.setText("127.0.0.1");
		send = new Button("发 送");
		log = new Button("记 录");
		clear = new Button("清 屏");
		shake = new Button("震 动");
		south.add(tf);
		south.add(send);
		south.add(log);
		south.add(clear);
		south.add(shake);
		this.add(south, BorderLayout.SOUTH); 		//将Panel放在Frame的南边
	}

	public void init() {
		this.setLocation(500, 50);
		this.setSize(400, 600);
		try {
			socket = new DatagramSocket();			//一初始化就创建码头
			bw = new BufferedWriter(new FileWriter("config.txt", true));
		} catch (Exception e) {
			e.printStackTrace();
		}
		new Receive().start();						//一初始化就开启接收,避免两条线程争抢执行权
		this.setVisible(true);
	}
	
	//内部定义一个内部类做接收端,方便调用外部类的方法(无论公有的还是私有的)
	private class Receive extends Thread {			//接收和发送需要同时执行,所以定义成多线程的
		public void run() {
			try {
				socket = new DatagramSocket(9999);
				DatagramPacket packet = new DatagramPacket(new byte[8192], 8192);
				
				while(true) {
					socket.receive(packet); 			//接收信息
					byte[] arr = packet.getData();		//获取字节数据
					int len = packet.getLength();		//获取有效的字节数据长度
					if(arr[0] == -1 && len == 1) {		//如果发过来的数组第一个存储的值是1,并且数组长度是1
						shake();						//调用震动方法
						continue;						//终止本次循环,继续下次循环,因为震动后不需要执行下面的代码
					}
					String message = new String(arr, 0, len);
					
					String time = getCurrentTime();		//获取当前时间
					String ip = packet.getAddress().getHostAddress();	//获取ip地址
					String str = time + " " + ip + " 对我说:\r\n" + message + "\r\n\r\n";
					viewText.append(str);
					bw.write(str);
				}
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}
	
	public static void main(String[] args) {
		new Demo4_GUIChat4();	//创建一个Frame的子类对象,即创建一个窗体出来
	}
}

UDP聊天快捷键和代码优化

演示:

import java.awt.BorderLayout;
import java.awt.Button;
import java.awt.Color;
import java.awt.Font;
import java.awt.Frame;
import java.awt.Panel;
import java.awt.TextArea;
import java.awt.TextField;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.BufferedWriter;
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
 * GUI聊天
 */
public class Demo4_GUIChat4 extends Frame{
	private TextField tf;
	private Button send;
	private Button log;
	private Button clear;
	private Button shake;
	private TextArea viewText;
	private TextArea sendText;
	private DatagramSocket socket;
	private BufferedWriter bw;

	public Demo4_GUIChat4() {	//一创建子类对象,构造方法中的代码自动运行
		init();		//将代码抽取成一个方法,防止构造方法中的代码过于臃肿
		southPanel();
		centerPanel();
		event();
	}

	public void event() {
		this.addWindowListener(new WindowAdapter() {
			public void windowClosing(WindowEvent e) {
				try {
					socket.close();						//在退出java虚拟机之前关流
					bw.close();
				} catch (IOException e1) {
					e1.printStackTrace();
				}
				System.exit(0);
			}
		});
		
		send.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				try {
					send();
				} catch (IOException e1) {
					e1.printStackTrace();
				}
			}
		});
		
		log.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				try {
					logFile();
				} catch (IOException e1) {
					e1.printStackTrace();
				}
			}
		});
		
		clear.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				viewText.setText("");
			}
		});
		
		shake.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				try {
					send(new byte[]{-1}, tf.getText());
				} catch (IOException e1) {
					e1.printStackTrace();
				}
			}
		});
		
		sendText.addKeyListener(new KeyAdapter() {
			public void keyReleased(KeyEvent e) {
//				if(e.getKeyCode() == KeyEvent.VK_ENTER && e.isControlDown()) {	//isControlDown	Ctrl键是否被按下
					if(e.getKeyCode() == KeyEvent.VK_ENTER) {
					try {
						send();
					} catch (IOException e1) {
						e1.printStackTrace();
					}
				}
			}
		});
	}
	
	private void shake() {
		int x = this.getLocation().x;				//获取横坐标位置
		int y = this.getLocation().y;				//获取纵坐标位置
		
		for(int i = 0; i < 5; i++) {
			try {
				this.setLocation(x + 20, y + 20);
				Thread.sleep(20);
				this.setLocation(x + 20, y - 20);
				Thread.sleep(20);
				this.setLocation(x - 20, y + 20);
				Thread.sleep(20);
				this.setLocation(x - 20, y - 20);
				Thread.sleep(20);
				this.setLocation(x, y);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
	
	private void logFile() throws IOException {
		bw.flush();									//刷新缓冲区
		FileInputStream fis = new FileInputStream("config.txt");
		ByteArrayOutputStream baos = new ByteArrayOutputStream();	//在内存中创建缓冲区
		
		int len;
		byte[] arr = new byte[8192];
		while((len = fis.read(arr)) != -1) {
			baos.write(arr, 0, len);
		}
		
		String str = baos.toString();				//将内存中的内容转换成了字符串
		viewText.setText(str);
		
		fis.close();
	}
	
	private void send(byte[] arr, String ip) throws IOException {
		DatagramPacket packet = 
				new DatagramPacket(arr, arr.length, InetAddress.getByName(ip), 9999);
		socket.send(packet); 						//发送数据
	}
	
	private void send() throws IOException {		//send属于在主线程里跑 main() - 构造方法 - event() - send()
		String message = sendText.getText();		//获取发送区域的内容
		String ip = tf.getText();					//获取ip地址
		ip = ip.trim().length() == 0 ? "255.255.255.255" : ip;
		
		send(message.getBytes(), ip);
		
		String time = getCurrentTime();				//获取当前时间
		String str = time + " 我对:" + (ip.equals("255.255.255.255") ? "所有人" : ip) + " 说\r\n" + message + "\r\n\r\n";	//Alt + Shift + L 抽取局部变量
		viewText.append(str); 						//将信息添加到显示区域中
		bw.write(str);
		sendText.setText("");						//清除发送区域的内容
	}

	private String getCurrentTime() {
		Date d = new Date();						//创建当前日期对象
		SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss");
		return sdf.format(d);						//将时间格式化
	}

	public void centerPanel() {
		Panel center = new Panel();					//创建中间的Panel
		viewText = new TextArea();
		sendText = new TextArea(5, 1);
		//Panel默认流式布局,而我们需要将两个文本区域一个放在Panel的南边,一个放在Panel的中间,应该用边界布局
		center.setLayout(new BorderLayout());		//设置为边界布局管理器
		center.add(sendText, BorderLayout.SOUTH);	//发送的文本区域放在南边
		center.add(viewText, BorderLayout.CENTER);	//显示的文本区域放在中间
		viewText.setEditable(false);				//设置显示的文本区域不可以编辑
		viewText.setBackground(Color.WHITE);		//设置背景颜色 Color类中同一颜色有大写字段和小写字段两种,其实是一个东西。底层代码中,小写的静态常量被RGB三原色作为参数的Color对象赋值,大写的静态常量直接被小写的静态常量赋值。
		sendText.setFont(new Font("xxx", Font.PLAIN, 15));
		viewText.setFont(new Font("xxx", Font.PLAIN, 15));
		this.add(center, BorderLayout.CENTER);		//将Panel放在Frame的中间
	}

	public void southPanel() {
		Panel south = new Panel();					//创建南边的Panel
		tf = new TextField(15);
		tf.setText("127.0.0.1");
		send = new Button("发 送");
		log = new Button("记 录");
		clear = new Button("清 屏");
		shake = new Button("震 动");
		south.add(tf);
		south.add(send);
		south.add(log);
		south.add(clear);
		south.add(shake);
		this.add(south, BorderLayout.SOUTH); 		//将Panel放在Frame的南边
	}

	public void init() {
		this.setLocation(500, 50);
		this.setSize(400, 600);
		try {
			socket = new DatagramSocket();			//一初始化就创建码头
			bw = new BufferedWriter(new FileWriter("config.txt", true));
		} catch (Exception e) {
			e.printStackTrace();
		}
		new Receive().start();						//一初始化就开启接收,避免两条线程争抢执行权
		this.setVisible(true);
	}
	
	//内部定义一个内部类做接收端,方便调用外部类的方法(无论公有的还是私有的)
	private class Receive extends Thread {			//接收和发送需要同时执行,所以定义成多线程的
		public void run() {
			try {
				socket = new DatagramSocket(9999);
				DatagramPacket packet = new DatagramPacket(new byte[8192], 8192);
				
				while(true) {
					socket.receive(packet); 			//接收信息
					byte[] arr = packet.getData();		//获取字节数据
					int len = packet.getLength();		//获取有效的字节数据长度
					if(arr[0] == -1 && len == 1) {		//如果发过来的数组第一个存储的值是1,并且数组长度是1
						shake();						//调用震动方法
						continue;						//终止本次循环,继续下次循环,因为震动后不需要执行下面的代码
					}
					String message = new String(arr, 0, len);
					
					String time = getCurrentTime();		//获取当前时间
					String ip = packet.getAddress().getHostAddress();	//获取ip地址
					String str = time + " " + ip + " 对我说:\r\n" + message + "\r\n\r\n";
					viewText.append(str);
					bw.write(str);
				}
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}
	
	public static void main(String[] args) {
		new Demo4_GUIChat4();	//创建一个Frame的子类对象,即创建一个窗体出来
	}
}

UDP聊天生成jar文件

右键项目文件 - Import... - 选择Java文件夹中的JAR file选项 点Next > - 确认是不是你选中的原文件 - 在Select the export destination:中选择生成的地方 - 不是直接点Finish 接着点Next > - 再点Next > - 在Select the class of the application entry point:中选择刚才那个类 然后点Finish - 在弹出的提示中选OK(网上基本也是这种方法,但我失败了= _ =!!)


TCP协议

客户端

创建Socket连接服务端(指定ip地址,端口号)通过ip地址找对应的服务器。

调用Socket的getInputStream()和getOutputStream()方法获取和服务端相连的IO流。

输入流可以读取服务端输出流写出的数据。

输出流可以写出数据到服务端的输入流。

演示:

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

/**
 * 客户端
 */
public class Demo1_Client {
	public static void main(String[] args) throws UnknownHostException, IOException {
		Socket socket = new Socket("127.0.0.1", 12345);
		InputStream is = socket.getInputStream();		//获取客户端的输入流
		OutputStream os = socket.getOutputStream();		//获取客户端的输出流	
		
		byte[] arr = new byte[1024];
		int len = is.read(arr);							//读取服务器发过来的数据
		System.out.println(new String(arr, 0, len));	//将数据转换成字符串并打印
		
		os.write("学习挖掘机数据哪家强?".getBytes()); 		//客户端向服务器写数据
		
		socket.close();
	}
}

服务端

创建ServerSocket(需要指定端口号)。

调用ServerSocket的accept()方法接收一个客户端请求,得到一个Socket。

调用Socket的getInputStream()和getOutputStream()方法获取和客户端相连的IO流。

输入流可以读取客户端输出流写出的数据。

输出流可以写出数据到客户端的输入流。

演示:

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

/**
 * 服务端
 */
public class Demo2_Server {
	public static void main(String[] args) throws IOException {
		ServerSocket server = new ServerSocket(12345);
		
		Socket socket = server.accept();			//接受客户端的请求
		InputStream is = socket.getInputStream();	//获取服务端的输入流
		OutputStream os = socket.getOutputStream();	//获取服务端的输出流	
		
		os.write("百度一下你就知道".getBytes());			//服务器向客户端写出数据
		
		byte[] arr = new byte[1024];
		int len = is.read(arr);							//读取客户端发过来的数据
		System.out.println(new String(arr, 0, len));	//将数据转换成字符串并打印
		
		socket.close();
	}
}

TCP协议代码优化

客户端 演示:i

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintStream;
import java.net.Socket;
import java.net.UnknownHostException;

/**
 * 客户端
 */
public class Demo2_Client2 {
	public static void main(String[] args) throws UnknownHostException, IOException {
		Socket socket = new Socket("127.0.0.1", 12345);
		BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));		//将字节流包装成了字符流
		PrintStream ps = new PrintStream(socket.getOutputStream());			//PrintStream中有写出换行的的方法
		
		ps.println("Hello~ 酷狗");
		System.out.println(br.readLine());
		ps.println("淦,我还以为你想开QQ音乐呢");
		System.out.println(br.readLine());
		
		socket.close();
	}
}

服务端 演示:

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintStream;
import java.net.ServerSocket;
import java.net.Socket;

/**
 * 服务端
 */
public class Demo2_Server2 {
	public static void main(String[] args) throws IOException {
		ServerSocket server = new ServerSocket(12345);
		
		Socket socket = server.accept();			//接受客户端的请求
		BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));		//将字节流包装成了字符流
		PrintStream ps = new PrintStream(socket.getOutputStream());			//PrintStream中有写出换行的的方法

		System.out.println(br.readLine());			//br.readLine()在读的的时候以判断\r\n为结束标记的。如果客户端在写入消息时用print而不是println。则读取不会结束,下一句也不会写入。因此,客户端也无法读取,客户端的下一句也无法写入。程序会僵持住
		ps.println("对不起,我想开的是网易云音乐,点错了~");
		System.out.println(br.readLine());
		ps.println("一起来网易云听音乐吧,这里的听众有各种各样公司的程序员");
		
		socket.close();
	}
}

服务端是多线程的

演示:

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintStream;
import java.net.ServerSocket;
import java.net.Socket;

/**
 * 服务端
 */
public class Demo2_Server2 {
	public static void main(String[] args) throws IOException {
		ServerSocket server = new ServerSocket(12345);
		
		while(true) {
			final Socket socket = server.accept();			//接受客户端的请求
			new Thread() {
				public void run() {
					try {
						BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));		//将字节流包装成了字符流
						PrintStream ps = new PrintStream(socket.getOutputStream());			//PrintStream中有写出换行的的方法
						
						System.out.println(br.readLine());			
						ps.println("对不起,我想开的是网易云音乐,点错了~");
						System.out.println(br.readLine());
						ps.println("一起来网易云听音乐吧,这里的听众有各种各样公司的程序员");
						socket.close();
					} catch (IOException e) {
						e.printStackTrace();
					}
				}
			}.start();
		}
	}
}

网络编程练习一

客户端向服务器写字符串(键盘录入),服务器(多线程)将字符串反转后写回,客户端再次读取到是反转后的字符串。

客户端演示:

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.Scanner;

public class Test1_Client {
	/**
	 * 客户端向服务器写字符串(键盘录入),服务器(多线程)将字符串反转后写回,客户端再次读取到是反转后的字符串。
	 * @param args
	 * @throws IOException 
	 * @throws UnknownHostException 
	 */
	public static void main(String[] args) throws UnknownHostException, IOException {
		Scanner sc = new Scanner(System.in);			//创建键盘录入对象
		Socket socket = new Socket("127.0.0.1", 54321);	//创建客户端,指定ip地址和端口号
		
		BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));	//获取输入流
		PrintStream ps = new PrintStream(socket.getOutputStream());		//获取输出流
		
		ps.println(sc.nextLine()); 			//将字符串写到服务器去
		System.out.println(br.readLine());	//将反转后的结果读出来
		
		socket.close();
	}
}

服务端演示:

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

public class Test1_Server {
	public static void main(String[] args) throws IOException {
		ServerSocket server = new ServerSocket(54321);
		System.out.println("服务器启动,绑定54321端口");
		
		while(true) {
			/*final*/ Socket socket = server.accept();	//接受客户端的请求  不加final也行,应该是版本更新后改了
			
			new Thread() {
				public void run() {						//开启一条线程
					try {
						BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));	//获取输入流
						PrintStream ps = new PrintStream(socket.getOutputStream());		//获取输出流
						
						String line = br.readLine();	//将客户端写过来的数据读取出来
						line = new StringBuilder(line).reverse().toString();			//将字符串转换成StringBuilder再进行反转再重新转换成字符串
						ps.println(line);				//反转后写回去
						
						socket.close();		//Socket类的close()中写到:关闭此套接字也将会关闭套接字的InputStream和OutputStream. 所以br和ps不用单独关.
					} catch (IOException e) {
						e.printStackTrace();
					}
				}
			}.start();
		}
	}
}

网络编程练习二

客户端向服务器上传文件。

客户端演示:

import java.io.BufferedReader;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.ServerSocket;
import java.net.Socket;

public class Demo2_UpdateServer {
	public static void main(String[] args) throws IOException {
		//3.建立多线程的服务器
		ServerSocket server = new ServerSocket(12345);
		System.out.println("服务器启动,绑定12345端口号");
		
		//4.读取文件名
		while(true) {
			Socket socket = server.accept();				//接受请求
			new Thread() {
				public void run() {
					try {
						InputStream is = socket.getInputStream();
						BufferedReader br = new BufferedReader(new InputStreamReader(is));
						PrintStream ps = new PrintStream(socket.getOutputStream());
						String fileName = br.readLine();
						
						//5.判断文件是否存在,将结果发回客户端
						File dir = new File("update");
						dir.mkdir();						//创建文件夹
						File file = new File(dir, fileName);//封装成File对象
						
						if(file.exists()) {					//如果服务器已经存在这个文件
							ps.println("存在"); 				//将存在写给客户端
							socket.close(); 				//关闭socket
							return;
						}else {
							ps.println("不存在");
						}
						
						//8.定义FileOutputStream,从网络读取数据,存储到本地
						FileOutputStream fos = new FileOutputStream(file);
						byte[] arr = new byte[8192];
						int len;
						
						while((len = is.read(arr)) != -1) {
							fos.write(arr, 0, len);
						}
						
						fos.close();
						socket.close();
					} catch (IOException e) {
						e.printStackTrace();
					}
				}
			}.start();
		}
	}
}

服务端演示:

import java.io.BufferedReader;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.ServerSocket;
import java.net.Socket;

public class Demo2_UpdateServer {
	public static void main(String[] args) throws IOException {
		//3.建立多线程的服务器
		ServerSocket server = new ServerSocket(12345);
		System.out.println("服务器启动,绑定12345端口号");
		
		//4.读取文件名
		while(true) {
			Socket socket = server.accept();				//接受请求
			new Thread() {
				public void run() {
					try {
						InputStream is = socket.getInputStream();
						BufferedReader br = new BufferedReader(new InputStreamReader(is));
						PrintStream ps = new PrintStream(socket.getOutputStream());
						String fileName = br.readLine();
						
						//5.判断文件是否存在,将结果发回客户端
						File dir = new File("update");
						dir.mkdir();						//创建文件夹
						File file = new File(dir, fileName);//封装成File对象
						
						if(file.exists()) {					//如果服务器已经存在这个文件
							ps.println("存在"); 				//将存在写给客户端
							socket.close(); 				//关闭socket
							return;
						}else {
							ps.println("不存在");
						}
						
						//8.定义FileOutputStream,从网络读取数据,存储到本地
						FileOutputStream fos = new FileOutputStream(file);
						byte[] arr = new byte[8192];
						int len;
						
						while((len = is.read(arr)) != -1) {
							fos.write(arr, 0, len);
						}
						
						fos.close();
						socket.close();
					} catch (IOException e) {
						e.printStackTrace();
					}
				}
			}.start();
		}
	}
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值