赞
踩
套接字及客户服务器应用程序基础
本次实验要求自己动手实现一个能够在局域网中进行点对点聊天的实用程序。
参考实验《套接字及客户/服务器应用程序基础》,Winsock编程基础。
public class SocketServer extends JFrame
该类继承自JFrame,为Socket服务器的主类。
类的成员变量
private JPanel contentPane;
private JTextArea centerTextArea;
private List<Client> onlineList =new ArrayList<Client>();
SimpleDateFormat dateFormat= new SimpleDateFormat("yyyy/MM/dd hh:mm:ss");
Date date;
其中contentPane、centerTextArea为GUI组件。该页面仅包含一个TextArea,用于显示聊天信息。值得一提的是date、dateFormat用于输出每条消息的时间,以“年/月/日 时:分:秒”的格式给出。
主函数
public static void main(String[] args) {
try {
SocketServer frame = new SocketServer();
ServerSocket serverSocket = new ServerSocket(5566);
frame.setVisible(true);
while(true) {
Socket socket = serverSocket.accept();
frame.getClient(socket);
}
} catch (Exception e) {
e.printStackTrace();
}
}
主函数的设计思路是,创建frame对象,在其函数内设置其具体布局;创建一个ServerSocket,并绑定端口号为5566。注意若该端口号被占用,则服务器不能使用,更改端口号目前仅能通过修改代码实现。之后,在一个while(true)循环内,利用Socket服务器的accept()方法获取客户端Socket对象,每获取到一个Socket对象,调用getClient()函数处理。
构造器
构造器主要用于实现布局,代码如下:
public SocketServer() { setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setBounds(100, 100, 400, 400); setResizable(false);//窗口大小不可调整 setTitle("Server"); setLocationRelativeTo(null);//窗口居中 contentPane = new JPanel(); contentPane.setBorder(new EmptyBorder(5, 5, 5, 5)); contentPane.setLayout(new BorderLayout(0, 0)); setContentPane(contentPane); centerTextArea=new JTextArea(); centerTextArea.setEditable(false); centerTextArea.setBackground(new Color(247, 247, 247)); add(new JScrollPane(centerTextArea),BorderLayout.CENTER); Date date = new Date(); SimpleDateFormat dateFormat= new SimpleDateFormat("yyyy/MM/dd hh:mm:ss"); centerTextArea.append(dateFormat.format(date)+"\n"); centerTextArea.append("服务器已运行\n\n"); centerTextArea.setCaretPosition(centerTextArea.getText().length()); }
注意到一些细节,比如设置窗口大小不可以调整、设置窗口居中、设置文本区域不可编辑、为体现出不可编辑使用浅灰色表示。值得一提的是,在这里我使用了setCaretPosition()方法控制滚动条,使每次文本区域更新后,滚动条都能在JTextArea最底端。
客户端Socket对象的处理
由getClient()函数实现,代码为:
private void getClient(Socket socket) { try { BufferedReader in = new BufferedReader(new InputStreamReader( socket.getInputStream())); String firstInfo=in.readLine(); date = new Date(); centerTextArea.append(dateFormat.format(date)+"\n"); centerTextArea.append(firstInfo+"\n\n"); centerTextArea.setCaretPosition(centerTextArea.getText().length()); Client client = new Client(firstInfo,socket); onlineList.add(client); Thread thread =new Thread(client); //创建客户端处理线程 thread.start();//启动线程 } catch (IOException e) { e.printStackTrace(); } }
首先初始化输入流并从网络缓冲区读取客户端传输的数据,接着在服务器端初始化一个Client对象,并将其添加到客户端对象队列中用于记录当前在线用户,然后为每个客户端创建一个线程,并开启该线程。
内部类Client
该类用于处理客户端对象,实现了Runnable接口。服务器相当于提供了点到点的中转功能,具体通过遍历当前在线列表中的Client对象实现。
class Client implements Runnable { String name; //客户端名字 Socket socket = null;//客户端Socket对象 BufferedReader in; PrintWriter out; Client(String name,Socket socket) { this.socket = socket; this.name=name; try { in = new BufferedReader(new InputStreamReader( socket.getInputStream())); out = new PrintWriter(socket.getOutputStream()); } catch (IOException e) { e.printStackTrace(); } } public void run(){ try { while (true) { String str = in.readLine(); if(str.split(":")[1].charAt(0)!='(') { //点对点中转 for(Client client: onlineList) { if(client!=this) { client.send(str); } } } else { String temp1=str.split("\\(")[1]; String temp2=temp1.split("\\)")[0]; //点对点中转 for(Client client: onlineList) { if(client!=this && client.name.equals(temp2)) { client.send(str); } } } date = new Date(); centerTextArea.append(dateFormat.format(date)+"\n"); centerTextArea.append(str+"\n\n"); centerTextArea.setCaretPosition(centerTextArea.getText().length()); if (str.equals("keyToEndTheRoom")) break; } try { socket.close(); } catch (IOException e) { e.printStackTrace(); } } catch (IOException e1) { e1.printStackTrace(); } }
2.2 客户端
类设计
public class SocketClient extends JFrame
该类继承自JFrame,为Socket客户端的主类。
类的成员变量
private JPanel contentPane;
private JLabel state;
private JTextArea textArea,inputArea;
JScrollPane textJScrollPane,inputJScrollPane;
private JButton send,clear;
private String name,ip;
private Socket server;
private BufferedReader in;
private PrintWriter out;
private SimpleDateFormat dateFormat= new SimpleDateFormat("yyyy/MM/dd hh:mm:ss");
private Date date;
private Thread client=new Thread(new Listener());
其中contentPane、state、textArea、inputArea等为GUI组件。该页面包含一个textArea用于显示聊天信息,并有一个inputArea用于输入信息。用于发送消息的按钮与用于值得一提的是date、dateFormat用于输出每条消息的时间,以“年/月/日 时:分:秒”的格式给出。
主函数
public static void main(String[] args) {
try {
SocketClient frame = new SocketClient();
frame.setVisible(true);
frame.start();
} catch (Exception e) {
e.printStackTrace();
}
}
主函数的设计思路是,创建frame对象,之后直接调用start()函数创建Socket对象并开启客户端线程,与服务器进行连接。
构造器
public SocketClient() { setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setBounds(100, 100, 400, 400); setResizable(false);//窗口大小不可调整 setTitle("Client"); setLocationRelativeTo(null);//窗口居中 contentPane = new JPanel(); contentPane.setBorder(new EmptyBorder(5, 5, 5, 5)); setContentPane(contentPane); contentPane.setLayout(null); clear = new JButton("清屏"); clear.setBounds(56, 316, 123, 29); contentPane.add(clear); send = new JButton("发送"); send.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent arg0) { } }); send.setBounds(217, 316, 123, 29); contentPane.add(send); inputArea = new JTextArea(); inputArea.setBounds(15, 231, 364, 70); //contentPane.add(inputArea); inputJScrollPane=new JScrollPane(inputArea); inputJScrollPane.setBounds(15, 231, 364, 70); contentPane.add(inputJScrollPane); textArea = new JTextArea(); textArea.setEditable(false); textArea.setBounds(15, 37, 364, 179); textArea.setBackground(new Color(247, 247, 247)); //contentPane.add(textArea); textJScrollPane=new JScrollPane(textArea); textJScrollPane.setBounds(15, 37, 364, 179); textJScrollPane.setBackground(new Color(247, 247, 247)); contentPane.add(textJScrollPane); name=JOptionPane.showInputDialog(this,"请输入您的昵称",JOptionPane.QUESTION_MESSAGE); ip=JOptionPane.showInputDialog(this,"请输入服务器IP地址",JOptionPane.QUESTION_MESSAGE); state=new JLabel("欢迎您,"+name); state.setBounds(5, 5, 384, 21); state.setHorizontalAlignment(JLabel.LEFT);//居右 getContentPane().add(state); send.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { String str=name+":"+inputArea.getText();//封装 date = new Date(); textArea.append(dateFormat.format(date)+"\n"); textArea.append(str+"\n\n"); textArea.setCaretPosition(textArea.getText().length()); inputArea.setText(""); try { out.println(str); out.flush(); if (str.split(":")[1].equals("keyToEndTheRoom")) { server.close(); } } catch (Exception e2) { JOptionPane.showMessageDialog(SocketClient.this, "发送失败"); e2.printStackTrace(); } } }); clear.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { textArea.setText(""); } }); }
构造器主要用于布局,同时提示用户输入服务器的IP地址与聊天昵称。同时send与clear两个按钮需要绑定动作,前者对应发送消息并清空输入框,后者对应清空聊天显示区域。
创建Socket
start()函数用于创建Socket对象,代码如下:
private void start() throws UnknownHostException, IOException { server = new Socket(ip, 5566); in=new BufferedReader(new InputStreamReader(server.getInputStream())); out=new PrintWriter(server.getOutputStream()); client.start(); date = new Date(); textArea.append(dateFormat.format(date)+"\n"); textArea.append(name+"已进入聊天室\n\n"); textArea.setCaretPosition(textArea.getText().length()); try { out.println(name+"已进入聊天室"); out.flush(); } catch (Exception e2) { JOptionPane.showMessageDialog(SocketClient.this, "发送失败"); e2.printStackTrace(); } }
客户端创建一个Socket对象绑定ip地址和服务器监听的端口号,这里即5566端口,之后初始化网络输入输出流。Socket对象创建成功之后,在聊天区域内显示该用户已进入聊天室,并将这条消息发送给服务器端。
消息监听
客户端需要监听来自服务器的socket消息:
class Listener implements Runnable{
@Override
public void run() {
try {
while(server!=null)
{
String str =in.readLine();
date = new Date();
textArea.append(dateFormat.format(date)+"\n");
textArea.append(str+"\n\n");
textArea.setCaretPosition(textArea.getText().length());
}
} catch (Exception e) {}
}
}
这里实现了一个Runnable接口,每接收到一条消息,将其打印到聊天区域。 需要注意的细节是时间的处理。
提示服务器已运行与当前时间。之后运行客户端,我们首先运行一个客户端。首先输入昵称。
之后输入服务器IP地址。
登陆成功,当前在线的所有用户(包括自己)将收到“qiao已进入聊天室”消息。
尝试发送消息:
若消息过多,则会自动出现滚轮,且滚轮会自动定位在文字最底端。
之后,如果其他用户登录,当前在线的所有用户将收到登陆提示:
若用户选择私聊,则输入消息为(发送对象名)消息,如下所示:
观察到A发送给B一条消息“hi”,仅B能收到、服务器端显示,当前在线的C不能收到。
完整代码下载地址:
https://download.csdn.net/download/qq_41112170/76748059
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。