最近在复习java的io流及网络编程。但复习写那些样板程序总是乏味的。便准备写个项目来巩固。想来想去还是聊天项目比较好玩。如果日后完成的比较好自己也可以用(哈哈哈)。并且自己后面也要继续巩固java多线程和集合(这两部分学的很差)。

我给这个项目命名为很大众的名字——" chat "

这算是"chat 1.0" 吧目前只实现了群聊+文件传输功能,没有用户注册模块。

因为自己在阿里云上的服务器之前到期没有续费,所以就无法将服务端实现两个局域网之间的信息传输。只能在自己电脑上开多个客户端自己和自己聊天。

不是很甘心,上网找到了一个比较好的方法,内网穿透。某神,某花,都是免费可以映射一两个公网ip的。这样就可以实现不同局域网之间的通信了

效果演示:

打开图形界面不会立刻执行,会有启动服务器,连接服务器按钮。图中2昵称仅支持本地自己的界面显示来分辨是自己发的消息(哈哈哈)

点击发送文件按钮,会显示文件对话框,来选择要发送的本地文件。

发送完毕后其它客户端会收到文件消息,并询问是否接收文件。

接收时也会弹出文件对话框,并选择存储的路径和文件名称

字不多打,上代码。(主要使用tcp通信)

服务端:

Server类实现一直监听客户端的套接字连接,并开启服务端读写线程

 import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List; import view.ServerStart_View; public class Server implements Runnable{ public static ArrayList<Socket> socketList = new ArrayList<>(); //存储连接的Socket对象
private static int num = 0;
static ServerStart_View gui; //定义为静态的
// public static final Server server = new Server(); public void get(ServerStart_View gui) {
this.gui=gui;
} public void run() {
ServerSocket socket=null;
Socket socket2 = null;
try {
socket = new ServerSocket(12345);
} catch (IOException e) {
e.printStackTrace();
} while(true) {
// System.out.println("didi");
//无限监听客户端连接,并为此开启读写线程。
try {
Thread.sleep(1);
socket2=socket.accept();
socketList.add(socket2);
++num;
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e1) {
e1.printStackTrace();
}
gui.setText("第"+num+"个客户端已经连接");
try {
//开启消息读入转发线程。
Server_IO io = new Server_IO(socket2,gui);
Thread thread = new Thread(io);
thread.start();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}

Server_IO类是实现服务端的读写功能,读取客户端传来的字节并转发给其它客户端

 import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket; import view.ServerStart_View; public class Server_IO implements Runnable {
private DataInputStream stream = null;
private Socket socket = null;
private static ServerStart_View start_View; public Server_IO(Socket socket,ServerStart_View start_View) throws IOException {
this.start_View = start_View;
this.socket = socket;
stream = new DataInputStream(socket.getInputStream());
} @Override
public void run() {
String rece = null;
// boolean judg = true;
while(true) {
try {
Thread.sleep(1);
if (stream.available()!=0) {
try {
rece = stream.readUTF();
start_View.setText(rece);
} catch (IOException e) {
e.printStackTrace();
// judg=false;
}
for (Socket Ssocket:Server.socketList)
new DataOutputStream(Ssocket.getOutputStream()).writeUTF(rece); // System.out.println(rece);
}
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
}
}
}

服务端文件传输:

服务端消息和文件传输是分开的,两个不同的端口。但原理一样,都是服务端不断监听客户端发来的连接请求。然后三次握手建立连接通信,开启输入流,输出流。

File类和Server类功能大致相同,只不过是用于接收转发文件信息

 import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List; public class File implements Runnable{ public static ArrayList<Socket> socketList_IO = new ArrayList<>();
ServerSocket ssocket;
Socket socket; public void run() {
//文件传输开启另一个端口监听。
try {
ssocket = new ServerSocket(12306);
} catch (IOException e) {
e.printStackTrace();
}
while(true) {
try {
Thread.sleep(1);
socket = ssocket.accept();
socketList_IO.add(socket);
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e1) {
e1.printStackTrace();
}
File_IO file_IO;
try {
file_IO = new File_IO(socket);
Thread thread = new Thread(file_IO);
thread.start();
} catch (IOException e) {
e.printStackTrace();
} }
} }

File_IO类 接收客户端的文件,并转发给其他客户端

 import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket; public class File_IO implements Runnable {
DataInputStream input;
DataOutputStream output;
Socket socket; public File_IO(Socket socket) throws IOException{
this.socket = socket;
System.out.println(socket.getInetAddress());
} @Override
public void run() {
byte[] read = new byte[1024];
String name;
try {
input = new DataInputStream(socket.getInputStream());
} catch (IOException e1) {
e1.printStackTrace();
}
while(true) {
try {
Thread.sleep(1);
if(input.available()!=0) {
name=input.readUTF();
System.out.println(name);
int len=0;
while((len=input.read(read,0,read.length))>0) {
for(Socket soc:File.socketList_IO) {
if(soc != socket)
{
output = new DataOutputStream(soc.getOutputStream());
output.writeUTF(name);
output.write(read,0,len);
output.flush();
// System.out.println("开始向客户机转发");
}
}
// System.out.println("执行");
// System.out.println(len);
}
}
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}

Swing图形界面。服务端如果是放在Linux系统的服务器中是不需要图形界面的。但我这主要在自己电脑上运行所以做个简单的可视化界面比较好。

客户端还是服务端的图形界面我都是借助于eclipse的windowbuilder插件进行绘制的,所以图形界面类我贴上主要的方法及按钮的事件监听代码部分。

服务端的界面

public class ServerStart_View extends JFrame {

    private JPanel contentPane;
private JScrollPane scrollPane;
private JTextArea textArea;
private static Server server = new Server(); //定义为静态的
private static File file = new File();
private final Action action = new SwingAction();
private JButton btnNewButton; //打印收到的消息在服务端的文本域中
public void setText(String string) {
textArea.append("\n"+">>: "+string);
} //
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
ServerStart_View frame = new ServerStart_View();
frame.setVisible(true);
server.get(frame); //传递对象变量
}
});
} ............
............
............ //启动服务按钮的事件处理。
private class SwingAction extends AbstractAction {
public SwingAction() {
putValue(NAME, "启动服务");
putValue(SHORT_DESCRIPTION, "Some short description");
}
public void actionPerformed(ActionEvent e) {
//开启服务端监听客户端连接线程(消息服务)
Thread thread = new Thread(server);
thread.start();
//同上 (文件服务)
Thread thread2 = new Thread(file);
thread2.start();
}
} }

服务端代码到此结束。主要是消息和文件分别各监听一个端口的等待连接。

客户端:

本来是定义Client类的但因为某些前期原因导致客户端的类名不是很规范。

chat_Client类

 package client;

 import view.ClientStart_View;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;
import java.net.UnknownHostException; public class chat_Client{
public chat_Client() {
} private Socket socket = null;
// private int Port = 0;
private static ClientStart_View jframe; //定义为静态的 public void text(ClientStart_View jframe) {
this.jframe = jframe;
} //“连接服务器” button 按钮点击事件,调用此方法
public void con(int port) throws UnknownHostException, IOException {
try {
socket = new Socket("127.0.0.1", port);
// System.out.println("客户端------------------------");
// System.out.println("连接到的地址"+socket.getInetAddress()+":"+socket.getLocalPort()); //打印在控制台
jframe.TextA("已连接");
} catch (IOException e1) {
e1.printStackTrace();
} Runnable runnable = new chat_Client_I(socket,jframe); //启动消息读写线程,并将传递socket,jframe对象变量
Thread thread = new Thread(runnable);
thread.start();
} //send()方法,发送消息给服务器。 “发送”button 按钮点击事件,调用此方法
public void send(String send) throws IOException {
DataOutputStream stream = new DataOutputStream(socket.getOutputStream());
stream.writeUTF(send);
} }

chat_Client_I类  客户端的消息字符读取类。读取服务端发来的消息字符并打印在界面的文本域中

 import view.ClientStart_View;

 import java.io.DataInputStream;
import java.io.IOException;
import java.net.Socket; public class chat_Client_I implements Runnable{
private String rece = null;
private static DataInputStream stream;
private ClientStart_View jframe; public chat_Client_I(Socket socket,ClientStart_View jframe) {
this.jframe = jframe;
try {
stream = new DataInputStream(socket.getInputStream());
} catch (IOException e2) {
e2.printStackTrace();
}
} @Override
public void run() {
while(true) {
try {
//if条件判断是否有可获取的字节 ,如果不是判断而一直读取的话,在结束时会有读取异常抛出。运行时是没有的。
if(stream.available()!=0) {
rece = stream.readUTF();
jframe.TextA(rece); //调用 ClientStart_View.java类中方法将读入的字符串打印在文本域中
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}

客户端的文件模块写的有点乱。客户端文件模块要实现发送和接收保存

发送要从计算机本地通过输入流读取文件,并通过输出流发送给服务端。

接收要从服务端通过输入流进行读入,然后通过输出流写入本地保存。

File_start类,连接服务端,并开启输入流读取服务端发来的文件

 import java.io.IOException;
import java.net.Socket;
import java.net.UnknownHostException; public class File_start implements Runnable{
Socket socket=null; @Override
public void run() {
try {
socket = new Socket("127.0.0.1", 12306);
// System.out.println("已连接");
// System.out.println(socket.getInetAddress()+":"+socket.getLocalPort());
} catch (UnknownHostException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} File_I file_i;
try {
File_O file_O = new File_O(socket);
//开启从服务器的输入流线程
file_i = new File_I(socket);
Thread thread = new Thread(file_i);
thread.start();
} catch (IOException e) {
e.printStackTrace();
} }
}

File_O类,读取本地文件并发送到服务端

 import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.Socket; public class File_O{
public static File_O file_O = new File_O();
// private Socket socket=null;
private byte[] readAllBytes = new byte[1024];
private static DataOutputStream outputstream;
private DataInputStream inputstream; public File_O() {}
public File_O(Socket socket) throws IOException {
// this.socket = socket;
outputstream = new DataOutputStream(socket.getOutputStream());
} //读取本地文件,并发送到服务端。面板中发送文件按钮在事件处理时会调用此函数
public void read(String url,String name) throws IOException {
outputstream.writeUTF(name);
inputstream = new DataInputStream(new FileInputStream(url));
int len=0;
while((len=inputstream.read(readAllBytes))>0) {
outputstream.write(readAllBytes, 0, len);
outputstream.flush();
}
//outputstream.close();
// System.out.println("发送完成");
} //此方法暂时没有用到
public byte[] getread() {
return readAllBytes;
}
}

File_I类 从服务端的输出流读取文件,并保存到本地。

 import view.ClientStart_View;

 import java.awt.HeadlessException;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.Socket; import javax.swing.JOptionPane; public class File_I implements Runnable{ // public static File_I file_I = new File_I();
private static ClientStart_View jframe;
private DataOutputStream output;
private static DataInputStream input;
private Socket socket; public File_I() {}
public File_I(Socket socket) throws IOException {
this.socket = socket;
input = new DataInputStream(socket.getInputStream());
} public void getfile(ClientStart_View jframe) {
this.jframe = jframe;
} @Override
public void run() {
String url = null;
String name = null;
byte[] rece = new byte[1024];
while(true) {
try {
//if判断服务端输出流中是否有可读取字节
if (input.available()!=0) {
name = input.readUTF();
// System.out.println(name); //是否接受此文件,接收才会进行输入流读入保存
JOptionPane jOptionPane = new JOptionPane();
int result=jOptionPane.showConfirmDialog(null, "是否接收该文件", "嘀嘀:一份文件消息", jOptionPane.YES_NO_OPTION);
// System.out.println(result);
if (result == jOptionPane.YES_OPTION) {
//默认存储路径及从服务端获取的文件名称
File file = new File("D:\\", name);
// file_I.jframe.getJF().setCurrentDirectory(new File("D:\\"));
jframe.getJF().setSelectedFile(file);
jframe.getJF().showSaveDialog(null);
url = jframe.getJF().getSelectedFile().getPath();
// name =file_I.jframe.getJF().getSelectedFile().getName(); int len;
try {
while((len=input.read(rece,0,rece.length))!=-1) {
// System.out.println("zhixing-1");
output = new DataOutputStream(new FileOutputStream(url));
output.write(rece, 0, len);
output.flush();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
} catch (HeadlessException | IOException e) {
e.printStackTrace();
}
}
}
}

客户端的图形界面也是使用插件,所以只贴上主要的代码段

public class ClientStart_View extends JFrame {

    public JPanel contentPane;
private JTextField textField;
JTextArea textArea;
private static client.chat_Client chat_Client=new chat_Client();
private static File_I file_I = new File_I();
private JFileChooser fileChooser =new JFileChooser();
private final Action action = new SwingAction();
private final Action action_2 = new SwingAction_2();
private JTextField textField_1;
private final Action action_3 = new SwingAction_3();
//JFileChooser 用于创建文件对话框
public JFileChooser getJF() {
return fileChooser;
} //此方法将接收到服务端的消息打印文本域中
public void TextA(String rece) {
textArea.append("\n"+textField_1.getText()+" > "+rece);
System.out.println(rece);
} public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
ClientStart_View frame = new ClientStart_View();
frame.setVisible(true);
chat_Client.text(frame);
// chat_Client.get().text(frame);
file_I.getfile(frame);
} catch (Exception e) {
e.printStackTrace();
}
}
});
} .........
.........
.........
......... //事件处理,连接服务端,并开启客户端的文件读取线程
private class SwingAction extends AbstractAction {
public SwingAction() {
putValue(NAME, "连接服务器");
putValue(SHORT_DESCRIPTION, "Some short description");
}
public void actionPerformed(ActionEvent e) {
try {
//chat_Client.get().con(12345); //QQ
chat_Client.con(12345);
} catch (IOException e1) {
e1.printStackTrace();
}
File_start file_start = new File_start();
Thread thread = new Thread(file_start);
thread.start(); }
} //发送按钮的事件处理,从输入文本中读取输入的字符,发送至服务端
private class SwingAction_2 extends AbstractAction {
public SwingAction_2() {
putValue(NAME, "发送");
putValue(SHORT_DESCRIPTION, "Some short description");
}
public void actionPerformed(ActionEvent e) {
String text = null;
text = textField.getText();
if(text.length()!=0) {
try {
// chat_Client.get().send(text);
chat_Client.send(text);
} catch (IOException e1) {
e1.printStackTrace();
}}
textField.setText(null); //输入文本框置空
}
} //发送文件按钮事件处理,从本地读取文件信息,发送至服务端
private class SwingAction_3 extends AbstractAction {
public SwingAction_3() {
putValue(NAME, "发送文件/图片");
putValue(SHORT_DESCRIPTION, "Some short description");
}
public void actionPerformed(ActionEvent e) {
String name=null; String paths=null;
// fileChooser = new JFileChooser();
fileChooser.setCurrentDirectory(new java.io.File("E:\\"));
int result=fileChooser.showOpenDialog(null);
if (result == fileChooser.APPROVE_OPTION) {
paths = fileChooser.getSelectedFile().getPath(); //返回所选文件的路径
name= fileChooser.getSelectedFile().getName(); //返回所选文件的名称
// System.out.println(name); System.out.println(paths);
try {
File_O.file_O.read(paths,name);
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
}
}
}

至此代码结束

这段代码只是实现一部分简单功能,还参杂着不少问题,”线程开的太多,并且是死循环,在运行是cpu使用总是达到100%“,”文件的发送只有文本txt能成功打开,其他发送后格式乱码无法打开“。,,,,

欢迎大家交流和指教。

ps : 项目更新,增加登录,注册功能,发现并优化了一些已知bug

进入主页查看阅读最新文章

链接:https://www.cnblogs.com/yuqingsong-cheng/p/12822628.html

java基于socket的网络通信,实现一个服务端多个客户端的群聊,传输文件功能,界面使用Swing的更多相关文章

  1. 写一个基于TCP协议套接字,服务端实现接收客户端的连接并发

    ''' 写一个基于TCP协议套接字,服务端实现接收客户端的连接并发 ''' client import socket import time client = socket.socket() clie ...

  2. 基于TCP协议套接字,服务端实现接收客户端的连接并发

    基于TCP协议套接字,服务端实现接收客户端的连接并发 服务端 import socket from multiprocessing import Process server=socket.socke ...

  3. socket基础实例(一个服务端对应一个客户端情形)

    服务端处理1个客户端的例子 运行结果: (1) while(accept+if(recv)) 情形 执行服务端进程: [root@localhost single_link]# ./server [s ...

  4. 项目源码--JAVA基于LBS地理位置信息应用的服务端

    技术要点: 1. LBS应用框架服务端实现 2. JAVA服务端技术 3. MYSQL数据库技术 4. 源码带详细的中文注释 ......   详细介绍: 1. LBS应用框架服务端实现 此套源码是基 ...

  5. Java 基于ArcFace人脸识别2.0 服务端Demo

    源代码传送:https://github.com/itboyst/ArcSoftFaceDemo 开发环境准备: ###开发使用到的软件和工具: Jdk8.mysql5.7.libarcsoft_fa ...

  6. java基于socket公共聊天室的实现

    项目:一个公共聊天室功能的实现,实现了登录聊天,保存聊天记录等功能. 一.实现代码 1.客户端 ChatClient.java import java.io.BufferedReader; impor ...

  7. java 网络编程基础 TCP/IP协议:服务端ServerSocket;客户端Socket; 采用多线程方式处理网络请求

    1.Java中客户端和服务器端通信的简单实例 Java中能接收其他通信实体连接请求的类是ServerSocket,ServerSocket对象用于监听来自客户端的Socket连接,如果没有连接,它将一 ...

  8. .net平台 基于 XMPP协议的即时消息服务端简单实现

    .net平台 基于 XMPP协议的即时消息服务端简单实现 昨天抽空学习了一下XMPP,在网上找了好久,中文的资料太少了所以做这个简单的例子,今天才完成.公司也正在准备开发基于XMPP协议的即时通讯工具 ...

  9. “快的打车”创始人陈伟星的新项目招人啦,高薪急招Java服务端/Android/Ios 客户端研发工程师/ mysql DBA/ app市场推广专家,欢迎大家加入我们的团队! - V2EX

    "快的打车"创始人陈伟星的新项目招人啦,高薪急招Java服务端/Android/Ios 客户端研发工程师/ mysql DBA/ app市场推广专家,欢迎大家加入我们的团队! - ...

随机推荐

  1. DevEco Toolkit使用指南--平行视界

      高效开发和创新业务是开发者一直追求的目标,当接到开发需求时,如果可以找到现成的API调用,能为开发者节省大把时间,将会留有更多的时间进行业务的创新.华为DevEcoToolkit聚合了华为丰富的开 ...

  2. ENVI 安装

    本文转自https://jingyan.baidu.com/article/2d5afd69d50e9585a2e28e37.html  但对该文有所补充,建议看本文,本文更详细. ENVI5.3安装 ...

  3. python-从酷狗下载爬取自己想要的音乐-可以直接拿来体验哟

    因为最近发现咪咕音乐版权好多,当时我就在想是不是可以爬取下来,然后花了一些时间,发现有加密,虽然找到了接口,但是只能手动下载VIP歌曲,对于我们学IT的人来说,这是不能忍的,于是就懒得去解密抓取了,但 ...

  4. 【Selenium06篇】python+selenium实现Web自动化:日志处理

    一.前言 最近问我自动化的人确实有点多,个人突发奇想:想从0开始讲解python+selenium实现Web自动化测试,请关注博客持续更新! 这是python+selenium实现Web自动化第六篇博 ...

  5. Pormetheus(一)

    (1)Prometheus由来普罗米修斯的灵感来自于谷歌的Borgmon.它最初是由马特·t·普劳德(Matt T. Proud)作为一个研究项目开发的,普劳德曾是谷歌(google)的一名雇员.在普 ...

  6. Centos 开启网络

    在VMWare上安装了Centos 7,但是发现网络无法连接. # ping www.baidu.com 确认网络无法连接 # ifconfig 查看centos7中的网络配置属性. 启动网络,开启e ...

  7. 软件包管理rpm和yum

    rpm的使用: 安装的包相关包信息会保存在/var/lib/rpm目录下的文件中 安装参数: -i install安装 -v 显示详细信息 -h 打印####号 -V 校验软件包,会到/var/lib ...

  8. git以及gitHub的使用说明书

    一.使用说明 1.Git与github的功能: Git是世界上最先进的分布式版本控制系统,也就是用来记录你的项目代码历史变更信息的工具:github就是用来存储你的代码以及变更信息的云端平台: 2.优 ...

  9. Python爬取抖音高颜值小视频

    前言 文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理. 作者: 有趣的python PS:如有需要Python学习资料的小伙伴可以加 ...

  10. Sprint 2 : ios图形界面设计与代码整合

    这周我们主要focus在personal photo experience 项目的ios图形界面设计与代码整合工作上. 工作进度: 1. 图形界面设计方面:兆阳和敏龙基本已经将ios手机客户端的雏形界 ...