已经把UDP和TCP分成了两个线程,但是不能运行了,报错也看不出什么毛病,能不能帮忙看看什么毛病呢TOT
server
```java
package chat;
import javax.swing.*;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.*;
import java.util.ArrayList;
class server extends JFrame implements Runnable, ListSelectionListener, ActionListener {
Socket s = null;
private ServerSocket ss = null;
ArrayList<ChatThread> users = new ArrayList<ChatThread>(); //容量能够动态增长的数组
DefaultListModel<String> dl = new DefaultListModel<String>();
JList<String> userList = new JList<String>(dl);//显示对象列表并且允许用户选择一个或多个项的组件。单独的模型 ListModel 维护列表的内容。
JPanel jpl = new JPanel();
JButton jbt = new JButton("踢出");
JTextArea jta = new JTextArea(10,20);
JScrollPane js = new JScrollPane(jta);
JPanel center = new JPanel();
JPanel jp=new JPanel();
JButton jbt1 = new JButton("群发");
JButton jbt2 = new JButton("组播1");
JButton jbt3 = new JButton("组播2");
JPanel west = new JPanel();
//群发消息输入栏
JTextField jtf = new JTextField();
public server() throws Exception{
this.setTitle("服务端");
// this.add(userList, "North");//放在北面
this.add(jpl, "South");
Dimension dim = new Dimension(100,150);
west.setPreferredSize(dim);//在使用了布局管理器后用setPreferredSize来设置窗口大小
BorderLayout bl2 = new BorderLayout();
west.setLayout(bl2);
west.add(userList,BorderLayout.CENTER);//显示好友列表
add(west,BorderLayout.EAST);
userList.setFont(new Font("宋体",Font.BOLD,18));
//仅将群发消息输入栏设为一栏
jtf.setColumns(1);
jpl.setLayout(new BorderLayout());
jp.add(jbt1);//群发消息
jp.add(jbt2);//组播1
jp.add(jbt3);//组播2
jpl.add(jtf, BorderLayout.NORTH);
jpl.add(jbt,BorderLayout.EAST);//踢出聊天室
jpl.add(jp,BorderLayout.WEST);
//实现群发
jbt1.addActionListener(this);
//实现踢人
jbt.addActionListener(this);
//组播1
jbt2.addActionListener(this);
//组播2
jbt3.addActionListener(this);
//center 聊天消息框 发送消息操作面板
jta.setEditable(false);//消息显示框是不能编辑的
BorderLayout bl3 = new BorderLayout();
center.setLayout(bl3);
center.add(js,BorderLayout.CENTER);//js是消息展示框JScrollPane
add(center,BorderLayout.CENTER);
js.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);//需要时才显示滚动条
this.setDefaultCloseOperation(EXIT_ON_CLOSE);
this.setLocation(400,100);
this.setSize(500, 400);
this.setVisible(true);
this.setAlwaysOnTop(true);
ss = new ServerSocket(3000); //侦听端口号
new Thread(this).start();//监听用户端的加入
}
@Override
public void run() {
while(true){
try{
s = ss.accept();
ChatThread ct = new ChatThread(s); //为该客户开一个线程
users.add(ct); //将每个线程加入到users
//发送Jlist里的用户登陆信息,为了防止后面登陆的用户无法更新有前面用户的好友列表
ListModel<String> model = userList.getModel();//获取Jlist的数据内容
for(int i = 0; i < model.getSize(); i++){
ct.ps.println("USERS#" + model.getElementAt(i));
}
ct.start();
}catch (Exception ex){
ex.printStackTrace();
javax.swing.JOptionPane.showMessageDialog(this,"服务器异常!");
System.exit(0);
}
}
}
//群发消息按钮点击事件监听
@Override
public void actionPerformed(ActionEvent e) {
String label = e.getActionCommand();
if(label.equals("群发")){
handleAll();
}else if(label.equals("组播1")){
handleBroadcast1();
}else if(label.equals("组播2")){
handleBroadcast2();
}else if(label.equals("踢出")){
try {
handleExpel();
} catch (IOException e1) {
e1.printStackTrace();
}
}
}
public void handleAll(){
if(!jtf.getText().equals("")){
sendMessage("ALL#" + jtf.getText());
//发送完后,是输入框中内容为空
System.out.print("ALL#" + jtf.getText());
jtf.setText("");
}
}//群发消息
public void handleBroadcast1(){
sendMessage("Broadcast");
if(!jtf.getText().equals("")){
try {
InetAddress group = InetAddress.getByName("224.0.0.1");
int port = 3000;
MulticastSocket socket = new MulticastSocket();
socket.setTimeToLive(99);
String message = "GR1#" +jtf.getText();
byte[] buffer = message.getBytes();
DatagramPacket packet = new DatagramPacket(buffer, buffer.length, group, port);
socket.send(packet);
System.out.print(message);
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
//发送完后,输入框中内容为空
jtf.setText("");
}
}//群发消息
public void handleBroadcast2(){
sendMessage("Broadcast#");
if(!jtf.getText().equals("")){
try {
InetAddress group = InetAddress.getByName("224.0.0.1");
int port = 3000;
MulticastSocket socket = new MulticastSocket();
socket.setTimeToLive(99);
String message ="GR2#" + jtf.getText();
byte[] buffer = message.getBytes();
DatagramPacket packet = new DatagramPacket(buffer, buffer.length, group, port);
socket.send(packet);
System.out.print(message);
//socket.close();
} catch (IOException e) {
e.printStackTrace();
}
//发送完后,输入框中内容为空
jtf.setText("");
}
}//群发消息
public void handleExpel() throws IOException {
sendMessage("OFFLINE#" + userList.getSelectedValuesList().get(0));
dl.removeElement(userList.getSelectedValuesList().get(0));//更新defaultModel
userList.repaint();//更新Jlist
}//踢人
public class ChatThread extends Thread{
Socket s = null;
private BufferedReader br = null;
private PrintStream ps = null;
public boolean canRun = true;
String nickName = null;
public ChatThread(Socket s) throws Exception{
this.s = s;
br = new BufferedReader(new InputStreamReader(s.getInputStream()));
ps = new PrintStream(s.getOutputStream());
}
public void run(){
while(canRun){
try{
String msg = br.readLine();//接收客户端发来的消息
String[] strs = msg.split("#");
if(strs[0].equals("LOGIN")){//收到来自客户端的上线消息
nickName = strs[1];
dl.addElement(nickName);
userList.repaint();
//System.out.print(msg);
jta.append(strs[1] + "已上线\n");
sendMessage(msg);
}else if(strs[0].equals("MSG") ){
jta.append(strs[1] + "说:" + strs[2] + "\n");
//System.out.print(msg);
sendMessage(msg);
}else if(strs[0].equals("SMSG") || strs[0].equals("FSMSG")) {
//System.out.print(msg);
sendMessage(msg);
}else if(strs[0].equals("OFFLINE")){//收到来自客户端的下线消息
//System.out.print(msg);
jta.append(strs[1] + "已下线\n");
sendMessage(msg);
//System.out.println(msg);
dl.removeElement(strs[1]);
// 更新List列表
userList.repaint();
}
}catch (Exception ex){}
}
}
}
public void sendMessage(String msg){ //服务器端发送给所有用户
for(ChatThread ct : users){
ct.ps.println(msg);
}
}
@Override
public void valueChanged(ListSelectionEvent e) {
// TODO 自动生成的方法存根
}
public static void main(String[] args) throws Exception{
new server();
}
}
client
package chat;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.DatagramPacket;
import java.net.InetSocketAddress;
import java.net.MulticastSocket;
import java.net.Socket;
public class client extends JFrame implements ActionListener {
JPanel north = new JPanel();
//west
JPanel west = new JPanel();
DefaultListModel<String> dl = new DefaultListModel<String>();//用来修改JList
JList<String> userList = new JList<String>(dl);//用来展示和选择
JScrollPane listPane = new JScrollPane(userList);
//center
JPanel center = new JPanel();
JTextArea jta = new JTextArea(10,20);
JScrollPane js = new JScrollPane(jta);
JPanel operPane = new JPanel();//发送消息的操作面板
JLabel input = new JLabel("请输入:");
JTextField jtf = new JTextField(24);
JButton jButton = new JButton("发消息");
JButton jbt = new JButton("发送");
JButton jbt1 = new JButton("私聊");
/*private*/ BufferedReader br = null;
/*private*/ PrintStream ps = null;
/*private*/ String nickName = null;
//私聊面板
JTextArea jTextArea = new JTextArea(11,45);
JScrollPane js1 = new JScrollPane(jTextArea);
JTextField jTextField = new JTextField(25);
String suser = new String();
double MAIN_FRAME_LOC_X;//父窗口x坐标
double MAIN_FRAME_LOC_Y;//父窗口y坐标
boolean FirstSecret = true;//是否第一次私聊
String sender=null;//私聊发送者的名字
String receiver=null;//私聊接收者的名字
public client() throws Exception{
BorderLayout bl = new BorderLayout();
north.setLayout(bl);
add(north,BorderLayout.NORTH);
//east 好友列表
Dimension dim = new Dimension(100,150);
west.setPreferredSize(dim);//在使用了布局管理器后用setPreferredSize来设置窗口大小
BorderLayout bl2 = new BorderLayout();
west.setLayout(bl2);
west.add(listPane,BorderLayout.CENTER);//显示好友列表
add(west,BorderLayout.EAST);
userList.setFont(new Font("宋体",Font.BOLD,18));
//center 聊天消息框 发送消息操作面板
jta.setEditable(false);//消息显示框是不能编辑的
jTextArea.setEditable(false);
BorderLayout bl3 = new BorderLayout();
center.setLayout(bl3);
FlowLayout fl = new FlowLayout(FlowLayout.LEFT);
operPane.setLayout(fl);
operPane.add(input);
operPane.add(jtf);
operPane.add(jbt);
operPane.add(jbt1);
center.add(js,BorderLayout.CENTER);//js是消息展示框JScrollPane
center.add(operPane,BorderLayout.SOUTH);
add(center,BorderLayout.CENTER);
js.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);//需要时才显示滚动条
//鼠标事件,点击
jbt.addActionListener(this);
jbt1.addActionListener(this);
this.setDefaultCloseOperation(EXIT_ON_CLOSE);
//this.setAlwaysOnTop(true);
nickName = JOptionPane.showInputDialog("请输入用户名:");
this.setTitle("用户: "+nickName);
this.setSize(700,400);
this.setVisible(true);
Thread t = new Thread(new chatroom());
// 启动线程
t.start();
Thread g = new Thread(new Broadcast());
// 启动线程
g.start();
ps.println("LOGIN#" + nickName);//发送登录信息,消息格式:LOGIN#nickName
/*jtf.setFocusable(true);//设置焦点
//键盘事件,实现当输完要发送的内容后,直接按回车键,实现发送
//监听键盘相应的控件必须是获得焦点(focus)的情况下才能起作用
jtf.addKeyListener(new KeyAdapter() {
@Override
public void keyPressed(KeyEvent e) {
if(e.getKeyCode() == KeyEvent.VK_ENTER) {
ps.println("MSG#" + nickName + "#" + jtf.getText());//发送消息的格式:MSG#nickName#message
//发送完后,是输入框中内容为空
jtf.setText("");
}
}
});
//私聊消息框按回车发送消息
jTextField.addKeyListener(new KeyAdapter() {
@Override
public void keyPressed(KeyEvent e) {
if(e.getKeyCode() == KeyEvent.VK_ENTER) {
handleSS();
}
}
});
*/
//监听系统关闭事件,退出时给服务器端发出指定消息
this.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
ps.println("OFFLINE#" + nickName);//发送下线信息,消息格式:OFFLINE#nickName
}
});
this.addComponentListener(new ComponentAdapter() {//监听父窗口大小的改变
public void componentMoved(ComponentEvent e) {
Component comp = e.getComponent();
MAIN_FRAME_LOC_X = comp.getX();
MAIN_FRAME_LOC_Y = comp.getY();
}
});
}
@Override
public void actionPerformed(ActionEvent e) {//鼠标点击事件
String label = e.getActionCommand();
if(label.equals("发送")){//群发
handleSend();
}else if(label.equals("私聊") && !userList.isSelectionEmpty()){//未点击用户不执行
suser = userList.getSelectedValuesList().get(0);//获得被选择的用户
handleSec(suser);//创建私聊窗口
sender = nickName;
receiver = suser;
}else if(label.equals("发消息")){
handleSS();//私发消息
}else{
System.out.println("不识别的事件");
}
}
public void handleSS(){//在私聊窗口中发消息
String name=sender;
if(sender.equals(nickName)) {
name = receiver;
}
if(FirstSecret) {
ps.println("FSMSG#" + nickName + "#" + name + "#" + jTextField.getText());
jTextField.setText("");
FirstSecret = false;
}
else {
ps.println("SMSG#" + nickName + "#" + name + "#" + jTextField.getText());
jTextField.setText("");
}
}
public void handleSend(){//群发消息
//发送信息时标识一下来源
ps.println("MSG#" + nickName + "#" + jtf.getText());
//发送完后,使输入框中内容为空
jtf.setText("");
}
public void handleSec(String name){ //建立私聊窗口
JFrame jFrame = new JFrame();//新建了一个窗口
JPanel JPL = new JPanel();
JPanel JPL2 = new JPanel();
FlowLayout f2 = new FlowLayout(FlowLayout.LEFT);
JPL.setLayout(f2);
JPL.add(jTextField);
JPL.add(jButton);
JPL2.add(js1,BorderLayout.CENTER);
JPL2.add(JPL,BorderLayout.SOUTH);
jFrame.add(JPL2);
jButton.addActionListener(this);
jTextArea.setFont(new Font("宋体", Font.PLAIN,15));
jFrame.setSize(400,310);
jFrame.setLocation((int)MAIN_FRAME_LOC_X+20,(int)MAIN_FRAME_LOC_Y+20);//将私聊窗口设置总是在父窗口的中间弹出
jFrame.setTitle("与" + name + "私聊中");
jFrame.setVisible(true);
jTextField.setFocusable(true);//设置焦点
jFrame.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
jTextArea.setText("");
FirstSecret = true;
}
});
}//私聊窗口
public static void main(String[] args)throws Exception{
new client();
}
class Broadcast implements Runnable{
@Override
public void run() {
while (true) {
try {
MulticastSocket socket = new MulticastSocket(9999);
InetSocketAddress group = new InetSocketAddress("224.0.1.1", 9999);
socket.joinGroup(group, null);
byte[] buffer = new byte[1024];
DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
socket.receive(packet);
String message = new String(packet.getData(), 0, packet.getLength());
String[] strs = message.split("#");
if (strs[0].equals("GR1")) {
jta.append("群组1管理员说" + strs[1] + "\n");
}
socket.leaveGroup(group, null);
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
class chatroom implements Runnable{
@Override
public void run () {//客户端与服务器端发消息的线程
while (true) {
try {
Socket s = new Socket("127.0.0.1", 3000);
br = new BufferedReader(new InputStreamReader(s.getInputStream()));
ps = new PrintStream(s.getOutputStream());
new Thread(this).start();//run()
String msg = br.readLine();//读取服务器是否发送了消息给该客户端
String[] strs = msg.split("#");
//System.out.print(msg+" msg");
//判断是否为服务器发来的登陆信息
if (strs[0].equals("LOGIN")) {
if (!strs[1].equals(nickName)) {//不是本人的上线消息就显示,本人的不显示
jta.append(strs[1] + "已上线\n");
dl.addElement(strs[1]);//DefaultListModel来更改JList的内容
userList.repaint();
}
} else if (strs[0].equals("MSG")) {//接到服务器发送消息的信息
if (!strs[1].equals(nickName)) {//别人说的
jta.append(strs[1] + "说:" + strs[2] + "\n");
} else {
jta.append("我说:" + strs[2] + "\n");
}
} else if (strs[0].equals("USERS")) {//USER消息,为新建立的客户端更新好友列表
dl.addElement(strs[1]);
userList.repaint();
} else if (strs[0].equals("ALL")) {
jta.append("系统消息:" + strs[1] + "\n");
} else if (strs[0].equals("OFFLINE")) {
if (strs[1].equals(nickName)) {//如果是自己下线的消息,说明被服务器端踢出聊天室,强制下线
javax.swing.JOptionPane.showMessageDialog(null, "您已被系统请出聊天室!");
System.exit(0);
}
jta.append(strs[1] + "已下线\n");
dl.removeElement(strs[1]);
userList.repaint();
} else if ((strs[2].equals(nickName) || strs[1].equals(nickName)) && strs[0].equals("SMSG")) {
if (!strs[1].equals(nickName)) {
jTextArea.append(strs[1] + "说:" + strs[3] + "\n");
jta.append("系统提示:" + strs[1] + "私信了你" + "\n");
} else {
jTextArea.append("我说:" + strs[3] + "\n");
}
} else if ((strs[2].equals(nickName) || strs[1].equals(nickName)) && strs[0].equals("FSMSG")) {
sender = strs[1];
receiver = strs[2];
//接收方收到私聊消息,自动弹出私聊窗口
if (!strs[1].equals(nickName)) {
FirstSecret = false;
jTextArea.append(strs[1] + "说:" + strs[3] + "\n");
jta.append("系统提示:" + strs[1] + "私信了你" + "\n");
handleSec(strs[1]);
} else {
jTextArea.append("我说:" + strs[3] + "\n");
}
}
} catch (Exception ex) {//如果服务器端出现问题,则客户端强制下线
javax.swing.JOptionPane.showMessageDialog(null, "您已被系统请出聊天室!");
System.exit(0);
}
}
}
}
}
```
服务器程序将一个套接字绑定到一个特定的端口,并通过此套接字等待和监听客户端到端口的连接请求。
package server;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
public class Server {
public static void main(String[] args) {
try {
//声明服务器的端口号
ServerSocket serverSocket = new ServerSocket(1111);
//等待接收一个socket
Socket socket = serverSocket.accept();
//根据socket获得输入流
InputStream inputStream = socket.getInputStream();
//创建输出流
FileOutputStream fileOutputStream = new FileOutputStream("D:\\1.png");
byte[] car = new byte[1024];
int length = 0;
//将接收的输入流写入输出流,即写入本地文件中
while((length = inputStream.read(car)) != -1) {
fileOutputStream.write(car);
}
//刷新缓冲区
fileOutputStream.flush();
//关闭资源
fileOutputStream.close();
inputStream.close();
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
客户端程序根据服务器程序所在的主机名和端口发出连接请求。
package client;
import java.io.FileInputStream;
import java.io.OutputStream;
import java.net.Socket;
public class Client {
public static void main(String[] args) {
try {
//创建套接字对象socket并封装ip与port
Socket socket = new Socket("127.0.0.1", 1111);
//根据创建的socket对象获得一个输出流
OutputStream outputStream = socket.getOutputStream();
//创建输入流,即要从本地发送给服务器的文件输入流
FileInputStream fileInputStream = new FileInputStream("D:\\icon.png");
byte[] car = new byte[1024];
int length = 0;
//将本地文件以IO流的方法发送给服务器
while((length = fileInputStream.read(car)) != -1) {
outputStream.write(car, 0, length);
}
//刷新缓冲区
outputStream.flush();
//关闭资源
fileInputStream.close();
fileInputStream.close();
socket.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
测试:
报错信息呢