TCP一对一通信:
实现服务端对话框:
其中可自由更改对话框的样式
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.net.*;
public class QqMain extends JFrame implements ActionListener{
public static void main(String[] args){
InetAddress ia = null;
try {
ia = ia.getLocalHost();
String localip = ia.getHostAddress();
System.out.println("本机的ip是 :" + localip);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
new QqMain();
}
// 说明:一个类需要页面的显示,则那个类要继承JFrame。
// 属性
// 文本域
private JTextArea jta;
// 滚动条
private JScrollPane jsp;
// 面板里面是文本框和按钮
private JPanel jp;
private JTextField jtf;
private JButton jb ;
BufferedWriter bw = null;
// 构造器
public QqMain(){
// 初始化上面的属性
jta = new JTextArea();
// 将文本域添加到滚动条中
jsp = new JScrollPane(jta);
jp = new JPanel();
jtf =new JTextField(15);
jb = new JButton("发送");
// 把按钮和文本框添加到面板中
jp.add(jtf);
jp.add(jb);
// 把滚动条和面板添加到JFrame中去
this.add(jsp,BorderLayout.CENTER); //这个设置在中间
this.add(jp,BorderLayout.SOUTH); //南
this.setTitle("qq聊天");
this.setSize(500,500);
this.setLocation(200, 200);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setVisible(true);
/***********TCP协议*************/
jb.addActionListener(this); // 这是按钮点击使用
// 回车键的监听事件 在接口KeyListener中
//jtf.addKeyListener(this);
jtf.addKeyListener(new KeyAdapter() {
public void keyTyped(KeyEvent e) {
if((char)e.getKeyChar()==KeyEvent.VK_ENTER) {
useVoid();
}
}
});
try{
// 1.创建一个服务端的套接字
ServerSocket serverSocket = new ServerSocket(8888);
//2.等待客户端的连接
Socket socket = serverSocket.accept();
// 3.获取socket通道的输入流(输入流的读取方式为一行一行的读取方式 ----> readLine())
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
// 4.获取通道的输入流(也是一行一行的写出 BufferedWriter ->newLine())
// 当用户点击“发送”按钮的时候才会,写出数据
bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
String line = null;
while((line = br.readLine()) !=null){
// 将读取的数据拼接到文本域中显示
jta.append(line + "\n");
}
// 5.关闭socket通道
serverSocket.close();
}catch(IOException e){
e.printStackTrace();
}
/************************/
}
// 点击按钮所实现的方法
public void actionPerformed(ActionEvent e){
useVoid();
}
public void useVoid(){
// 1.获取文本框中的内容
String text = jtf.getText();
text = "服务端对客户端说:" + text;
// 自己显示
jta.append(text + "\n");
// 2.发送
try{
// 4.发送
bw.write(text);
bw.newLine(); // 换行
bw.flush(); // 刷新
// 5.清空文本框
jtf.setText("");
}catch (IOException e1){
e1.printStackTrace();
}
}
/*public void KeyPressed(KeyEvent e){
//回车键
System.out.println("按钮数字");
}
public void KeyTyped(KeyEvent e){
}
public void KeyReleased(KeyEvent e){
}*/
//行为
}
实现客户端的对话框来获取服务器端的ip地址和端口号进行链接:
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.net.*;
public class QqFu extends JFrame implements ActionListener{
public static void main(String[] args){
new QqFu();
}
// 说明:一个类需要页面的显示,则那个类要继承JFrame。
// 属性
// 文本域
private JTextArea jta;
// 滚动条
private JScrollPane jsp;
// 面板里面是文本框和按钮
private JPanel jp;
private JTextField jtf;
private JButton jb ;
BufferedWriter bw = null;
// 构造器
public QqFu(){
// 初始化上面的属性
jta = new JTextArea();
// 将文本域添加到滚动条中
jsp = new JScrollPane(jta);
jp = new JPanel();
jtf =new JTextField(15);
jb = new JButton("发送");
// 把按钮和文本框添加到面板中
jp.add(jtf);
jp.add(jb);
// 把滚动条和面板添加到JFrame中去
this.add(jsp,BorderLayout.CENTER); //这个设置在中间
this.add(jp,BorderLayout.SOUTH); //南
this.setTitle("qq聊天客户端"); //获取用户的昵称
this.setSize(500,500);
this.setLocation(200, 200);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setVisible(true);
jb.addActionListener(this);
// 回车点击事件
jtf.addKeyListener(new KeyAdapter() {
public void keyTyped(KeyEvent e) {
if((char)e.getKeyChar()==KeyEvent.VK_ENTER) {
useVoid01();
}
}
});
try{
/*******客户端 TCP协议*********/
// 1.创建一个客户端的套接字(尝试连接)
Socket socket = new Socket("127.0.0.1",8888);
// 2.获取socket通道的输入流
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
// 3
bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
String line = null;
while((line = br.readLine()) !=null){
jta.append(line + "\n");
}
// 3. 获取输出流
// 4.关闭流
socket.close();
/******************************/
}catch(Exception e){
e.printStackTrace();
}
}
public void actionPerformed(ActionEvent e){
useVoid01();
}
public void useVoid01(){
// 1.获取文本框中需要发送的内容
String text = jtf.getText();
// 2. 拼接内容
text = "客户端对服务端说:" + text;
// 3.自己显示
jta.append(text + "\n");
try{
// 4.发送
bw.write(text);
bw.newLine(); // 换行
bw.flush(); // 刷新
// 5.清空
jtf.setText("");
}catch(IOException e1){
e1.printStackTrace();
}
}
//行为
}
必须先启动服务端再启动客户端才可,如果是两台电脑的情况下,只需要获得其中一个电脑的ip进行服务器启动,另一个进行链接即可。就可实现实时对话
效果展示:
UDP群聊服务器端:
import java.io.*;
import java.net.*;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
import javax.swing.*;
import java.awt.*;
public class UDPServer extends JFrame{
private JTextArea m_display=new JTextArea();
private ServerSocket serverSocket;
/**
* 创建线程池来管理客户端的连接线程
* 避免系统资源过度浪费
*/
private ExecutorService exec;
// 存放客户端之间私聊的信息
private Map<String,PrintWriter> storeInfo;
public UDPServer() {
super("聊天程序服务器端");
Container c=getContentPane();
c.add(new JScrollPane(m_display),BorderLayout.CENTER);
try {
serverSocket = new ServerSocket(6666);
storeInfo = new HashMap<String, PrintWriter>();
exec = Executors.newCachedThreadPool();
} catch (Exception e) {
e.printStackTrace();
}
}
// 将客户端的信息以Map形式存入集合中
private void putIn(String key,PrintWriter value) {
synchronized(this) {
storeInfo.put(key, value);
}
}
// 将给定的输出流从共享集合中删除
private synchronized void remove(String key) {
storeInfo.remove(key);
m_display.append("当前在线人数为:"+ storeInfo.size());
//for(String name: storeInfo.key)
}
// 将给定的消息转发给所有客户端
private synchronized void sendToAll(String message) {
for(PrintWriter out: storeInfo.values()) {
out.println(message);
// m_display.append("已经发送了");
}
}
// 将给定的消息转发给私聊的客户端
private synchronized void sendToSomeone(String name,String message) {
PrintWriter pw = storeInfo.get(name); //将对应客户端的聊天信息取出作为私聊内容发送出去
if(pw != null) pw.println("私聊: "+message);
}
public void start() {
try {
m_display.setVisible(true);
//m_display.append("mayanshuo");
while(true) {
m_display.append("等待客户端连接... ... \n");
Socket socket = serverSocket.accept();
// 获取客户端的ip地址
InetAddress address = socket.getInetAddress();
m_display.append("客户端:“" + address.getHostAddress() + "”连接成功! ");
/*
* 启动一个线程,由线程来处理客户端的请求,这样可以再次监听
* 下一个客户端的连接
*/
exec.execute(new ListenrClient(socket)); //通过线程池来分配线程
}
} catch(Exception e) {
e.printStackTrace();
}
}
/**
* 该线程体用来处理给定的某一个客户端的消息,循环接收客户端发送
* 的每一个字符串,并输出到控制台
*/
class ListenrClient implements Runnable {
private Socket socket;
private String name;
public ListenrClient(Socket socket) {
this.socket = socket;
}
// 创建内部类来获取昵称
private String getName() throws Exception {
try {
//服务端的输入流读取客户端发送来的昵称输出流
BufferedReader bReader = new BufferedReader(
new InputStreamReader(socket.getInputStream(), "UTF-8"));
//服务端将昵称验证结果通过自身的输出流发送给客户端
PrintWriter ipw = new PrintWriter(
new OutputStreamWriter(socket.getOutputStream(), "UTF-8"),true);
//读取客户端发来的昵称
while(true) {
String nameString = bReader.readLine();
if ((nameString.trim().length() == 0) || storeInfo.containsKey(nameString)) {
ipw.println("FAIL");
} else {
ipw.println("OK");
return nameString;
}
}
} catch(Exception e) {
throw e;
}
}
@Override
public void run() {
try {
/*
* 通过服务器端的socket分配给每一个
* 用来将消息发送给客户端
*/
PrintWriter pw = new PrintWriter(
new OutputStreamWriter(socket.getOutputStream(), "UTF-8"), true);
/*
* 将客户昵称和其所说的内容存入共享集合HashMap中
*/
name = getName();
putIn(name, pw);
Thread.sleep(100);
// 服务端通知所有客户端,某用户上线
sendToAll("*系统消息* “" + name + "”已上线");
/*
* 通过客户端的Socket获取输入流
* 读取客户端发送来的信息
*/
BufferedReader bReader = new BufferedReader(
new InputStreamReader(socket.getInputStream(), "UTF-8"));
String msgString = null;
while((msgString = bReader.readLine()) != null) {
// 检验是否为私聊(格式:@昵称:内容)
if(msgString.startsWith("@")) {
int index = msgString.indexOf(":");
if(index >= 0) {
//获取昵称
String theName = msgString.substring(1, index);
String info = msgString.substring(index+1, msgString.length());
info = name + ":"+ info;
//将私聊信息发送出去
sendToSomeone(theName, info);
sendToSomeone(name,info);
continue;
}
}
// 遍历所有输出流,将该客户端发送的信息转发给所有客户端
m_display.append(name+":"+ msgString+"\n");
sendToAll(name+":"+ msgString);
}
} catch (Exception e) {
// e.printStackTrace();
} finally {
remove(name);
// 通知所有客户端,某某客户已经下线
sendToAll("*系统消息* "+name + "已经下线了。\n");
if(socket!=null) {
try {
socket.close();
} catch(IOException e) {
e.printStackTrace();
}
}
}
}
}
public static void main(String[] args) {
UDPServer server = new UDPServer();
server.setSize(400,400);
server.setVisible(true);
server.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
server.start();
}
}
UDP客户端:
import java.io.*;
import java.net.*;
import java.util.Scanner;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
public class UDPClient extends JFrame {
private JTextField m_enter=new JTextField();
private JTextArea m_display=new JTextArea();
private int m_count=0;
private static Socket clientSocket;
//private ExecutorService exec = Executors.newCachedThreadPool();
private BufferedReader br;
private PrintWriter pw;
public UDPClient()
{
super("聊天程序客户端");
Container c=getContentPane();
//m_enter.setSize(100,99);
//m_display.setSize(200,100);
m_enter.setVisible(true);
m_display.setVisible(true);
m_enter.requestFocusInWindow(); //转移输入焦点到输入区域
//将光标放置在文本区域的尾部
m_display.setCaretPosition(m_display.getText().length());
c.add(m_enter,BorderLayout.SOUTH);
c.add(new JScrollPane(m_display),BorderLayout.CENTER);
// this.add(panel);
// this.setContentPane(jp);
}
public static void main(String[] args) throws Exception {
UDPClient client = new UDPClient();
client.setVisible(true);
client.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
client.setSize(470,708);
client.start();
}
public void start() {
try {
m_display.append("请创建用户名:");
clientSocket=new Socket("localhost",6666);
BufferedReader br = new BufferedReader(
new InputStreamReader(clientSocket.getInputStream(), "UTF-8"));
PrintWriter pw = new PrintWriter(
new OutputStreamWriter(clientSocket.getOutputStream(), "UTF-8"), true);
//ListenrServser l=new ListenrServser();
m_enter.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent event)
{
try{
String s=event.getActionCommand();
m_enter.setText("");
if(m_count==0)
{
pw.println(s);
m_display.append("\n'"+s+"'"+"昵称设置成功。\n");
}
else
{
pw.println(s);
}
m_count++;
}catch(Exception e)
{
e.printStackTrace();
}
}
});
String msgString;
while((msgString = br.readLine())!= null) {
m_display.append(msgString+"\n");
}
} catch(Exception e) {
e.printStackTrace();
} finally {
if (clientSocket !=null) {
try {
clientSocket.close();
} catch(IOException e) {
e.printStackTrace();
}
}
}
}
}
运行结果:
1、这里是服务器端,显示当前连接人数,以及公聊信息:
自由创建对象来实现群聊
效果如图: