此篇博客是基于传智播客出版的《java 基础入门》一书中的第十章网络编程基础上写的代码,具体细节并未在此篇博客中出现,不懂的小伙伴可以参考原书,这里是关于哪本书中的几个程序的实现,包括UDP网络程序,简单的TCP网络程序,以及多线程的TCP网络程序,还有一个关于TCP案例–文件上传。ps:代码都是先写服务器端,再次是客户端,但UDP不分服务器端和客户端,所以分为接收端还有发送端。
1. UDP网络程序
(1)接收端程序

package com.ithei.tcpandipUDP;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;

/*
 * 接收端程序
 */
public class Example02 {
	public static void main(String[] args) throws Exception {
		//创建一个长度为1024字节数组,用于接受数据
		byte [] buff=new byte[1024];
		//2. 定义一个码头,监听的端口号为8954,这样发送端就能通过这个端口与接收端进行通信
		DatagramSocket ds=new DatagramSocket(8954);
		//3. 定义一个集装箱,用于接受数据
		DatagramPacket dp=new DatagramPacket(buff,1024);
		//4. 等待接收数据,如果没有数据则会发生堵塞
		System.out.println("等待接收数据......");
		ds.receive(dp);//数据会自动填充到集装箱中
		//信息:调用集装箱的方法获取接收到的信息,包括数据的内容,长度,发送的ip地址和端口号
		String str=new String(dp.getData(),0,dp.getLength())+" from "+dp.getAddress().getHostAddress()+":"+dp.getPort();
		//5. 打印接收到的信息
		System.out.println(str);
		//6. 释放资源
		ds.close();
	}
}

(2)发送端程序

package com.ithei.tcpandipUDP;

import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;

//发送端
public class Example03 {
	public static void main(String[] args) throws Exception {
		//创建一个码头:这里可以不进行端口的指定
		/*
		 * 端口的指定的意义:为了每次运行时接收端的getPort()方法返回值都是一致的,
		 * 否则发送端的端口号有系统自动分配,接收端的getPort()方法的返回值每次都不同
		 * 注意:这里可能会出现java.net.BindException说明编写的UDP程序所使用的端口号已经被其他的程序占用
		 * 解决方案:在命令窗口输入"netstat -anb"命令查看当前计算机端口的占用那个情况
		 * 			只需要关闭占用端口号的应用程序或者为程序分配一个未被占用的端口号即可
		 */
		DatagramSocket ds=new DatagramSocket(3000);
		//准备要发送的数据
		String str="hello world";
		//创建一个要发送的包:包括发送的数据,数据的长度,接收端的ip地址以及端口号
		/*
		 * 接收端的ip地址以及端口号:端口号必须和接收端指定的端口号一致,
		 * 这样调用码头的send()方法才能将数据发送到对应的接收端
		 */
		DatagramPacket dp=new DatagramPacket(str.getBytes(),str.length(),InetAddress.getByName("localhost"),8954);
		//发送数据
		System.out.println("发送信息....");
		ds.send(dp);
		//释放资源
		ds.close();
	}
}

2. TCP通信
(1)服务端

package com.ithei.tcpandipUDP;

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

/*
 * TCP通信和UDP通信一样,都能实现两台计算机之间的通信,通信的两端都需要创建Socket对象,
 * 区别在于:UDP中只有接收端和发送端,不区分客户端和服务端,计算机之间可以任意的发送数据。
 * 而TCP通信是严格区分客户端与服务端的,在通信时,必须先有客户端去连接服务端才能实现通信,
 * 服务端不可以主动链接客户端,并且服务端程序需要实现启动,等待客户端的实现
 */
//实现服务端程序
public class Example04 {

	public static void main(String[] args) throws Exception {
		new TCPServer().listener();

	}

}
//TCP服务端
class TCPServer{
	//定义一个端口号
	private static final int PORT=7788;
	//2. 创建一个listener()方法
	public void listener() throws Exception {
		//3. 创建一个服务端对象
		ServerSocket serverSocket=new ServerSocket(PORT);
		//4. 调用服务端对象的accept()方法接收数据
		Socket client=serverSocket.accept();
		/*
		 * socket对象,即客户端对象的API:InputStream getInputStream()的方法介绍:
		 * 该方法返回一个InputStream类型的输入流对象,
		 * 如果该对象是由服务器端的Socket返回,就用于读取客户端发送的数据,
		 * 反之,用于读取服务端发送的数据
		 */
		//获得客户端的输出流
		OutputStream os=client.getOutputStream();
		System.out.println("开始与客户端交互数据....");
		String str="这是一个简单的TCP网络程序的实现哟....";
		os.write(str.getBytes());
		//5. 模拟执行其他功能占用的时间
		Thread.sleep(5000);
		System.out.println("结束与客户端交互数据");
		os.close();
		client.close();
	}
}

(2) 客户端

package com.ithei.tcpandipUDP;

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

//客户端
public class Example05 {

	public static void main(String[] args) throws Exception {
		new TCPClient().connect();
	}

}
//TCP客户端
class TCPClient{
	//1. 服务端的端口号
	private static final int PORT=7788;
	//2. 创建一个connect()方法
	public void connect() throws Exception {
		//3. 创建一个Socket()并连接到给出地址和端口号的计算机
		Socket client=new Socket(InetAddress.getLocalHost(),PORT);
		//4. 定义并且输出数据
		InputStream is=client.getInputStream();
		byte [] buff=new byte[1024];
		int len=is.read(buff);//将数据读到缓冲区中
		System.out.println(new String(buff,0,len));
		//5. 释放资源
		client.close();
	}
	
}

3. 多线程的TCP网络程序

package com.ithei.tcpandipUDP;

import java.io.IOException;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
//可以开启三个客户端程序进行校验,具体代码可以参考TCP客户端的程序
public class Example06 {

	public static void main(String[] args) throws Exception {
		new TCPServer1().listen();

	}

}
class TCPServer1{
	//定义一个端口号
		private static final int PORT=7788;
		public void listen() throws Exception {
			ServerSocket serverSocket=new ServerSocket(PORT);
			while(true) {
				final Socket client=serverSocket.accept();
				new Thread() {
					OutputStream os;
					public void run() {
						try {
							os=client.getOutputStream();
							System.out.println("开始与客户端交互数据....");
							String str="这是一个多线程的TCP网络程序的实现哟....";
							os.write(str.getBytes());
							Thread.sleep(5000);
							System.out.println("结束与客户端交互数据");
							os.close();
							client.close();
						} catch (Exception e) {
							e.printStackTrace();
						}
					};
				}.start();
			}
		}
		
		
}

4、 TCP案例—文件上传
(1)服务端的代码

package com.ithei.tcpandipUDP;

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

/*
 * 目前大多数的服务器都会提供文件上传的功能,
 * 由于文件上传需要数据的安全性和完整性,很明显需要TCP协议来实现
 */
//服务端的程序,用来接收图片
public class Example07 {

	public static void main(String[] args) throws Exception {
		//创建ServerSocket对象
		ServerSocket serverSocket=new ServerSocket(10001);
		while(true) {
			//调用accept方法接收客户端的请求,获得Socket对象
			Socket client=serverSocket.accept();
			//每当客户端建立socket连接后,单独开启一个线程处理和客户端的交互
			new Thread(new ServerThread(client)).start();;
		}
	}
}
class ServerThread implements Runnable{
	//持有一个客户端对象的属性
	private Socket socket;
	//构造方法把Socket对象作为实参传入
	public ServerThread(Socket socket) {
		this.socket=socket;
	}
	@Override
	public void run() {
		//获取客户端的ip地址
		String ip = socket.getInetAddress().getHostAddress();
		//上传图片的个数
		int count=1;
		//创造上传图片目录的file对象,如果没有,就创建
		try {
			InputStream is=socket.getInputStream();
			File parentFile=new File("d:\\upload\\");
			if(!parentFile.exists()) {
				parentFile.mkdir();
			}
			//把客户端ip地址作为上传文件的文件名,如果重名count++
			File file=new File(parentFile, ip+"("+count+").jpg");
			while(file.exists()) {
				file=new File(parentFile, ip+"("+(count++)+").jpg");
			}
			//创建一个 FileOutputStream对象
			FileOutputStream fos=new FileOutputStream(file);
			byte [] buff=new byte[1024];
			int len=0;
			while((len=is.read(buff))!=-1) {
				fos.write(buff,0,len);
			}
			//获得服务端的输出流
			OutputStream out=socket.getOutputStream();
			out.write("上传成功!".getBytes());
			//关闭资源
			fos.close();
			socket.close();
		} catch (IOException e) {
			throw new RuntimeException(e);
		}
	}
}

(2)客户端的代码

package com.ithei.tcpandipUDP;

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

public class Example08 {

	public static void main(String[] args) throws Exception {
		//1. 创建客户端Socket
		Socket socket=new Socket("127.0.0.1",10001);
		//2. 获取socket的输出流对象
		OutputStream out=socket.getOutputStream();
		//3. 创建FileInputStream对象
		FileInputStream fis=new FileInputStream("D:\\1.jpg");
		//4. 循环读取数据
		byte [] buff=new byte[1024];
		int len;
		while((len=fis.read(buff))!=-1) {
			out.write(buff,0,len);
		}
		//关闭客户端输出流
		/*
		 * 注意:shutdownOutput()方法非常重要,因为服务器端程序在while循环中读取客户端发送的数据,
		 * 当读到-1时才会结束循环,如果用户在客户端不调用shutdownOutput()关闭输出流,服务器就不会读到-1,
		 * 而会一直执行while循环,同时客户端读取服务器端数据fis.read(buff) 方法也是一个阻塞方法,这样
		 * 服务器端和客户端程序进入了一个死锁状态,两个程序都不会结束
		 */
		socket.shutdownOutput();
		//获取Socket客户端输入流,接收服务端的信息
		InputStream in=socket.getInputStream();
		byte [] bufMsg=new byte[1024];
		int num=in.read(bufMsg);
		String Msq=new String(bufMsg,0,num);
		System.out.println(Msq);
		//关闭资源
		fis.close();
		socket.close();

	}

}