聊天室系列02:用UDP和TCP编程实现双人聊天、文件传输、多人登录

一、Socket编程 UDP基本步骤

概述

1、使用基于UDP协议的Socket网络编程实现
2、不需要利用I0流实现数据的传输
3、每个数据发送单元被统一封装成数据包的方式,发送方将数据包发送到网络中,数据包在网络中去寻找他的目的地

  • DatagramSocket:用于发送或接受数据包的套接字
  • DatagramPacket:数据包

发送数据一般不超过60K,比如微信发消息

实例一:自动发送指定信息

接收端(服务器):

步骤1、用DatagramSocket制定端口,创建接收端
步骤2、准备容器,封装成DatagramPacket包裹
步骤3、阻塞式接收报包receive(DatagramPacket p)
步骤4、分析数据:
getData()
getLength()
步骤5、释放资源

代码实现:
public class UdpServer {
	public static void main(String[] args) throws Exception {
		System.out.println("接收方启动中...");
//		1、用DatagramSocket制定端口,创建接收端
		DatagramSocket server=new DatagramSocket(9999);
//		2、准备容器,封装成DatagramPacket包裹
		byte[] container=new byte[1024*60];   //容器最大为60K
		DatagramPacket packet=new DatagramPacket(container,0,container.length);
//		3、阻塞式接收报包receive(DatagramPacket p) 
		server.receive(packet);
//		4、分析数据  getData() getLength() 
		byte[] datas=packet.getData();
		int len=packet.getLength();
		System.out.println(new String(datas,0,len));
//		5、释放资源
		server.close();
	}
}
发送端(客户端):

步骤1、用DatagramSocket指定端口,创建接收端
步骤2、准备数据,一定转成字节数组
步骤3、封装成DatagramPacket包裹 ,需要指定目的地
步骤4、发送包裹,sent(DatagramPacket p)
步骤5、释放资源

代码实现:
public class UdpClient {
	public static void main(String[] args) throws Exception {
		System.out.println("发送方启动中...");
//		1、用DatagramSocket制定端口,创建发送端
		DatagramSocket client=new DatagramSocket(8888);
//		2、准备数据,一定转成字节数组
		String data="珍惜时间!持之以恒";
		byte[] datas=data.getBytes();
//		3、封装成DatagramPacket包裹 ,需要指定目的地
		DatagramPacket packet=new DatagramPacket(datas,0,datas.length,new InetSocketAddress("localhost",9999));
//		4、发送包裹,sent(DatagramPacket p) 
		client.send(packet);
//		5、释放资源
		client.close();
	}
}
效果:

在这里插入图片描述

实例二:UDP上传文件

服务器与客户端双方实现的步骤,和上述一致。

服务器端代码:
public class UdpFileServer {
	public static void main(String[] args) throws Exception {
		System.out.println("接收方启动中...");
//		1、用DatagramSocket制定端口,创建接收端
		DatagramSocket server=new DatagramSocket(9999);
//		2、准备容器,封装成DatagramPacket包裹
		byte[] container=new byte[1024*60];   //容器最大为60K
		DatagramPacket packet=new DatagramPacket(container,0,container.length);
//		3、阻塞式接收报包receive(DatagramPacket p) 
		server.receive(packet);
//		4、分析数据  getData() getLength() 
		byte[] datas=packet.getData();
		int len=packet.getLength();
		//读取的顺序和写出的顺序保持一致
		IOUtils.byteArrayTofile(datas, "E:/Java/copy.jpg");
		//		5、释放资源
		server.close();
	}
}
客户端代码:
public class UdpFileClient {
	static byte[] datas;
	public static void main(String[] args) throws Exception {
		System.out.println("发送方启动中...");
//		1、用DatagramSocket制定端口,创建发送端
		DatagramSocket client=new DatagramSocket(8888);
//		2、准备数据,转成字节数组
		byte[] datas = IOUtils.fileToByteArray("C:/Users/ZhouBaiqing/Pictures/Saved Pictures/sun_mountain.jpg");
//		3、封装成DatagramPacket包裹 ,需要指定目的地
		DatagramPacket packet=new DatagramPacket(datas,0,datas.length,new InetSocketAddress("localhost",9999));
//		4、发送包裹,sent(DatagramPacket p) 
		client.send(packet);
//		5、释放资源
		client.close();
	}
}

实例三:在线聊天

---------------------双发可同时发送信息---------------------

服务器端代码(面向对象封装):
public class TalkReceive implements Runnable{
	private DatagramSocket server;
	private String from;
	public TalkReceive(int Port,String from){
		this.from = from;
		try {
			//1、用DatagramSocket制定端口,创建接收端
			server=new DatagramSocket(Port);
		} catch (SocketException e) {
			e.printStackTrace();
		}
	}
	public void run(){
		//2、准备容器,封装成DatagramPacket包裹
		while(true){
		byte[] container=new byte[1024*60];   //容器最大为60K
		DatagramPacket packet=new DatagramPacket(container,0,container.length);
		//3、阻塞式接收报包receive(DatagramPacket p) 
		try {
			server.receive(packet);
			//4、分析数据  getData() getLength() 
			byte[] datas=packet.getData();
			int len=packet.getLength();
			String data = new String(datas,0,len);
			System.out.println(from+":"+data);
			if(data.equals("exit")) break;
		} catch (IOException e) {
			e.printStackTrace();
		}
		}
		//5、释放资源
		server.close();
	}
}
客户端代码(面向对象封装):
public class TalkSend implements Runnable{
	private DatagramSocket client;
	private BufferedReader reader;
	private String toIP;
	private int toPort;
	public TalkSend(int port,String toIP,int toPort){
		this.toIP=toIP;
		this.toPort=toPort;
		try {
			//1、用DatagramSocket制定端口,创建发送端
			client = new DatagramSocket(port);
			//2、准备数据,一定转成字节数组
			reader = new BufferedReader(new InputStreamReader(System.in));
		} catch (SocketException e) {			
			e.printStackTrace();
		}
	}
	public void run(){
		//加控制台&字节转字符
		while(true){
			String data;
			try {
				data = reader.readLine();
				byte[] datas=data.getBytes();
				//3、封装成DatagramPacket包裹 ,需要指定目的地
				DatagramPacket packet=new DatagramPacket(
						datas,0,datas.length,new InetSocketAddress(
									this.toIP,this.toPort));
				//4、发送包裹,sent(DatagramPacket p) 
				client.send(packet);
				if(data.equals("exit")) break;
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
			//5、释放资源
			client.close();
	}
}

---------------------继续加入多线程,实现双向交流,模拟在线聊天---------------------

客户1:周百青
public class TalkStudent {
	public static void main(String[] args) {
		//指定自己的发送端口7777,指向对方的接收端口6666
		new Thread(new TalkSend(7777,"localhost",6666)).start(); 
		//指定自己的接收端口5555,指定对方的名字为 杜静宜
		new Thread(new TalkReceive(5555, "杜静宜")).start(); 
	}
}
客户2:杜静宜
public class TalkStudent2 {
	public static void main(String[] args) {
		//指定自己的接收端口6666,指定对方的名字为 周百青
		new Thread(new TalkReceive(6666, "周百青")).start(); 
		//指定自己的发送端口是4444,指向对方的接收端口5555
		new Thread(new TalkSend(4444,"localhost",5555)).start(); 
	}
}
结果:

在这里插入图片描述

二、TCP编程实现

1、相比UDP性能略低,UDP倾向于底层数据包的发送和接收
包中写IP地址。
2、用IO流实现数据的传输。http的底层协议是TCP协议,http是超文本传输协议,信息是
明文传输,https是有安全性的ssl加密传输协议。
3、请求响应模式,服务器端监听客户端的请求。建立连接后,使用IO流传输数据。

实例一:网络登录

---------------------单人单向登录---------------------

创建服务器:
  • 1、指定端口,使用ServerSocket创建服务器
  • 2、阻塞式等待连接 accept
  • 3、操作:输入输出流操作
  • 4、释放资源
代码实现:
public class LoginServer {
	public static void main(String[] args) throws IOException {
		System.out.println("--------Server--------");
		//1、指定端口,使用ServerSocket创建服务器
		ServerSocket server = new ServerSocket(9999);
		//2、阻塞式等待连接 accept
		Socket client = server.accept();
		System.out.println("一个客户端建立了连接");
		//3、操作:输入输出流操作
		DataInputStream dis = new DataInputStream(client.getInputStream());
		String datas = dis.readUTF();
		//分析数据
		String[] dataArray = datas.split("&");
		for(String info:dataArray){   //遍历数组
			String[] userinfo = info.split("=");
			if(userinfo[0].equals("userid"))
			    System.out.println("你的用户名为:"+userinfo[1]);
			else if(userinfo[0].equals("passwd"))
				System.out.println("你的密码为:"+userinfo[1]);
		}
		//4、释放资源
		dis.close();
		client.close();//关闭Socket
		//一般服务器不会关闭
		//server.close();
	}
}
创建客户端:
  • 1、建立连接:使用Socket创建客户器+服务器的地址和端口
  • 2、操作:输入输出流操作
  • 3、释放资源
代码实现:
public class LoginClient {
	public static void main(String[] args) throws UnknownHostException, IOException{
		System.out.println("--------Client--------");
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		System.out.println("请输入用户名:");
		String userid = br.readLine();
		System.out.println("请输入密码:");
		String passwd = br.readLine();
		//1、建立连接:使用Socket创建客户器+服务器的地址和端口
		Socket client = new Socket("localhost",9999);
		//2、操作:输入输出流操作
		//推荐用Data流,不用去操作字节
		DataOutputStream dos = new DataOutputStream(client.getOutputStream());
		dos.writeUTF("userid="+userid+"&"+"passwd"+passwd);
		dos.flush();
		//3、释放资源
		dos.close();
		client.close();//关闭客户端
	}
}
效果:

在这里插入图片描述
---------------------多人登录,并完成代码封装---------------------

创建服务器:
  • 1、指定端口,使用ServerSocket创建服务器
  • 2、阻塞式等待连接 accept
  • 3、操作:输入输出流操作
  • 4、释放资源
代码实现:
public class LoginMultiServer {
	//声明用户名与密码
	static String userid="";
	static String passwd="";
	public static void main(String[] args) throws IOException {
		System.out.println("--------Server--------");
		//1、指定端口,使用ServerSocket创建服务器
		ServerSocket server = new ServerSocket(9999);
		//boolean is=true; //当while(true)循环出错时,可设标志位骗过JVM
		//2、阻塞式等待连接 accept
		while(true){
			Socket client = server.accept();
			System.out.println("一个客户端建立了连接");
			new Thread(new Channel(client)).start();
		}
		//一般服务器不会关闭
		//server.close();
	}
	//定义一个静态匿名内部类,方便后续操作
	//一个Channel就代表一个客户端,即一个线程,封装了输入输出流
	static class Channel implements Runnable{
		private Socket client;
		private DataOutputStream dos;
		private DataInputStream dis;
		//编写构造器
		public Channel(Socket client) throws IOException{
			this.client=client;
			try {
				//3、操作:输入输出流操作
				dis = new DataInputStream(client.getInputStream());
				dos = new DataOutputStream(client.getOutputStream());
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
				release();
			}
		}
		//接收数据(封装成一个方法)
		private String receive(){
		String datas="";
			try {
				datas = dis.readUTF();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			return datas;
		}	
		//发送数据(封装成一个方法)
		private void send(String msg){
			try {
				dos.writeUTF(msg);
				dos.flush();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		//释放资源(封装成一个方法)
		private void release(){
			try {
				if(dos!=null){
					//关闭输出流
					dos.close();
				}
				if(dis!=null){
					//关闭输入流
					dis.close();
				}
				if(client!=null){
					//关闭Socket
					client.close();
				}
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		//重写run方法(线程体)
		//目的:1、验证用户名和密码2、响应3、释放资源
		@Override
		public void run() {
			//分析数据
			String[] dataArray = receive().split("&"); //分隔分析
			for(String info:dataArray){   //遍历数组
				String[] userinfo = info.split("=");
				if(userinfo[0].equals("userid")){
				    System.out.println("你的用户名为:"+userinfo[1]);
					userid = userinfo[1];
				}
				else if(userinfo[0].equals("passwd")){
					System.out.println("你的密码为:"+userinfo[1]);
					passwd = userinfo[1];
				}
			}
			//判断是否登录成功
			if(userid.equals("zbq") && passwd.equals("djy")){//成功
				send("登录成功!");
			}else{//失败
				send("用户名或密码输入错误!");
			}
			//调用释放资源的方法
			release();
		}
	}
}
创建客户端:
  • 1、建立连接:使用Socket创建客户器+服务器的地址和端口
  • 2、操作:输入输出流操作
  • 3、释放资源
代码实现
public class LoginMultiClient {
	//主方法体
	public static void main(String[] args) throws UnknownHostException, IOException{
		//初始化
		String init_msg = new Init().init();
		//1、建立连接:使用Socket创建客户器+服务器的地址和端口
		Socket client = new Socket("localhost",9999);
		//2、发送请求
		new Send(client).send(init_msg);
		//3、接收响应
		new Receive(client).receive();
		//4、释放资源
		new Release(client).release();
	}
	//定义一个静态内部类:负责初始化设置
	static class Init{
		BufferedReader br;
		String userid="";
		String passwd="";
		//定义初始化方法
		public String init(){
			System.out.println("--------Client--------");
			br = new BufferedReader(new InputStreamReader(System.in));
			try {
				System.out.println("请输入用户名:");
				userid = br.readLine();
				System.out.println("请输入密码:");
				passwd = br.readLine();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
				return "";
			}
			return "userid="+userid+"&"+"passwd="+passwd;
		}
	}
	//定义一个静态内部类:负责发送数据
	static class Send{
		private DataOutputStream dos;
		//构造器中传成员
		public Send(Socket client){
			try {
				//2、操作:输出流操作
				//推荐用Data流,不用去操作字节
				dos = new DataOutputStream(client.getOutputStream());
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		//构造一个发送数据的方法
		public void send(String msg){
			try {
				dos.writeUTF(msg);
				dos.flush();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
	//定义一个静态内部类:负责接收数据
	static class Receive{
		private DataInputStream dis;
		String result="";
		//接收响应
		//定义构造器
		public Receive(Socket client){
			try {
				dis = new DataInputStream(client.getInputStream());
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		//写方法
		public void receive(){
			try {
				//反馈结果
				result = dis.readUTF();
				System.out.println(result);
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
	//定义一个静态内部类:负责释放资源
	static class Release{
		private DataOutputStream dos;
		private DataInputStream dis;
		private Socket client;
		public Release(Socket client){
			this.client = client;
		}
		//定义释放资源的方法
		public void release(){
			try {
				if(dos!=null){
					//关关闭输出流
					dos.close();
				}
				if(dis!=null){
					//关闭输入流
					dis.close();
				}
				if(client!=null){
					//关闭客户端
					client.close();
				}
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
}
效果:

在这里插入图片描述

  • 2
    点赞
  • 45
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值