html服务依据的通信协议,依据通信协议设计一个简单的聊天对话

1、通信首先要有服务端和客户端,并且服务端和客户端要分开设计(一般用是一个工程用于设计服务器,

另建一个工程设计客户端)。运行时先运行服务器,在运行客户端。

2、通信中两个重要的内容:Socket 和 ServerSocket 。

Socket:在客户端通过建立Socket对象,并根据ip和端口连接服务器,还可以通过Socket获取输入输

出流对象,用于读取从服务器发送过来的消息和往服务器发送消息。

ServerSocket:在服务端用于监听服务器设置的端口,等待接受客户端Socket对象的连接。

这两个内容把服务端和客户端连接起来

3、通信中两个主要的内容:发送消息和接收消息。

无论是服务端还是客户端,都是通过Socket对象获取输入输出流对象。并且都是通过字节的形式

发送和接受消息的,发送前先把发送的内容转换成字节的形式,然后再发送,接受到的发送的内容是字

节的形式,接收后要转换成自己需要的形式。

发送消息:OutputStream,它有write写入的方法。

接收消息:InputStream,它有read读取的方法。

4、主要实现的功能:注册账号(注册已存在的账号时,会提示此账号已存在)、登录(不可同时登录两

个相同的账号)、聊天(群聊和点对点(在线的用户可进行的功能))。

5、服务端:

用于创建ServerSocket对象和接受Socket的访问的类:MyServer

package com.fuwuduan;

import java.io.IOException;

import java.net.ServerSocket;

import java.net.Socket;

import java.util.ArrayList;

public class MyServer {

//创建一个队列用于存储每个客户端访问时创建的线程

public static ArrayList list = new ArrayList();

public void setServer(int port){

try {

//创建ServerSocket套接字对象用于监听port端口

ServerSocket ss = new ServerSocket(port);

while(true){

System.out.println("服务器等待客户端的访问");

//循环等待接受客户端的访问,如果没有客户端的访问,循环就在这停止等待客户端的访问

Socket socket = ss.accept();

System.out.println("已连有客户接服务器");

//创建ServerThread线程对象,把客户端的连接放到线程里处理,并启动线程

ServerThread st = new ServerThread(socket);

st.start();

}

} catch (IOException e) {

e.printStackTrace();

}

}

public static void main(String[] args) {

new MyServer().setServer(8888);

}

}

用于处理客户端的连接的线程类:ServerThread

package com.fuwuduan;

import java.io.ByteArrayOutputStream;

import java.io.FileInputStream;

import java.io.FileOutputStream;

import java.io.IOException;

import java.io.InputStream;

import java.io.OutputStream;

import java.net.Socket;

import java.util.HashMap;

import java.util.Iterator;

import java.util.Set;

public class ServerThread extends Thread {

private String userName;

private InputStream input;

private OutputStream output;

private Socket socket; //把socket对象通过构造方法传过来

public static HashMap map = new HashMap();//哈希表用于存储账号和密码

/**

* 构造方法把socket传过来

* @param socket

*/

public ServerThread(Socket socket){

this.socket = socket;

}

/**

* 读文件,把文件中的用户和密码读出来,并放到哈希表中

*/

public void readFile(){

map.clear(); // 往哈希表中村用户和密码前,先清空哈希表

int count = 0; // 用于计数多少用户

try {

//由于往文件中写入账号和密码后用\n结尾,可以根据\n先读取文件中有多少用户

FileInputStream fis1 = new FileInputStream("D:/Users/Adminstrator/workspace/通信服务器0826/src/com/fuwuduan/cunchu");

int n = fis1.read();

while(n != -1){

if(n == '\n'){

count++;

}

n = fis1.read();

}

fis1.close(); //读完文件直接关闭文件输入流

//在次读取文件,根据写入的方式读出用户和密码

FileInputStream fis2 = new FileInputStream("D:/Users/Adminstrator/workspace/通信服务器0826/src/com/fuwuduan/cunchu");

for(int i=0; i

readFileLine(fis2);

String userName = readFileLine(fis2); //读取用户名

readFileLine(fis2);

readFileLine(fis2);

String passWord = readFileLine(fis2); //读取密码

map.put(userName, passWord); //把用户和密码存到哈希表中

}

fis2.close(); //读完文件直接关闭文件输入流

} catch (Exception e) {

e.printStackTrace();

}

}

public void run(){

try {

//得到网络连接的输入输出流对象

input = socket.getInputStream();

output = socket.getOutputStream();

//从客户端读取是注册还是确定

String cmd = readLine(input);

if("注册".equals(cmd)){

while(true){

//读文件里的账号和密码

readFile();

//读取注册的账号

String suer = readLine(input);

//if("注册窗口已经关闭".equals(suer)){

//break;

//}

//读取注册的密码

String spass = readLine(input);

//初始化一个布尔类型的变量,用于标示注册的用户是否存在,true表示注册的用户不存在,false表示注册的用户存在

boolean Flag = true;

Set set = map.keySet(); //创建Set对象,并获取哈希表中的用户名集合,Set中存的内容是无序的且不可重复的

Iterator iter = set.iterator(); // 创建迭代对象,

while(iter.hasNext()){

String key = iter.next();

if(suer!=null&&suer.equals(key)){

//往客户端写入内容

String msg = "此账号已存在,请重新注册#";

System.out.println(msg);

output.write(msg.getBytes());

Flag = false;

break;

}

}

if(Flag == true){

//往客户端写入内容

String msg = "注册成功#";

output.write(msg.getBytes());

//往文件里写注册的账号和密码

FileOutputStream fos = new FileOutputStream("D:/Users/Adminstrator/workspace/通信服务器0826/src/com/fuwuduan/cunchu",true);

//往文件里写账号和密码

fos.write("账号: ".getBytes());

fos.write((suer+" , ").getBytes());

fos.write("密码: ".getBytes());

fos.write((spass+"\n ").getBytes());

fos.close();

}

}

}

if("确定".equals(cmd)){

//读文件里的账号和密码

readFile();

//往客户端写入内容

String msg = "请输入账号 #";

output.write(msg.getBytes());

//从客户端读取账号

userName = readLine(input);

//往客户端写入内容

msg = "请输入密码 #";

output.write(msg.getBytes());

//从客户端读取密码

String passWord = readLine(input)+"\n";

//判断账号和密码是否正确

if(passWord.equals(map.get(userName))&&eqUser()){

//把每个客户访问创建的线程存储到队列中

MyServer.list.add(this);

msg = "服务器登录成功#";

System.out.println(msg);

output.write(msg.getBytes());

while(true){

msg = readLine(input); //从客户端读取一行

while(true){

if((1+"").equals(msg)){ //群发消息

msg = readLine(input); //从客户端读取一行

while(!(2+"").equals(msg)){

String msg1 = userName+"说:\n "+msg+"#";

sendAllMsg(msg1);

msg = readLine(input);

}

}

if((2+"").equals(msg)){ //点对点发消息

msg = readLine(input); //从客户端读取一行

while(!(1+"").equals(msg)){

for(int i=0; i

if(MyServer.list.get(i).getUserName().equals(msg)){

String msg1 = userName+"说:\n "+readLine(input)+"#";

sendMsg(msg1); //往发出信息的客户端发送这条信息

ServerThread sst = MyServer.list.get(i);

sst.sendMsg(msg1); //往要接受的用户发送信息

}

}

msg = readLine(input);

}

}else{

break;

}

}

if(!(1+"").equals(msg)&&!(2+"").equals(msg)){

String strname = "请点击群聊还是点对点#";

output.write(strname.getBytes());

}

}

}else if(passWord.equals(map.get(userName))&&!eqUser()){

msg = "账号已经登录#";

System.out.println(msg);

output.write(msg.getBytes());

}else{

msg = "账号或密码输入不正确,请重新输入 #";

output.write(msg.getBytes());

System.out.println(msg);

}

}

} catch (IOException e) {

e.printStackTrace();

}finally{

//关闭流和socket

try {

if(socket != null){

socket.close();

}

if(input != null){

input.close();

}

if(output != null){

output.close();

}

} catch (IOException e) {

e.printStackTrace();

}

}

}

/**

* 发送一条消息

*/

public void sendMsg(String msg){

try {

output.write(msg.getBytes());

} catch (IOException e) {

e.printStackTrace();

}

}

/**

* 群发消息

* @param msg

*/

public void sendAllMsg(String msg){

for(int i=0; i

ServerThread st = MyServer.list.get(i);

st.sendMsg(msg);

}

}

/**

* 每次读取一行

* @param input 输入流对象

* @return

* @throws IOException

*/

public String readLine(InputStream input) throws IOException{

//创建一个队列

ByteArrayOutputStream intent = new ByteArrayOutputStream();

while(true){

//每次读一个字节

int n = input.read();

if(n == '#'){

break;

}

//把每次读取的字节存到队列中

intent.write(n);

}

//把队列转换成字节数组

byte[] bytes = intent.toByteArray();

//把字节转换成字符串

String str = new String(bytes, "GB2312");

return str;

}

/**

* 读取文件时每次读取一行

* @param input 输入流对象

* @return

* @throws IOException

*/

public static String readFileLine(FileInputStream input) throws IOException{

//创建一个队列

ByteArrayOutputStream intent = new ByteArrayOutputStream();

while(true){

//每次读一个字节

int n = input.read();

if(n == ' '){

break;

}

//把每次读取的字节存到队列中

intent.write(n);

}

//把队列转换成字节数组

byte[] bytes = intent.toByteArray();

//把字节转换成字符串

String str = new String(bytes, "GB2312");

return str;

}

/**

* 获取用户名

* @return

*/

public String getUserName() {

return userName;

}

/**

* 比较此用户是否登录

* @return

*/

public boolean eqUser(){

for(int i=0; i

ServerThread st = MyServer.list.get(i);

if(st.getUserName().equals(userName)){

return false;

}

}

return true;

}

}

6、客户端:

登录界面:Cilent

package com.kehuiduan;

import java.awt.Dimension;

import java.awt.FlowLayout;

import javax.swing.JButton;

import javax.swing.JComboBox;

import javax.swing.JFrame;

import javax.swing.JLabel;

import javax.swing.JPasswordField;

public class Cilent {

/**

* 开始登录界面

*/

public void getUI(){

JFrame jf = new JFrame("登录界面");

jf.setLayout(new FlowLayout());

JLabel jb1 = new JLabel("账号");

jf.add(jb1);

//实例化一个JComboBox类的对象

JComboBox jcbName = new JComboBox();

//设置下拉框对象可以编辑

jcbName.setEditable(true);

//设置jcbName的大小

jcbName.setPreferredSize(new Dimension(220,25));

jf.add(jcbName);

JLabel jb2 = new JLabel("密码");

jf.add(jb2);

//实例化一个JPasswordField类的对象

JPasswordField jpaPwd = new JPasswordField();

//设置jpaPwd的大小

jpaPwd.setPreferredSize(new Dimension(220,25));

jf.add(jpaPwd);

JButton jb3 = new JButton("确定");

jf.add(jb3);

JButton jb4 = new JButton("注册");

jf.add(jb4);

//jf.setUndecorated(true);

jf.setSize(300,290); //给窗体设置大小

jf.setResizable(false); //设置窗体的大小是否改变

jf.setLocationRelativeTo(null); //居中

jf.setDefaultCloseOperation(3); //关闭窗体时,关闭程序

jf.setVisible(true); //显示窗体

//创建事件监听器对象,给按钮添加监听器

ClientAction cal = new ClientAction(jf,jcbName,jpaPwd);

jb3.addActionListener(cal);

jb4.addActionListener(cal);

}

public static void main(String[] args) {

new Cilent().getUI();

}

}

登录界面的事件监听器类:ClientAction

package com.kehuiduan;

import java.awt.event.ActionEvent;

import java.awt.event.ActionListener;

import java.io.ByteArrayOutputStream;

import java.io.IOException;

import java.io.InputStream;

import java.io.OutputStream;

import java.net.Socket;

import javax.swing.JComboBox;

import javax.swing.JFrame;

import javax.swing.JOptionPane;

import javax.swing.JPasswordField;

import 对话界面.MyDialog;

import 注册.ZhuCe;

public class ClientAction implements ActionListener{

private JComboBox jcbName;

private JPasswordField jpaPwd;

private JFrame jf;

private InputStream input;

private OutputStream output;

private Socket client;

public ClientAction(JFrame jf, JComboBox jcbName, JPasswordField jpaPwd){

this.jf = jf;

this.jcbName = jcbName;

this.jpaPwd = jpaPwd;

}

public void actionPerformed(ActionEvent e) {

String cmd = e.getActionCommand();

try {

//连接服务器

client = new Socket("192.168.0.133",8888);

//得到输入输出流对象

input = client.getInputStream();

output = client.getOutputStream();

if("确定".equals(cmd)){

//往服务器发一条消息

output.write(("确定"+"#").getBytes());

//得到下拉框的内容

String userName = (String) jcbName.getSelectedItem();

//得到密码框的内容

char[] charpwd = jpaPwd.getPassword();

String passWord = new String(charpwd);

//从服务器读取一条信息

String msg = readLine(input);

System.out.println(msg);

//往服务器书写账号

System.out.println("userName :"+userName);

output.write((userName+"#").getBytes());

//从服务器读取一条信息

msg = readLine(input);

System.out.println(msg);

//往服务器书写密码

System.out.println("passWord :"+passWord);

output.write((passWord+"#").getBytes());

//从服务器读取一条信息

msg = readLine(input);

System.out.println(msg);

if(msg.equals("服务器登录成功")){

//登录成功,关闭登录界面

jf.dispose();

MyDialog st = new MyDialog(input, output, userName);

st.mgUI();

}else if(msg.equals("账号已经登录")){

JOptionPane.showMessageDialog(null, msg);

}else{

//登录不成功,弹出一个对话框

System.out.println(msg);

JOptionPane.showMessageDialog(null, msg);

}

}

if("注册".equals(cmd)){

//往服务器发一条消息

output.write(("注册"+"#").getBytes());

ZhuCe zhuce = new ZhuCe(input,output);

zhuce.cell();

}

} catch (Exception e1) {

e1.printStackTrace();

//运行报错时关闭Socket和输入输出流

try {

if(client != null){

client.close();

}

if(input != null){

input.close();

}

if(output != null){

output.close();

}

} catch (IOException e2) {

e2.printStackTrace();

}

}

}

/**

* 每次读取一行

* @param input 输入流对象

* @return

* @throws IOException

*/

public String readLine(InputStream input) throws IOException{

//创建一个队列

ByteArrayOutputStream intent = new ByteArrayOutputStream();

while(true){

//每次读一个字节

int n = input.read();

if(n == '#'){

break;

}

//把每次读取的字节存到队列中

intent.write(n);

}

//把队列转换成字节数组

byte[] bytes = intent.toByteArray();

//把字节转换成字符串

String str = new String(bytes, "GB2312");

return str;

}

}

注册界面: ZhuCe

package 注册;

import java.awt.Dimension;

import java.awt.FlowLayout;

import java.io.InputStream;

import java.io.OutputStream;

import javax.swing.JButton;

import javax.swing.JFrame;

import javax.swing.JLabel;

import javax.swing.JPasswordField;

import javax.swing.JTextField;

public class ZhuCe {

private InputStream input;

private OutputStream output;

public ZhuCe(InputStream input, OutputStream output) {

this.input = input;

this.output = output;

}

public void cell(){

JFrame jf = new JFrame();

jf.setTitle("注册界面");

jf.setLayout(new FlowLayout());

JLabel jb1 = new JLabel("账号");

jf.add(jb1);

//实例化一个JTextField类的对象

JTextField jcbName = new JTextField();

//设置jcbName的大小

jcbName.setPreferredSize(new Dimension(220,25));

jf.add(jcbName);

JLabel jb2 = new JLabel("密码");

jf.add(jb2);

//实例化一个JPasswordField类的对象

JPasswordField jpaPwd = new JPasswordField();

//设置jpaPwd的大小

jpaPwd.setPreferredSize(new Dimension(220,25));

jf.add(jpaPwd);

JButton jb3 = new JButton("确定");

jf.add(jb3);

jf.setSize(300,290); //给窗体设置大小

jf.setLocationRelativeTo(null); //居中

jf.setDefaultCloseOperation(3); //关闭窗体时,关闭程序

jf.setVisible(true); //显示窗体

//创建事件监听器对象,给按钮添加监听器

ZhuCeAction dat = new ZhuCeAction(input, output, jf, jcbName, jpaPwd);

jb3.addActionListener(dat);

}

}

注册界面的事件监听器类:

package 注册;

import java.awt.event.ActionEvent;

import java.awt.event.ActionListener;

import java.io.ByteArrayOutputStream;

import java.io.IOException;

import java.io.InputStream;

import java.io.OutputStream;

import javax.swing.JFrame;

import javax.swing.JOptionPane;

import javax.swing.JPasswordField;

import javax.swing.JTextField;

public class ZhuCeAction implements ActionListener{

private JTextField jcbName;

private JPasswordField jpaPwd;

private JFrame jf;

private InputStream input;

private OutputStream output;

public ZhuCeAction(InputStream input, OutputStream output, JFrame jf, JTextField jcbName, JPasswordField jpaPwd){

this.input = input;

this.output = output;

this.jf = jf;

this.jcbName = jcbName;

this.jpaPwd = jpaPwd;

}

/**

*

*/

public void actionPerformed(ActionEvent e) {

String userName = jcbName.getText(); //得到文本框输入的内容

char[] ch = jpaPwd.getPassword(); //得到密码框输入的内容

String passWord = new String(ch); //把从密码框得到的内容转换成字符串

try {

output.write((userName+"#").getBytes()); // 把账号发给服务器

output.write((passWord+"#").getBytes()); // 把密码发给服务器

String msg = readLine(input); //读取从服务器发送过来的信息

System.out.println(msg);

if("此账号已存在,请重新注册".equals(msg)){

//注册不成功,弹出一个对话框

System.out.println(msg);

JOptionPane.showMessageDialog(null, msg);

}

if("注册成功".equals(msg)){

//String ss = "注册窗口已经关闭#";

//output.write(ss.getBytes());

jf.dispose();

}

} catch (IOException e2) {

e2.printStackTrace();

}

}

/**

* 每次读取一行

* @param input 输入流对象

* @return

* @throws IOException

*/

public String readLine(InputStream input) throws IOException{

//创建一个队列

ByteArrayOutputStream intent = new ByteArrayOutputStream();

while(true){

//每次读一个字节

int n = input.read();

if(n == '#'){

break;

}

//把每次读取的字节存到队列中

intent.write(n);

}

//把队列转换成字节数组

byte[] bytes = intent.toByteArray();

//把字节转换成字符串

String str = new String(bytes, "GB2312");

return str;

}

}

聊天界面:MyDialog

package 对话界面;

import java.awt.FlowLayout;

import java.io.InputStream;

import java.io.OutputStream;

import javax.swing.JButton;

import javax.swing.JFrame;

import javax.swing.JLabel;

import javax.swing.JScrollPane;

import javax.swing.JTextArea;

public class MyDialog {

private InputStream input ;

private OutputStream output ;

private String userName;

public MyDialog (InputStream input, OutputStream output,String userName){

this.input = input;

this.output = output;

this.userName = userName;

}

/**

* 对话框界面

*/

public void mgUI(){

JFrame jf = new JFrame(userName+"--对话界面");

jf.setLayout(new FlowLayout());

//设置一个文本区设置行和列,然后添加到窗体上

JTextArea jtf = new JTextArea(17,50);

jtf.setEditable(false); //文本区不可直接往上面写内容

//jtf.setPreferredSize(new Dimension(550,350));

JScrollPane jspf = new JScrollPane(jtf); //内容超过文本区设置的行和列时,自动生成滚动条

jf.add(jspf);

JButton jb1 = new JButton("群聊");

jf.add(jb1);

JButton jb2 = new JButton("点对点");

jf.add(jb2);

JLabel jl = new JLabel("对话输入框:");

jf.add(jl);

JTextArea jtf1 = new JTextArea(10,50);

//jtf1.setPreferredSize(new Dimension(550,100));

JScrollPane jspf1 = new JScrollPane(jtf1);

jf.add(jspf1);

JButton jb3 = new JButton("发送");

jf.add(jb3);

jf.setSize(620,600); //给窗体设置大小

jf.setLocationRelativeTo(null); //居中

jf.setDefaultCloseOperation(3); //关闭窗体时,关闭程序

jf.setVisible(true); //显示窗体

DialogThread dt = new DialogThread(input, jtf);

dt.start();

//创建事件监听器对象,给按钮添加监听器

DialogAction dat = new DialogAction(output,jtf1);

jb1.addActionListener(dat);

jb2.addActionListener(dat);

jb3.addActionListener(dat);

}

}

聊天界面接受消息的线程类:DialogThread

package 对话界面;

import java.io.ByteArrayOutputStream;

import java.io.IOException;

import java.io.InputStream;

import javax.swing.JOptionPane;

import javax.swing.JTextArea;

public class DialogThread extends Thread{

private InputStream input ;

private JTextArea jtf;

public DialogThread(InputStream input, JTextArea jtf) {

this.input = input;

this.jtf = jtf;

}

public void run(){

while(true){

try{

//从服务器读取一条信息

String msg = readLine(input);

//如果没有点击是群聊还是点对点,弹出一个提示框

if("请点击群聊还是点对点".equals(msg)){

JOptionPane.showConfirmDialog(null, msg);

continue;

}

//将读取的信息在第一个文本框中显示出来

jtf.append(msg+"\n");

}catch (Exception e) {

e.printStackTrace();

}

}

}

/**

* 每次读取一行

* @param input 输入流对象

* @return

* @throws IOException

*/

public String readLine(InputStream input) throws IOException{

//创建一个队列

ByteArrayOutputStream intent = new ByteArrayOutputStream();

while(true){

//每次读一个字节

int n = input.read();

if(n == '#'){

break;

}

//把每次读取的字节存到队列中

intent.write(n);

}

//把队列转换成字节数组

byte[] bytes = intent.toByteArray();

//把字节转换成字符串

String str = new String(bytes, "GB2312");

return str;

}

}

聊天界面的事件监听器类:

package 对话界面;

import java.awt.event.ActionEvent;

import java.awt.event.ActionListener;

import java.io.IOException;

import java.io.OutputStream;

import javax.swing.JTextArea;

public class DialogAction implements ActionListener{

private OutputStream output ;

private JTextArea jtf1;

public DialogAction(OutputStream output, JTextArea jtf1) {

this.output = output;

this.jtf1 = jtf1;

}

public void actionPerformed(ActionEvent e) {

//获取按钮上的字符串

String str = e.getActionCommand();

//得到在第二个文本框中输入的内容

String cmd = jtf1.getText();

try {

if("群聊".equals(str)){

String a = 1+"#";

output.write(a.getBytes()); //往服务器发送一条信息

}

if("点对点".equals(str)){

String b = 2+"#";

output.write(b.getBytes()); //往服务器发送一条信息

}

if("发送".equals(str)){

output.write((cmd+"#").getBytes()); //往服务器发送一条信息

}

} catch (IOException e1) {

e1.printStackTrace();

}

}

}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值