广州大学学生实验报告
开课实验室:计算机科学与工程实验 2020年x月x日
学院 | 计算机科学与网络工程学院 | 年级、专业、班 |
实验课程名称 | 网络编程实验 |
实验1:TCP编程
源代码:
服务端/Server:
package chat;
import com.kuang.dao.UserDao;
import com.kuang.dao.UserDaoImpl;
import com.kuang.pojo.Message;
import com.kuang.pojo.User;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.*;
public class servergui implements Runnable {
static HashSet<Socket> socketList = new HashSet<Socket>();//客户端列表
static List<String> namelist = new ArrayList<>();//所有用户的名字,包括未登录
static List<String> loginuser = new ArrayList<String>(); //已登录的用户
private String name;
private Socket socket;
private static int sum=0;
public static HashMap<String,Socket> link;
public static ServerSocket server;
public servergui(Socket socket,String name) {
this.socket = socket;
this.name =name;
}
public static void main(String[] args) throws Exception {
try {
server = new ServerSocket(12345);
System.out.println("服务器启动,正在等待连接...");
//构建用户名列表以及总数生成id,避免用户名相同
ApplicationContext context = new ClassPathXmlApplicationContext("mybatis-config.xml");
UserDaoImpl mapper = (UserDaoImpl) context.getBean("UserDao",UserDao.class);
namelist =mapper.selectname();
sum=mapper.selectcount();
String name=null;
link=new HashMap<String,Socket>();
while(true) {
//接受客户端的Socket,若没有,阻塞在这;
Socket socket = server.accept();
User user1 = null;
while(true) {
ObjectInputStream oIn = new ObjectInputStream(socket.getInputStream());
user1 = (User) oIn.readObject();
int flag = user1.getFlag();
if (flag == 1) {
//登陆
if (namelist.contains(user1.getName())) {
int exist=mapper.getbynamepwd(user1);
if(exist==1&&loginuser.contains(user1.getName())){
ObjectOutputStream oOut = new ObjectOutputStream((socket.getOutputStream()));
oOut.writeObject("登陆失败,用户已登录");
}
else if(exist==1){
ObjectOutputStream oOut = new ObjectOutputStream((socket.getOutputStream()));
oOut.writeObject("登陆成功");
name=user1.getName();
loginuser.add(name);
//把在线用户写过去
oIn = new ObjectInputStream(socket.getInputStream());
String linshi=(String)oIn.readObject();
Message message=new Message(1,loginuser);
socketList.add(socket);
for(Socket a:socketList){
oOut = new ObjectOutputStream((a.getOutputStream()));
oOut.writeObject(message);
}
break;
}
else{
ObjectOutputStream oOut = new ObjectOutputStream((socket.getOutputStream()));
oOut.writeObject("登陆失败,用户名或密码错误");
}
} else {
ObjectOutputStream oOut = new ObjectOutputStream((socket.getOutputStream()));
oOut.writeObject("该用户不存在请注册");
}
} else if (flag == 0) {//注册
if (namelist.contains(user1.getName())) {
ObjectOutputStream oOut = new ObjectOutputStream((socket.getOutputStream()));
oOut.writeObject("该用户名存在,请更改");
} else {
user1.setId(sum + 1);
Map<String, Object> map = new HashMap<String, Object>();
map.put("username", user1.getName());
map.put("pwd", user1.getPwd());
map.put("id", user1.getId());
mapper.addUser(map);
ObjectOutputStream oOut = new ObjectOutputStream((socket.getOutputStream()));
oOut.writeObject("注册成功");
namelist.add(user1.getName());
}
}
}
//每来一个客户端,创建一个线程处理它
link.put(user1.getName(),socket);
servergui user = new servergui(socket,name);
Thread u=new Thread(user);
System.out.println(name+"加入线程");
u.start();
}
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void run() {
try {
ObjectOutputStream oOut;
ObjectInputStream oIn;
while(true) {
//等待接收信息
oIn = new ObjectInputStream(socket.getInputStream());
Message receive = (Message) oIn.readObject();
if(receive.getFlag()==3){
//从客户端列表和已登录用户移除
socketList.remove(socket);
for(int i=0;i<loginuser.size();i++)
if(loginuser.get(i).equals(name))
{
loginuser.remove(i);
break;
}
oOut = new ObjectOutputStream((socket.getOutputStream()));
oOut.writeObject(receive);
socket.close();
System.out.println(name+"已下线");
break;
}
else if(receive.getFlag()==4){
//接受文件到服务端指定路径
//先发送信息过去让客户端把文件发送过来
oOut = new ObjectOutputStream((socket.getOutputStream()));
oOut.writeObject(receive);
String filepath="D://savingdocument";
fliedeverty use=new fliedeverty();
File file=use.receivefile(filepath,socket);
//接受完成,发送信息给接收端看接收端是否愿意接受文件
receive.setFlag(5);
receive.setFile(file);
//发送信息给接收端,让接收端准备接受文件
Socket recei=link.get(receive.getReceiveuser());//获得接收端的Socket
oOut = new ObjectOutputStream((recei.getOutputStream()));
oOut.writeObject(receive);
continue;
}else if(receive.getFlag()==5){
Socket recei=link.get(receive.getReceiveuser());//获得接收端的Socket
String result = receive.getResult();
File file= receive.getFile();
if(result.equals("拒绝接受"))
file.delete();
else{
//发送文件过去
fliedeverty use=new fliedeverty();
use.sentfile(file,recei);
}
continue;
}
Socket recei=link.get(receive.getReceiveuser());//获得接收端的Socket
if(socket==recei)
continue;
receive.setCharmessage(name+"说:"+receive.getCharmessage()+"\r\n");
oOut = new ObjectOutputStream((recei.getOutputStream()));
oOut.writeObject(receive);
}
} catch (IOException e) {
// TODO Auto-generated catch block
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
客户端/client:
package chat;
import com.kuang.pojo.Message;
import com.kuang.pojo.User;
import java.awt.event.*;
import java.io.*;
import java.net.InetAddress;
import java.net.Socket;
import java.util.HashMap;
import java.util.HashSet;
import javax.swing.JList;
import javax.swing.*;
public class clientgui extends JFrame implements KeyListener {
public static Socket s;
public ObjectOutputStream oOut;
public static JTextField text2;
public static JPasswordField text3;
public static JTextField text4;
public static JPasswordField text5;
public static JButton send2;
public static JButton send3;
public static JButton send4;
public static JButton friendchoose;
public static JLabel l1;
public static JLabel l2;
public static JLabel l3;
public static JLabel l4;
public static clientgui frame;
public static clientgui client3;
public static clientgui client;
public JPanel root;
public static JList<String> jlist;//好友列表
public static String selfname;//自己的名字
public receive rea;//一个客户端一个额外的线程
public static HashMap<String,JTextArea> map;//用在多线程
public static HashSet<String> chatopen;//聊天框
public static InetAddress ipadress;
/**
* Launch the application.
* @ClassName:client
* @Author:leyulv
*/
public static void main(String[] args) {
try {
ipadress=InetAddress.getLocalHost();//服务器的ip地址
s=new Socket(ipadress,12345);//打开服务器端口
client=null;
map=new HashMap<>();
frame = new clientgui();//打开登陆窗口
frame.setTitle("登录窗口");
frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
public void putmap(String a,JTextArea b){
map.put(a,b);
}
public clientgui() {//登陆界面
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setBounds(0, 0, 300, 200);
final JPanel root = new JPanel();
this.setLocation(600,300);
this.setContentPane(root);
this.setLayout(null);
l1=new JLabel("账号:");
l2=new JLabel("密码:");
send2=new JButton("登陆");
send3=new JButton("注册用户");
text2=new JTextField();
text3=new JPasswordField ();
l1.setBounds(0, 10, 100, 25);
l2.setBounds(0, 40, 100, 25);
text2.setBounds(40, 10, 200, 25);//账号
text3.setBounds(40, 40, 200, 25);//密码
send2.setBounds(20, 75, 70, 40);//按钮
send3.setBounds(140, 75, 100, 40);//注册
root.add(l1);
root.add(l2);
root.add(text2);
root.add(text3);
root.add(send2);
root.add(send3);
send2.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
loginthing(text2.getText(), String.valueOf(text3.getPassword()));
}
});
send3.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
client3 = new clientgui(1);
client3.setTitle("注册窗口");
client3.setVisible(true);
}
});
text3.addKeyListener(new KeyListener() {//回车响应
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_ENTER) {
loginthing(text2.getText(), String.valueOf(text3.getPassword()));
}
}
public void keyReleased(KeyEvent e) {
}
public void keyTyped(KeyEvent e) {
}
});
}
public void loginthing(String sname,String mima){
selfname=sname;
User user=new User();
user.setName(selfname);
user.setPwd(mima);
user.setId(22);
user.setFlag(1);
try {
oOut = new ObjectOutputStream(s.getOutputStream());
oOut.writeObject(user);
//while(s.getInputStream().available()==0);
ObjectInputStream oIn = new ObjectInputStream(s.getInputStream());
String message=(String)oIn.readObject();
JOptionPane.showMessageDialog(null, message,"", JOptionPane.INFORMATION_MESSAGE);//提示消息,登陆成功&失败
if(message.equals("登陆成功")){
frame.dispose();//销毁登录界面
//打开好友列表
chatlist();
}
} catch (IOException | ClassNotFoundException ex) {
ex.printStackTrace();
}
}
public void chatlist(){//聊天列表
setTitle(selfname+"列表");
setVisible(true);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setBounds(0, 0, 500, 500);
this.setLocation(600,300);
root = new JPanel();
this.setContentPane(root);
this.setLayout(null);
friendchoose=new JButton("聊天");
jlist=new JList<String>(new DefaultListModel<String>());
friendchoose.setBounds(150,10,100,50);//选择按钮
jlist.setBounds(10,10,100,500);//好友列表
//向服务端请求已登录的用户列表
Thread u=null;
try {
//主线程监控更新信息
oOut = new ObjectOutputStream(s.getOutputStream());
oOut.writeObject("打开列表");
//while(s.getInputStream().available()==0);
ObjectInputStream oIn = new ObjectInputStream(s.getInputStream());
Message message=(Message)oIn.readObject();
createuserlist(message);
//专门接受列表信息
rea=new receive(s,message);
u=new Thread(rea);
u.start();
chatopen=new HashSet<>();
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
root.add(jlist);
root.add(friendchoose);
addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
//关闭socket,并通知服务器
try {
Message clse=new Message(3);
ObjectOutputStream oOut = new ObjectOutputStream((s.getOutputStream()));
oOut.writeObject(clse);
} catch (IOException ex) {
ex.printStackTrace();
}
}
});
jlist.addMouseListener(new MouseAdapter() {
public void mouseClicked(MouseEvent e) {
if (jlist.getSelectedIndex() != -1) {
if (e.getClickCount() == 2) {
String mes=(String)jlist.getSelectedValue();
if(mes==null)
JOptionPane.showMessageDialog(null, "请选择好友","", JOptionPane.INFORMATION_MESSAGE);
else if(chatopen.contains(mes))
JOptionPane.showMessageDialog(null, "您已打开该对话框","", JOptionPane.INFORMATION_MESSAGE);
else{
chatopen.add(mes);
clientgui jt=new clientgui(mes,rea);
jt.setTitle(selfname+"(你)和"+mes);
jt.setVisible(true);
}
}
}
}
});
friendchoose.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
String mes=(String)jlist.getSelectedValue();
if(mes==null)
JOptionPane.showMessageDialog(null, "请选择好友","", JOptionPane.INFORMATION_MESSAGE);
else if(chatopen.contains(mes))
JOptionPane.showMessageDialog(null, "您已打开该对话框","", JOptionPane.INFORMATION_MESSAGE);
else{
chatopen.add(mes);
clientgui jt=new clientgui(mes,rea);
jt.setTitle(selfname+"(你)和"+mes);
jt.setVisible(true);
}
}
});
}
//在JList控件更新列表
public static void createuserlist(Message message){
String []liststr=new String[message.getList().size()];
message.getList().toArray(liststr);
jlist.setListData(liststr);
}
public clientgui(int flag1) {//注册界面
this.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
this.setBounds(0, 0, 300, 200);
final JPanel root = new JPanel();
//this.setSize(200,200);
this.setLocation(700,400);
this.setContentPane(root);
this.setLayout(null);
this.setName("注册用户");
l3=new JLabel("账号:");
l4=new JLabel("密码:");
send4=new JButton("注册");
text4=new JTextField();
text5=new JPasswordField ();
l3.setBounds(0, 10, 100, 25);
l4.setBounds(0, 40, 100, 25);
text4.setBounds(40, 10, 200, 25);//账号
text5.setBounds(40, 40, 200, 25);//密码
send4.setBounds(100, 75, 100, 40);//注册
root.add(l3);
root.add(l4);
root.add(text4);
root.add(text5);
root.add(send4);
send4.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
String username=text4.getText();
String mima=text5.getText();
if(username.equals(""))
{
JOptionPane.showMessageDialog(null, "账号为空","", JOptionPane.INFORMATION_MESSAGE);
}
else if(mima.equals("")){
JOptionPane.showMessageDialog(null, "密码为空","", JOptionPane.INFORMATION_MESSAGE);
}
else
{
User user=new User();
user.setName(username);
user.setPwd(mima);
user.setId(22);
user.setFlag(0);
try {
oOut = new ObjectOutputStream(s.getOutputStream());
oOut.writeObject(user);
ObjectInputStream oIn = new ObjectInputStream(s.getInputStream());
String message=(String)oIn.readObject();
JOptionPane.showMessageDialog(null, message,"", JOptionPane.INFORMATION_MESSAGE);
if(message.equals("注册成功"))
client3.dispose();
} catch (IOException | ClassNotFoundException ex) {
ex.printStackTrace();
}
//销毁注册窗口
client3.dispose();
}
}
});
}
public clientgui(String name,receive rea) {//聊天框
this.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
this.setBounds(0, 0, 625, 550);
JButton send= new JButton("发送");
JButton send1= new JButton("选择文件");
JTextArea text1=new JTextArea();
JTextField text = new JTextField();
JPanel root = new JPanel();
root.setVisible(true);
this.setContentPane(root);
this.setLayout(null);
text1.setEditable(false);
text.setBounds(0, 400, 500, 100);//输入框
text1.setBounds(0, 0, 600, 400);//聊天记录
send.setBounds(500, 400, 100, 50);//按钮
send1.setBounds(500, 450, 100, 50);//按钮
root.add(text1);
root.add(send);
root.add(text);
root.add(send1);
text.addKeyListener(this);
putmap(name,text1);
rea.message.setFlag(2);
rea.message.setSenduser(selfname);
rea.message.setReceiveuser(name);
addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
chatopen.remove(name);//当聊天框关闭从集合移除
}
});
send.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
sendmessage(rea,text,text1,name);
}
});
text.addKeyListener(new KeyListener() {//回车响应
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_ENTER) {
sendmessage(rea,text,text1,name);
}
}
public void keyReleased(KeyEvent e) {
}
public void keyTyped(KeyEvent e) {
}
});
send1.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
//选择文件
choosefile ch=new choosefile();
File file=null;
file=ch.choose();
if(file==null)
return;
String filename=file.getName();
//选择完文件,然后发送Message过去服务端
Message a=new Message(4,selfname,name,filename,file);
try {
oOut = new ObjectOutputStream(s.getOutputStream());
oOut.writeObject(a);
} catch (IOException ex) {
ex.printStackTrace();
}
}
});
}
public void sendmessage(receive rea, JTextField text, JTextArea text1,String name){
try {
if(!text.getText().equals(""))
{
rea.message.setReceiveuser(name);
Message mes= rea.re();
//设置发送方,接收方,以及信息发过去
mes.setCharmessage(text.getText());
oOut = new ObjectOutputStream(s.getOutputStream());
oOut.writeObject(mes);
text1.append(selfname+"(你自己)说:"+text.getText()+"\r\n");
text.setText("");
}
} catch (Exception e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
}
@Override
public void keyTyped(KeyEvent e) {
// TODO Auto-generated method stub
}
@Override
public void keyPressed(KeyEvent e) {
}
@Override
public void keyReleased(KeyEvent e) {
// TODO Auto-generated method stub
}
}
class receive implements Runnable{
private JTextArea a;
private JTextField b;
private Socket s;
public Message message;
public receive(Socket s,Message message){
this.s=s;
this.message=message;
}
public Message re(){
return message;
}
public void run() {
try {
while (true) {
ObjectInputStream oIn = new ObjectInputStream(s.getInputStream());
Message save = (Message) oIn.readObject();
if (save.getFlag() == 1) {//更新好友列表
clientgui.createuserlist(save);
} else if (save.getFlag() == 2) {//接受信息
a=clientgui.map.get(save.getSenduser());
a.append(save.getCharmessage());
}else if(save.getFlag()==3){
s.close();
break;
}else if(save.getFlag()==4){
try {
fliedeverty senta=new fliedeverty();
senta.sentfile(save.getFile(),s);
a=clientgui.map.get(save.getReceiveuser());
a.append(save.getSenduser()+"(你自己)发送了文件:"+save.getFile().getName()+"\r\n");
} catch (Exception ex) {
ex.printStackTrace();
}
}
else if(save.getFlag()==5){
a=clientgui.map.get(save.getSenduser());
a.append(save.getSenduser()+"发来了文件:"+save.getFilenmane()+"\r\n");
int n = JOptionPane.showConfirmDialog(null, save.getSenduser()+"发来"+save.getFilenmane()+"是否接受?", null,JOptionPane.YES_NO_OPTION);
if(n==1){
save.setResult("拒绝接受");
ObjectOutputStream oOut = new ObjectOutputStream(s.getOutputStream());
oOut.writeObject(save);
}else{
//获得文件夹
choosefile ch=new choosefile();
File file=ch.choosedir();//选择文件夹
String filepath="";
filepath=file.getAbsolutePath();//获得路径
if(filepath.equals(""))
save.setResult("拒绝接受");
else
save.setResult("同意接受");
ObjectOutputStream oOut = new ObjectOutputStream(s.getOutputStream());
oOut.writeObject(save);
if(save.getResult().equals("同意接受")){
fliedeverty recei=new fliedeverty();
recei.receivefile(filepath,s);
}
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
实现的功能有:用户进行私聊,用户间发送文件,当有用户登陆时刷新列表,注册用户,登陆用户(需要绑定数据库)
在这里插入图片描述
登陆成功后的聊天列表如上图。
选择文件和文件夹的类:
package chat;
import javax.swing.*;
import java.io.File;
public class choosefile {
public File choose(){
JFileChooser jf=new JFileChooser();
jf.setFileSelectionMode(JFileChooser.FILES_ONLY);
jf.showDialog(new JLabel(), "选择");
int value=jf.showSaveDialog(null);
if(value==JFileChooser.CANCEL_OPTION)
return null;
File file=jf.getSelectedFile();
return file;
}
public File choosedir(){
JFileChooser jf=new JFileChooser();
jf.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
jf.showDialog(new JLabel(), "选择");
int value=jf.showSaveDialog(null);
if(value==JFileChooser.CANCEL_OPTION)
return null;
File file=jf.getSelectedFile();
return file;
}
}
socket间传输文件的类:
package chat;
import java.io.*;
import java.net.Socket;
public class fliedeverty {
public File receivefile(String filepath, Socket socket) throws IOException {
File file=null;
FileOutputStream fos = null;
DataInputStream dis=null;
try {
dis = new DataInputStream(socket.getInputStream());
// 文件名和长度
String fileName = dis.readUTF();
long fileLength = dis.readLong();
File directory = new File(filepath);
if(!directory.exists()) {
directory.mkdir();
}
file = new File(directory.getAbsolutePath() + File.separatorChar + fileName);
fos = new FileOutputStream(file);
byte[] bytes = new byte[1024];
int length = 0;
long sumlength=0;
System.out.println("======== 开始接收文件 ========");
while((length = dis.read(bytes, 0, bytes.length)) != -1) {
fos.write(bytes, 0, length);
fos.flush();
sumlength+=(long)length;
if(sumlength==fileLength)
break;
}
System.out.println("======== 文件接收成功 ========");
} catch (IOException e) {
e.printStackTrace();
}finally {
if (fos != null)
fos.close();
}
return file;
}
public void sentfile(File file,Socket recei) throws Exception {
FileInputStream fis=null;
DataOutputStream dos=null;
try {
fis = new FileInputStream(file);
dos = new DataOutputStream(recei.getOutputStream());
dos.writeUTF(file.getName());
dos.flush();
dos.writeLong(file.length());
dos.flush();
// 开始传输文件
System.out.println("======== 开始传输文件 ========");
byte[] bytes = new byte[1024];
int length = 0;
long sumlength=0;
while ((length = fis.read(bytes, 0, bytes.length)) != -1) {
dos.write(bytes, 0, length);
dos.flush();
sumlength+=(long)length;
if(file.length()==sumlength)
break;
}
System.out.println("======== 文件传输成功 ========");
} catch (IOException e) {
e.printStackTrace();
System.out.println("客户端文件传输异常");
}finally {
if(fis!=null)
fis.close();
}
}
}
并不是通过新建socket发送,而是监控文件长度,可以说发送文件的极限在于long的最大值
最后的是关键的Message类,用于监控客户端发送的是信息还是文件:
package com.kuang.pojo;
import java.io.File;
import java.net.Socket;
import java.util.List;
public class Message implements java.io.Serializable {
private String charmessage;//聊天内容
private List<String> list;//好友列表
private String senduser;//发送方
private String receiveuser;//接收方
private int flag;//1为更新好友列表,2为私发,3为关闭信息,4为发送文件,5为接受文件
private String filenmane;
private File file;
private String result;
public Message(int flag,String senduser,String receiveuser,String filenmane,File file){
this.flag=flag;
this.receiveuser=receiveuser;
this.filenmane=filenmane;
this.senduser=senduser;
this.file=file;
}
public Message(int flag){
this.flag=flag;
}
public Message(int flag,List<String> list){
this.list=list;
this.flag=flag;
}
user类:
package com.kuang.pojo;
import java.io.Serializable;
public class User implements Serializable {
private int id; //id
private String name; //姓名
private String pwd; //密码
public int flag;//0为注册,1为登陆
public User() {
}
public User(int id, String name, String pwd) {
this.id = id;
this.name = name;
this.pwd = pwd;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPwd() {
return pwd;
}
public void setPwd(String pwd) {
this.pwd = pwd;
}
public int getFlag() {
return flag;
}
public void setFlag(int flag) {
this.flag = flag;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
", pwd='" + pwd + '\'' +
", flag=" + flag +
'}';
}
}
基本上功能都完善了,但是存在一个致命问题,当用户给其他用户发送信息时,若另一个用户未打开对话框会报错。