大一在校,第一次写博客,最近写了用Java Socket实现的简单私聊群聊,代码本身存在问题,就是在这里记录一下成长历程,意识到自己现在很菜,以后会多找些小项目练手,多写博客。这个程序其中有不少不完善的地方,各位不要借鉴。。。。。。
欢迎指正
关于思路问题就是制定一个自己的通讯协议,比如正常文字消息就直接群发,遍历outputstream,如果是@+对方昵称+:就把名字提取出来,利用HashMap映射发给该昵称对应的输出流。客户端窗口为windowbuilder构造。下面是代码:
server类:
import java.awt.Button;
import java.awt.EventQueue;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import javax.swing.JFrame;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.SwingConstants;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
public class Server extends JFrame implements ActionListener{
//创建服务器界面类
private JButton btn,btn1;
private JLabel label;
private ServerSocket sereverSocket;
public static ClientMap<String, DataOutputStream> clients=new ClientMap<>();
public static void main(String[] args) {
Server server =new Server();
}
public Server() {
addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
initialize();
}
//创建服务器界面
private void initialize() {
btn=new JButton(“开启服务器”);
btn1=new JButton(“关闭服务器”);
btn1.setEnabled(false);
btn.addActionListener(this);
btn1.addActionListener(this);
label = new JLabel(“服务器未开启”);
getContentPane().add(label);
getContentPane().add(btn);
getContentPane().add(btn1);
setTitle(“服务器端”);
getContentPane().setLayout(new GridLayout(3, 0, 0, 0));
setSize(500, 350);
setLocation(200, 200);
setVisible(true);
setDefaultCloseOperation(EXIT_ON_CLOSE);
}
//对按钮创建监听器
@Override
public void actionPerformed(ActionEvent e) {
// TODO 自动生成的方法存根
if(e.getSource()==btn) {
label.setText(“服务器已开启!”);
btn.setEnabled(false);
btn1.setEnabled(true);
try {
sereverSocket=new ServerSocket(Constant.port);
while(true) {
Socket socket=sereverSocket.accept();
Thread thread =new Thread(new ServerThread(socket));
thread.start();
label.setText(“服务器已开启”);
}
} catch (IOException e1) {
// TODO 自动生成的 catch 块
e1.printStackTrace();
}
}else if(e.getSource()==btn1) {
label.setText(“服务器已关闭!”);
btn.setEnabled(true);
btn1.setEnabled(false);
System.exit(0);
}
}
}
ServerThread类:
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.Socket;
import java.text.SimpleDateFormat;
import java.util.Date;
public class ServerThread implements Runnable{
//服务器端线程类
private Socket socket;
private DataInputStream inputStream=null;
private DataOutputStream outputStream=null;
public ServerThread(Socket s) {
this.socket=s;
}
@Override
public void run() {
// TODO 自动生成的方法存根
SimpleDateFormat df=new SimpleDateFormat(“yyyy-MM-dd HH:mm:ss”);
try {
inputStream =new DataInputStream(socket.getInputStream());
outputStream =new DataOutputStream(socket.getOutputStream());
String content=null;
while((content=inputStream.readUTF())!=null) {
if(content.startsWith(Constant.USER)) {
String name=getName(content);
if(Server.clients.containsKey(name)) {
System.out.print(“该用户名已存在!!!请重新设置昵称!”);
}else{
Server.clients.put(name, outputStream);
for(DataOutputStream outputStream:Server.clients.ValueSet()) {
outputStream.writeUTF(df.format(new Date())+”\n”+name+”进入了聊天室!”);
}
}
}else if(content.startsWith(Constant.U)) {
String name_1=getChatName(content);
String primsg=getRealMsg(content);
Server.clients.get(name_1).writeUTF(df.format(new Date())+”\n”+”私聊<—-+++&&&+++—->”+Server.clients.getKeyByvalue(outputStream)+”对你说:”+primsg);
}else {
String whoSend=Server.clients.getKeyByvalue(outputStream);
for(DataOutputStream outputStream:Server.clients.ValueSet()) {
outputStream.writeUTF(df.format(new Date())+”\n”+whoSend+”:”+content);
}
}
}
} catch (IOException e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
}
}
//提取登录名
private String getName(String s) {
return s.substring(2);
}
//提取私聊对象昵称
private String getChatName(String s) {
return s.substring(1, s.indexOf(Constant.colon));
}
//提取私聊信息内容
private String getRealMsg(String s) {
return s.substring(s.indexOf(“:”));
}
}
ClientMap类:
import java.util.HashMap;
import java.util.HashSet;
import java.util.Set;
//通过API了解到HashMap类,继承HashMap类,通过用户昵称到其对应输出流的映射,实现私聊,群聊
public class ClientMap<K, V> extends HashMap<K, V> {
//根据Value删除指定项
public void removeByValue(Object value) {
for(Object key:keySet()) {
if(get(key)==value||get(key).equals(value)) {
remove(key);
break;
}
}
}
//获取Value列表
public Set<V> ValueSet(){
Set<V> valueList=new HashSet<V>();
for(Object key:keySet()) {
valueList.add(get(key));
}
return valueList;
}
//重写put方法
public V put(K key,V value) {
for (V val:ValueSet()) {
if(val==value||val.equals(value)) {
throw new RuntimeException(“value重复!”);
}
}
return super.put(key, value);
}
public K getKeyByvalue(Object value) {
for(K key:keySet()) {
if(get(key)==value||get(key).equals(value)) {
return key;
}
}return null;
}
}
Constant常量类:
public class Constant {
//常量类
public static final String USER=”用户”;
public static final String U=”@”;
public static final String colon=”:”;
public static final int port=8899;
}
Client类:
import java.awt.EventQueue;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.Date;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JScrollPane;
import javax.swing.JTextField;
import javax.naming.spi.DirStateFactory.Result;
import javax.swing.JButton;
import javax.swing.JTextArea;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.ActionEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import javax.swing.SwingConstants;
import java.awt.Toolkit;
public class Client implements Runnable{
//创建用户界面类
private JFrame frame;
private JTextField textField;
private JTextField textField_1;
private JTextArea textArea;
private JButton button_1 ;
private JScrollPane scrollPane;
private Socket socket=null;
private DataInputStream inputstream=null;
private DataOutputStream outputstream=null;
/**
* Launch the application.
*/
public static void main(String[] args) {
Client client = new Client();
client.frame.setVisible(true);
Thread thread = new Thread(client);
thread.start();
}
/**
* Create the application.
*/
public Client() {
initialize();
try {
socket=new Socket(“127.0.0.1”,Constant.port);
inputstream=new DataInputStream(socket.getInputStream());
outputstream=new DataOutputStream(socket.getOutputStream());
} catch (UnknownHostException e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
} catch (IOException e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
}
}
/**
* Initialize the contents of the frame.
* 创建用户端界面
*/
private void initialize() {
frame = new JFrame();
frame.setIconImage(Toolkit.getDefaultToolkit().getImage(Client.class.getResource(“/images/qq.png”)));
frame.setTitle(“\u5317\u533A3012\u5C0F\u7EC4-\u591A\u7A97\u53E3\u7248”);
frame.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
try {
if (socket != null)
socket.close();
if (inputstream!= null)
inputstream.close();
if (outputstream != null)
outputstream.close();
} catch (IOException e1) {
e1.printStackTrace();
}
}
});
frame.setBounds(100, 100, 744, 409);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().setLayout(null);
JLabel label = new JLabel(“\u6635\u79F0 \uFF1A”);
label.setBounds(49, 33, 58, 15);
frame.getContentPane().add(label);
textField = new JTextField();
textField.setBounds(100, 31, 265, 21);
frame.getContentPane().add(textField);
textField.setColumns(10);
JButton button = new JButton(“\u767B\u5F55”);
button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
String s1=textField.getText();
if(textField.getText().length()==0) {
textArea.append(“昵称不能为空!\n”);
}else {
textField.setEditable(false);
button.setEnabled(false);
String s2=Constant.USER+s1;
try {
outputstream.writeUTF(s2);
} catch (IOException e1) {
// TODO 自动生成的 catch 块
e1.printStackTrace();
}
if(Server.clients.containsKey(s1)) {
textArea.append(“用户名重复!”);
}else {
button_1.setEnabled(true);
}
}
}
});
button.setBounds(389, 30, 97, 23);
frame.getContentPane().add(button);
JScrollPane scrollPane_1 = new JScrollPane();
scrollPane_1.setBounds(38, 69, 473, 223);
frame.getContentPane().add(scrollPane_1);
textArea = new JTextArea();
scrollPane_1.setViewportView(textArea);
textArea.setLineWrap(true);
textArea.setWrapStyleWord(true);
textArea.setEditable(false);
textField_1 = new JTextField();
textField_1.setBounds(31, 322, 372, 21);
frame.getContentPane().add(textField_1);
textField_1.setColumns(10);
button_1 = new JButton(“\u53D1\u9001”);
button_1.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
String s=textField_1.getText();
if(!s.equals(“”)) {
try {
outputstream.writeUTF(s);
} catch (IOException e1) {
// TODO 自动生成的 catch 块
e1.printStackTrace();
}
} textField_1.setText(“”);
}
});
button_1.setBounds(418, 324, 97, 23);
frame.getContentPane().add(button_1);
button_1.setEnabled(false);
JTextArea textArea_1 = new JTextArea();
textArea_1.setEditable(false);
textArea_1.setText(“\u4F7F\u7528\u8BF4\u660E\uFF1A1.\u767B\u9646\u540E\u76F4\u63A5\r\n\r\n\u53D1\u6D88\u606F\u4E3A\u7FA4\u804A\uFF1B2.@+\u5BF9\r\n\r\n\u65B9\u6635\u79F0+\uFF1A(\u4E2D\u6587\u5192\u53F7)+\r\n\r\n\u5185\u5BB9\u5373\u53EF\u8FDB\u884C\u79C1\u804A\u3002”);
textArea_1.setBounds(541, 43, 143, 318);
frame.getContentPane().add(textArea_1);
}
@Override
public void run() {
// TODO 自动生成的方法存根
try {
inputstream = new DataInputStream(socket.getInputStream());
while (true) {
String s = inputstream.readUTF();
textArea.append(s+”\n”);
}
} catch (IOException e) {
}
}
}