通信最基本的也就是一个输入流一个输出流,InputStream 和 OutputStream 。这样一开始我们写了一个最基本的服务器接着一步一步实现功能。
1、就实现一个简单的服务器,可以跟系统的最简单的客户端进行聊天
java.net.ServerSocket ss=new java.net.ServerSocket(8888);
java.net.Socket s=ss.accept();
//输出流
java.io.DataOutputStream ds=new java.io.DataOutputStream(s.getOutputStream());
//输入流
java.io.DataInputStream di=new java.io.DataInputStream(s.getInputStream());
String sss="你好、、、、";
ds.write(sss.getBytes());
int t=di.read();
while(t!=13){
System.out.println(t);
t=di.read();
}
s.close();
只是简单的包括一个输入流一个输出流,在输出流中把自己传的内容构成的byte数组传过去。读入的话就一次读一个字节,当为回车键时停止。
2、我可视化了这个服务器,添加了一个创建服务器按钮,在点击这个按钮时先取得输入框中的你输入的端口号值,创建ServerSocket对象,并且在这里启动一个输入流的线程,这就意味着创建服务器的同时就可以接收客户端发来的值,并且把这个值setText到界面上。发送按钮就是先取得界面上用户输入的值,再把它写出去。最后的关闭按钮就是关掉每个线程的输入输出流。
if("创建".equals(e.getActionCommand())){
String sl=tt.jt2.getText();
if(sl.length()==0){
javax.swing.JOptionPane.showMessageDialog(tt, "端口值不能为空");
System.exit(0);
}
int s2=Integer.parseInt(sl);
try {
//创建服务器
java.net.ServerSocket ss=new java.net.ServerSocket(s2);
s=ss.accept();
String sss="你好、、、、";
writeMsg(s,sss);
//这个线程用来启动输入流
readThread tc=new readThread(s,tt);
tc.start();
} catch (Exception ef) {
ef.printStackTrace();
}
}
//把我写好的信息发出去
else if("发送".equals(e.getActionCommand())){
String s3=tt.ja2.getText();
s3+="\r\n";
writeMsg(s,s3);
tt.ja2.setText("");//每次发送完毕后都将输入界面清空
}
//退出程序
else if("关闭".equals(e.getActionCommand())){
try {
s.close();
System.exit(0);
} catch (Exception e1) {
e1.printStackTrace();
System.exit(0);
}
}
3、上次的版本有一个Bug,就是点击创建按钮时会等客户端来接收这个Socket对象,如果没有接收的话就会卡死在那,然后我们用了一个线程来把接收Socket放到了这个线程中启动就会很好的改善这个问题。
public void run(){
while(true){
try {
Socket client=server.accept();
readThread rt=new readThread(client,tt);
rt.start();
lis.alist.add(rt);
sleep(10);
} catch (Exception e) {
e.printStackTrace();
}
}
}
并且在这个线程中启动输入流线程。
4、接下来遇到的是只能每次与一个客户机进行聊天,要想实现多个客户端连接就要在同一个服务器上的话就要用到线程了。在启动输入流线程的时候服务器接收到的每个Socket对象就是不同的客户端,
public void run(){
while(true){
try {
Socket client=server.accept();
readThread rt=new readThread(client,tt);
rt.start();
lis.alist.add(rt);
sleep(10);
} catch (Exception e) {
e.printStackTrace();
}
}
}
5.写客户机跟服务器差不多,界面基本一样,也是在得到登陆时现在获取IP地址和端口后实例化socket对象,并且启动一个输入流线程,然后在这里把用户的名字写过去,然后在服务器那边把读取线程改下,循环外面先读一次,也就是对应的用户名。然后在关闭按钮时出了一个问题,也就是你在点了关闭按钮时输入流关闭了,但是线程还在运行,线程中的socket对象此时已为空了,这就要求在输入流中加一个判断,判断socket对象是否为空。
public void run() {
try {
ins = s.getInputStream();
BufferedReader buffReader = new BufferedReader(new InputStreamReader(ins));
while (true) {
if (!s.isClosed()) {
String st = buffReader.readLine();
if (st == null)
return;
String sy = tt.ja1.getText();
st = sy + st + "\r\n";// 获取以前的聊天记录,并把新的记录加上去
tt.ja1.setText(st);// 显示
} else
return;
}
} catch (Exception e1) {
e1.printStackTrace();
}
}
6.其实中间还改过不少版本,因为我用的都是构造器传参,这样程序的耦合性比较差,改动一个地方会有很大问题,要改好多地方才能实现这个功能。我知道这里应该用接口,而且最好把方法封装到一个类中,但是确实还不是怎么会,所以目前没搞出来。
最后的版本中用到了协议,1表示发送文字,2表示发送图片等文件。协议内容就是一开始写入的是整个文件长度,接下来是标示符 cmd 1或2, 1表示文字,2表示图片。在这里判断后就进入不同的写入方法,写入文字时要求读取的时候用的是BufferedReadLine 这个方法是根据根据后面的换行符判断是否为一句话,这样就可以在每句话发送时在后面加上一个/r/n,这样读取的时候就可以一句一句读了。
读取文件时,先弹出一个文件选择框,选择得到文件名,然后用文件读入流FileInputStream fis,把这个读到文件体长度的byte数组中。这个写完标识符后要写文件名字的长度,文件名,消息体内容。这就是一个简单的完整协议了。
// 把我写好的信息发出去
else if ("发送文字".equals(e.getActionCommand())) {
if (tt.ja2.getText() != null) {
String s13 = "服务器说 :" + tt.ja2.getText() + "\r\n";
String sy = tt.ja1.getText();
String s14 = sy + s13;
tt.ja1.setText(s14);
try {
for (int i = 0; i < alist.size(); i++) {
System.out.println("--dd"+alist.size());
alist.get(i).dos.writeInt(4+4+s13.length());
alist.get(i).dos.writeInt(1);
alist.get(i).sendMsg(s13);
}
} catch (Exception e1) {
e1.printStackTrace();
}
tt.ja2.setText("");// 每次发送完毕后都将输入界面清空
} else {
System.out.println("发送值不能为空");
}
}else
//判断发送的为文件,文件协议为先是一个int型的文件总长度,接下来是控制符 2代表图片,在就是名字长度,名字,文件体长度
if("发送图片".equals(e.getActionCommand())){
JFileChooser jfs=new JFileChooser();
jfs.setFileSelectionMode(JFileChooser.FILES_ONLY);
jfs.showOpenDialog(null);
File file=jfs.getSelectedFile();
String fileName=file.getAbsolutePath();
java.io.FileInputStream fis;
byte[] fileByte=null;
try {
fis=new java.io.FileInputStream(fileName);
int fileLen=(int)file.length();
totallenth=4+4+4+fileName.getBytes().length+fileLen;
fileByte=new byte[fileLen];
fis.read(fileByte);
} catch (Exception e1) {
e1.printStackTrace();
}
try {
for (int i = 0; i < alist.size(); i++) {
alist.get(i).dos.writeInt(totallenth);
alist.get(i).dos.writeInt(2);
alist.get(i).dos.writeInt(fileName.getBytes().length);
alist.get(i).sendMsg(fileName);
alist.get(i).dos.write(fileByte);
}
}catch(Exception ef){
ef.printStackTrace();
}
try {
Document doc = tt.ja1.getDocument();
ByteArrayInputStream bis = new ByteArrayInputStream(fileByte);
BufferedImage image = ImageIO.read(bis);
ImageIcon icon = new ImageIcon(image);
tt.ja1.setCaretPosition(doc.getLength());
tt.ja1.insertIcon(icon);
} catch (Exception ed) {
ed.printStackTrace();
}
}
然后客户机跟服务器的读入读出流都要严格根据协议走,哪一个地方不行都会出错。
通信其实代码不难,但是要统一协议,加上传参就比较麻烦了,而且出一点错都不行。
这时候就更体会得到代码的规范性的重要性,如果能用到接口和方法的封装性就会好很多。
分享到:
相关推荐
聊天室使用WebSocket完成聊天室应用程序的实现。背景WebSocket是一种通信协议,它使edu.udacity.java.nano.it可以在服务器和客户端之间建立双向通信通道。操作说明实施消息模型消息模型是将在客户端和服务器之间交换...
服务器下的客户端也可以通信,XMPP的前身是一个开源组织制定的网络通信协议——Jabber,XMPP的核心是在网络上分片段发送XML流的协议,这个协议是XMPP的即时通讯指令的传递手段。 为了防止服务器间发送的数据被篡改...
:person_running: 使用Socket.io,React,MongoDB,Express和Node.js构建的实时聊天室 前端框架: React,React-dom 前端Ajax通信: axios,socket.io-client 前端Web RWD设计: Material-ui,livechat-ui 后端...
基于浏览器端的web技术,那么它的通信肯定少不了http,websocket本身虽然也是一种新的应用层协议,但是它也不能够脱离http而单独存在。具体来讲,我们在客户端构建一个websocket实例,并且为它绑定一个需要连接到的...
在Html5中存在着这样的一个新特性,引入了websocket,关于websocket的内部实现原理可以看这篇文章,这篇文章讲述了websocket无到有,根据协议,分析数据帧的头,进行构建websocket。虽然代码短,但可以很好地体现...
SignalR简化了构建实时应用的过程,它包括了一个Asp .Net服务器端库和一个Js端库,集成了数种常见的消息传输方式,如long polling,WebSocket,并提供相应的Api供开发人员选择如何调用,帮助其可以简单快速地实现...
实例105 构建简单的EJB 368 实例106 无状态会话Bean编程 371 实例107 简单的累加器 374 实例108 简单Bean管理的实体 377 实例109 创建CMP EntityBean 386 实例110 编写MessageDrivenBean 392 实例111 使用JNDI 396 ...
java聊天室 2个目标文件,简单。 java模拟掷骰子2个 1个目标文件,输出演示。 java凭图游戏 一个目标文件,简单。 java求一个整数的因子 如题。 Java生成密钥的实例 1个目标文件 摘要:Java源码,算法相关,密钥 ...
116.htm 在程序中打开 Internet 拨号连接窗口 ◆ 117.htm 在一个单位内部或通过广域协议(如X.25)互联的行业内部都有几十或上万台计算机互联,用Intranet虽然可以建立聊天室,但实现点对点实时对话却比较困难...
泥巴 一组模块,用于构建由多个协议...聊天室 ctf 谜 蛇 去做 触控板 进一步阅读 读灯: 协议缓冲区 雷神之锤3网络模型 亚努斯 “ 行星歼灭的技术:ChronoCam ” “编织中倒带的实现” “相对论复制” “源多人
JAVA+JSP的聊天室 8个目标文件 简单 JavaScript万年历 显示出当前时间及年份,还可以选择年份及月份和日期 Java编写的HTML浏览器 一个目标文件 摘要:Java源码,网络相关,浏览器 Java编写的HTML浏览器源代码,一个很...
java聊天室 2个目标文件,简单。 java模拟掷骰子2个 1个目标文件,输出演示。 java凭图游戏 一个目标文件,简单。 java求一个整数的因子 如题。 Java生成密钥的实例 1个目标文件 摘要:Java源码,算法相关,...
java聊天室 2个目标文件,简单。 java模拟掷骰子2个 1个目标文件,输出演示。 java凭图游戏 一个目标文件,简单。 java求一个整数的因子 如题。 Java生成密钥的实例 1个目标文件 摘要:Java源码,算法相关,...
java聊天室 2个目标文件,简单。 java模拟掷骰子2个 1个目标文件,输出演示。 java凭图游戏 一个目标文件,简单。 java求一个整数的因子 如题。 Java生成密钥的实例 1个目标文件 摘要:Java源码,算法相关,...
JAVA+JSP的聊天室 8个目标文件 简单 JavaScript万年历 显示出当前时间及年份,还可以选择年份及月份和日期 Java编写的HTML浏览器 一个目标文件 摘要:Java源码,网络相关,浏览器 Java编写的HTML浏览器源代码,一个很...
JAVA+JSP的聊天室 8个目标文件 简单 JavaScript万年历 显示出当前时间及年份,还可以选择年份及月份和日期 Java编写的HTML浏览器 一个目标文件 摘要:Java源码,网络相关,浏览器 Java编写的HTML浏览器源代码,一个很...
java聊天室 2个目标文件,简单。 java模拟掷骰子2个 1个目标文件,输出演示。 java凭图游戏 一个目标文件,简单。 java求一个整数的因子 如题。 Java生成密钥的实例 1个目标文件 摘要:Java源码,算法相关,密钥 ...