java TCP/IP实现简单的多人聊天功能

2023-05-16

TCP/IP是可靠的网络协议,数据的传输需要服务端和客户端之间三次“握手”,比较适合文本等一些可靠性要求高的数据传输,但是它的效率较UDP低。下面通过一张图来简要说明使用 ServerSocket 创建 TCP 服务器端和使用Sock创建客户端通信的流程:


这只是实现了单个服务端与客户端的通讯,要想实现与多个客户端的通讯,要在服务端的发送信息线程里把信息转发给各个客户端,在服务端里循环监听客户端的连接,每当有一个客户端连接则把该客户端放进一个集合里面(转发信息时用),然后为该客户端新建一个接收线程和一个发送线程。


具体实现代码:

Server端

package project20150806;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.Scanner;

//发送信息线程
class SendThreat implements Runnable {
	Socket socket;
	//在这里使用PrintWriter流来向客户端发送信息,也可以用其它流
	PrintWriter pWriter;
	//接收来自主线程的客户端集合
	private ArrayList<Socket> socketList;
	
	//从键盘输入获取信息
	Scanner scanner = new Scanner(System.in);

	public SendThreat(Socket socket,ArrayList<Socket> socketList) {
		super();
		this.socket = socket;
		this.socketList=socketList;
		try {
			//接收socket的字节输出流,用OutputStreamWriter把字节输出流转化为字符流,再传给PrintWriter
			pWriter = new PrintWriter(new OutputStreamWriter(socket.getOutputStream()));
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	@Override
	public void run() {

		while (true) {
			//获取从键盘输入的信息
			String strMsg =socket.getInetAddress().getHostAddress()+":"+ scanner.nextLine();
			if (strMsg == "b") {
				break;
			}
			
			//把服务器收到的信息转发给各个客户端
			for (Socket clientSock : socketList) {
				PrintWriter pWriter;
				try {
					//获取socket的输出流,用来向客户端发送信息
					pWriter = new PrintWriter(clientSock.getOutputStream());
					//输出信息给客户端
					pWriter.println(strMsg);
					//刷新输出流
					pWriter.flush();
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				
				// pWriter.close();
			}
//			pWriter.println(strMsg);
//			pWriter.flush();
//			pWriter.close();
			// System.out.println(strMsg);
		}

	}
}

//接收信息线程
class ReceiveThreat implements Runnable {
	Socket socket;
	BufferedReader bReader;
	private ArrayList<Socket> socketList;

	public ReceiveThreat(Socket socket, ArrayList<Socket> socketList) {
		super();
		this.socket = socket;
		this.socketList = socketList;
		try {
			//获取socket的输入流
			bReader = new BufferedReader(new InputStreamReader(socket.getInputStream()));

		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	@Override
	public void run() {
		while (true) {

			try {
				String strMsg = bReader.readLine();
				System.out.println(strMsg);

				for (Socket clientSock : socketList) {
					PrintWriter pWriter = new PrintWriter(clientSock.getOutputStream());
					pWriter.println(strMsg);
					pWriter.flush();
					// pWriter.close();
				}

			} catch (IOException e) {
				// TODO Auto-generated catch block
				ChatServer.socketList.remove(socket);
			}
		}
	}
}

public class ChatServer {

	//定义一个集合用来存放 监听到的客户端socket
	public static ArrayList<Socket> socketList = new ArrayList<>();

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		ServerSocket serverSocket = null;

		try {
			//新建一个服务端ServerSocket,锁定端口号为30000,端口号建议锁定大一点的
			serverSocket = new ServerSocket(30000);
			System.out.println("等待客户端连接...");
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		while (true) {
			Socket socket = null;
			while (true) {
				try {
					//监听客户端的连接
					socket = serverSocket.accept();
					//加入集合
					socketList.add(socket);
					System.out.println("客户端 " + socket.getInetAddress().getHostAddress() + "连接成功!");

					// showHello(socket);
					//为该客户端分别开启一个发送信息线程和接收信息线程
					new Thread(new SendThreat(socket,socketList)).start();
					new Thread(new ReceiveThreat(socket, socketList)).start();

				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}
	}

}


客户端:

package project20150806;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Scanner;

//发送信息线程
class SendClientThreat implements Runnable {
	Socket socket;
	PrintWriter pWriter;
	Scanner scanner;

	public SendClientThreat(Socket socket) {
		super();
		this.socket = socket;
		this.scanner = new Scanner(System.in);
		try {
			pWriter = new PrintWriter(new OutputStreamWriter(socket.getOutputStream()));
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	@Override
	public void run() {

		while (true) {
			String strMsg =socket.getInetAddress().getHostAddress()+":"+ scanner.nextLine();
			pWriter.println(strMsg);
			pWriter.flush();
			
//			System.out.println(strMsg);
		}

	}
}

//接收信息线程
class ReceiveClientThreat implements Runnable {
	Socket socket;
	BufferedReader bReader;

	public ReceiveClientThreat(Socket socket) {
		super();
		this.socket = socket;
		try {
			bReader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	@Override
	public void run() {
		try {

			while (true) {
				String strMsg = bReader.readLine();
				System.out.println(strMsg);
			}

		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

	}
}

public class ChatClient {

	private static Socket socket;

	public static void main(String[] args) {
		//服务端的IP
		String IPAdress="192.168.1.199";
		//创建一个客户端socket,指定服务端的IP和端口号
		try {
			socket = new Socket(IPAdress, 30000);
			System.out.println("连接主机成功! ");
			
			new Thread(new ReceiveClientThreat(socket)).start(); 
			new Thread(new SendClientThreat(socket)).start();
			
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		
	}

}


这样就可以实现简单的聊天室功能了。把从键盘获取的信息换成从文件获取的流,还可以实现文件传输功能。

如有不足之处请多多指教,谢谢!



本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

java TCP/IP实现简单的多人聊天功能 的相关文章

  • "我"与AI

    有人说过 xff0c 在这世界上 xff0c 一共有10种人 xff0c 一种是懂二进制的 xff0c 一种是不懂的 其实 xff0c 在不远的未来 xff0c 这个世界多了两种机器 xff0c 懂AI的 xff0c 以及不懂的 在如今的互
  • java集合篇(一)——ArrayList扩容原理

    相信大家都对ArrayList相当熟悉了 xff0c 今天笔者就对ArrayList的源码进行解读 xff0c 讲解一下对ArrayList扩容的基本原理 虽然大家都有用过 xff0c 但还是简单介绍一下吧 xff0c ArrayList实
  • 怎样快速开发一个 Dubbo 应用?

    背景 本文将以 Dubbo 为例 xff0c 介绍如何快速开发一个 Dubbo 应用 为了便于读者理解 xff1a 首先会介绍一下传统的 RMI 的基本概念 然后比较下现代的 RPC 框架与 RMI 的区别 再基于 Dubbo 提供的 AP
  • 百度历届笔试题(1)

    题目描述 牛牛和妞妞正在玩一个猜数游戏 xff0c 妞妞心里想两个不相等的正数 xff0c 把这两个正数的和y告诉牛牛 妞妞声称这两个数都不超过x xff0c 让牛牛猜这两个数是多少 牛牛每猜一次 xff0c 妞妞会告诉他猜对了还是猜错了
  • 最佳线性无偏估计BLUE

    最佳线性无偏估计BLUE 1 定义 xff1a 线性估计是参数估计最重要的一类 xff0c 应用 广泛 如果对参数x 的估计可以表示成为量测信 息的线性函数就是线性估计 而线性无偏最小方差估计称为BLUE Best Linear Unbia
  • 手把手教你使用CMake自动编译中CMakeLists.txt 怎么写

    背景 CMake 一直都是用别人的开源项目来编译 Makefile都是自己写 最近看zlm源码 里面用的CMake编译 比较简洁易懂 所以有尝试给现有项目也写个CMakeList txt来使用cmake自动编译 跟它耍耍 基础语法介绍 主要
  • 巧用snprintf动态打印任意长度的buf

    Q xff1a 调试程序的时候 xff0c 经常需要对传入的连续地址空间或数组进行打印 xff0c 常规做法是起一个for循环 xff0c 在每次循环中调用printf依次打印每个数组下标的值 就像这样 xff1a char Buf 99
  • 仿滴滴抢单倒计时的Demo

    滴滴里面有一个下单完成之后等待界面的倒计时转圈的视图 就是这个 原理 xff1a 通过CAShapeLayer层添加到自己自定义的视图layer上 设置ShapeLayer的path 他的路劲绘制一般通过UIBezierPath配合设置 通
  • 电机PID控制补充篇-野火上位机串口协议介绍

    0 引言 之前介绍的电机PID控制的系列文章 电机控制与PID实践 得到不少电子爱好者的关注 xff0c 不过也收到一些关于串口通信的疑问反馈 之前的一系列文章 xff0c 确实没有着重介绍串口软件的使用细节以及通信协议的具体格式 本篇就来
  • 【Ubuntu】修改ubuntu和windows双系统启动顺序

    目录 一 问题描述二 背景知识1 GRUB是什么2 GRUB配置文件3 96 etc default grub 96 主配置文件 二 问题分析三 解决方案1 修改grub主配置文件2 更新grub配置文件 一 问题描述 Ubuntu 43
  • STM32状态机编程实例——全自动洗衣机(上)

    前面几篇文章 xff0c 以按键功能 xff0c 介绍了状态机的原理与按键状态机实例 xff0c 实现按键单击 双击 长按等状态的检测 本篇 xff0c 继续使用状态机编程 xff0c 来实现一个更有趣的功能 全自动洗衣机 1 全自动洗衣机
  • Keil的stm32工程中一些文件的作用

    stm32工程中有一堆文件如下 xff1a 一直没搞明白他们的作用 xff0c 现整理如下 xff1a 从start文件夹开始 xff0c 第一个文件startup stm32f10x md s 这是启动文件 xff0c 是用汇编语言编写的
  • FreeRTOS消息队列、信号量、事件组、任务通知之间的总结

    转载自 xff1a FreeRTOS消息队列 信号量 事件标志组 任务通知 丨匿名用户丨的博客 CSDN博客 功能及区别列表 消息队列 xff08 需要传递消息时使用 xff09 在任务与任务间 中断和任务间传递信息 xff0c 可以数据传
  • FreeRTOS小项目实战------基于FreeRTOS和stm32的门禁系统

    目录 收获 系统总体框架 程序框架 具体程序实现 工程文件网盘链接 收获 学习freertos的移植与裁剪 xff0c 对任务间通信的认识更加深刻 xff0c 加深了实时操作系统的理解 xff0c 学习了as608指纹模块 xff0c rc
  • 自制操作系统12:移动鼠标 - 中断机制探秘,捕获键盘中断

    参考 xff1a https www bilibili com video BV1VJ41157wq p 61 12 amp spm id from 61 pageDriver https blog csdn net tyler downl
  • 全日制和非全日制的含金量是哪个高啊?

    我们不该拿成人学历去和普通全日制的学历去比较 xff0c 去纠结成人学历的含金量高与低 成人在职人士只是需要学历作为人生进步的敲门砖 不是每个人都有魄力和时间去重新参加全日制高考 xff0c 读完三 四年的全日制学历再去工作
  • c/c++笔试

    1 xff0e 进程和线程的差别 线程是指进程内的一个执行单元 也是进程内的可调度实体 与进程的区别 1 调度 xff1a 线程作为调度和分配的基本单位 xff0c 进程作为拥有资源的基本单位 2 并发性 xff1a 不仅进程之间可以并发执
  • Linux摄像头驱动1——vivid

    CSDN仅用于增加百度收录权重 xff0c 排版未优化 xff0c 日常不维护 请访问 xff1a www hceng cn 查看 评论 本博文对应地址 https hceng cn 2018 03 08 Linux摄像头驱动1 vivid
  • webrtc 百亿流媒体服务器开发(1)-服务器编程基础

    webrtc 百亿流媒体服务器开发 xff08 1 xff09 网络编程基础 xff08 1 xff09 文章目录 如何开发以一个简单服务器 信号什么是信号信号的处理方式都有哪些信号 几个重要的信号发送信号signal xff08 xff0
  • 不知道怎么开发VR游戏?Unity5.3官方VR教程重磅登场-系列3 VR中的交互方式

    不知道怎么开发VR游戏 xff1f Unity5 3官方VR教程重磅登场 系列3 VR中的交互方式 王寒 4 个月前 https zhuanlan zhihu com p 20505470 概览 xff1a 在VR项目中 xff0c 我们需

随机推荐

  • md5.pro.js前端MD5加密插件

    下载地址 例如我们对abc进行md5加密 xff0c 则只需要调用hex md5 34 md5 34 xff0c 方法则会返回加密后的字符串900150983cd24fb0d6963f7d28e17f72 dd
  • 【项目经验】Jetson xavier nx开发板-从裸机到深度学习环境配置

    Jetson xavier nx 开发板 一 镜像安装 参考博文Jetson Xavier NX 烧写系统镜像 1 镜像下载 到英伟达官方下载地址https developer nvidia com zh cn embedded downl
  • ROS探索总结(十五)——amcl(导航与定位)

    在理解了move base的基础上 xff0c 我们开始机器人的定位与导航 gmaping包是用来生成地图的 xff0c 需要使用实际的机器人获取激光或者深度数据 xff0c 所以我们先在已有的地图上进行导航与定位的仿真 amcl是移动机器
  • 关于ROS学习的一些反思

    距离发布上一篇ROS的博客已经过去两年了 xff0c 才发现原来自己已经这么久可没有写过关于ROS的文章 xff0c 想来很是惭愧 这两年时间 xff0c 自己怀着程序员的梦想 xff0c 研究过RTOS xff0c 探索过Linux xf
  • ROS探索总结(十八)——重读tf

    在之前的博客中 xff0c 有讲解tf的相关内容 xff0c 本篇博客重新整理了tf的介绍和学习内容 xff0c 对tf的认识会更加系统 1 tf简介 1 1 什么是tf tf是一个让用户随时间跟踪多个参考系的功能包 xff0c 它使用一种
  • ROS探索总结(十二)——坐标系统

    在机器人的控制中 xff0c 坐标系统是非常重要的 xff0c 在ROS使用tf软件库进行坐标转换 相关链接 xff1a http www ros org wiki tf Tutorials Learning tf 一 tf简介 我们通过一
  • qt切换设置多个ui界面的两种方式

    qt切换多个ui界面的两种方式 这是本人写的第一篇问文章 xff0c 有什么问题请大家多多批评指正 话不多说 xff0c 进入正文 xff1a 老师最近接了一个自来水管检漏的项目 xff0c 需要制作上位机来接收传感器传来的数据 于是果断选
  • firebird数据库安装连接的一些常见错误及解决方法

    firebird数据库安装连接的一些常见错误及解决方法 最近有一个需求是把数据库里面的数据提取出来 xff0c 这个数据库的后缀是 fdb xff0c 查阅资料可知应该是firebird数据库的文件 xff0c 可是firebird数据库是
  • PX4与Gazebo、ROS/MAVROS以及QGC地面站之间的通信

    PX4与Gazebo ROS MAVROS以及QGC地面站之间的通信 一 整体框架 ROS xff08 机器人操作系统 xff09 可用于PX4和Gazebo模拟器 它使用MAVROS MAVLink节点与PX4通信 ROS Gazebo与
  • 四旋翼飞行器控制模型公式推导

    四旋翼飞行器控制模型 为便于建立模型 xff0c 现对四旋翼飞行器进行以下假设 xff1a 1 四旋翼飞行器是均匀对称的刚体 2 四旋翼飞行器的质量和转动惯量不发生改变 3 四旋翼飞行器的几何中心与其重心重合 4 四旋翼飞行器只受重力和螺旋
  • java中几种读取配置文件的方法

    java读取 properties配置文件的几种方法 xff08 1 xff09 Properties类读取 Properties类继承自Hashtable类并且实现了Map接口 xff0c 也是使用一种键值对的形式来保存属性集 不过Pro
  • Java项目分层

    MVC模式 在实际的开发中有一种项目的程序组织架构方案叫做MVC模式 xff0c 按照程序 的功能将他们分成三个层 xff0c 如下图 xff1a Modle层 xff08 模型层 xff09 View层 xff08 显示层 xff09 C
  • 简单介绍控制理论(经典、现代)

    1 经典和现代的区别和联系 xff08 1 xff09 区别 研究对象 经典控制系统一般局限于单输入单输出 线性定常系统 主要分为开环控制系统和闭环控制系统 严格的说 xff0c 理想的线性系统在实际中并不存在 实际的物理系统 xff0c
  • 框架中<include>**/*.xml</include>配置解释

    在mybatis Spring SpringMVC SpringBoot等框架的配置文件中经常会使用到如下代码 xff1a lt resource gt lt directory gt src main java lt directory
  • 反转单链表的几种方式对比(包括双指针法和递归)

    需求 xff1a 给你单链表的头节点 head xff0c 请你反转链表 xff0c 并返回反转后的链表 方式一 xff1a 双指针法 建立一个虚拟节点 class Solution public ListNode reverseList
  • 正则表达式 ^$ 同时出现代表什么

    与 同时出现在正则前后表示什么 xff1f 脱字符 xff1a 匹配开头 xff0c 若存在多行匹配多行的行头 美元符 xff1a 匹配尾部 xff0c 若存在多行匹配多行的尾部 同时写时只是限制字符的起点与终点 xff0c 比如 xff1
  • 设置虚拟机为固定IP,避免每次启动虚拟机都会分配新的IP地址

    采用一种最简单的方式 xff0c 通过修改配置文件来指定IP xff0c 并可以连接到外网 要求 xff1a 将IP地址配置为静态的 xff0c 比如设固定IP地址为192 168 117 131 打开文件 etc sysconfig ne
  • 我的2014--众人皆醉我独醒

    转眼间大学两年过去了 xff0c 舍友们还在撸游戏 xff0c 有的也找到了另一半的归属 我是我宿舍唯一一个不玩电脑游戏的人 xff0c 当然 xff0c 不是不玩游戏就代表着成绩很好 xff0c 也不代表玩游戏就不好 xff0c 但意味着
  • Java多线程通信-利用传统的线程通信wait(),notify()方法实现“生产者消费者模式”

    想利用传统的线程通信wait notify xff0c notifyAll 方法 xff0c 必须依赖于同步监听器的存在 xff0c 也就是说 xff0c 对于synchronized修饰的同步方法 xff0c 因为该类的默认实例 xff0
  • java TCP/IP实现简单的多人聊天功能

    TCP IP是可靠的网络协议 xff0c 数据的传输需要服务端和客户端之间三次 握手 xff0c 比较适合文本等一些可靠性要求高的数据传输 xff0c 但是它的效率较UDP低 下面通过一张图来简要说明使用 ServerSocket 创建 T