用TCP编程实现一对一式聊天,并用多线程解决了处于同一线程中的问题。
客户端代码:mport java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Scanner;
public class ChatSocketClient {
public static void main(String[] args) {
try {
Socket socket =new Socket("127.0.0.1",8888);
System.out.println("连接成功!");
new ClientSend(socket).start();
new Clientreive(socket).start();
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 用于发送消息线程类
*/
class ClientSend extends Thread{
@Override
public void run() {
this.sendMsy();
}
private Socket socket;
public ClientSend(Socket socket){
this.socket =socket;
}
/**
* 发送消息
*/
private void sendMsy(){
Scanner scanner =null;
PrintWriter pw =null;
try{
scanner =new Scanner(System.in);
pw =new PrintWriter(this.socket.getOutputStream());
while(true){
String str =scanner.nextLine();
pw.println(str);
pw.flush();
}
}catch (Exception e){
e.printStackTrace();
}finally {
if (scanner!=null){
scanner.close();
}
if (pw!=null){
pw.close();
}
if (socket!=null){
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
/**
*用于接收消息线程类
*/
class Clientreive extends Thread{
private Socket socket=null;
public Clientreive(Socket socket){
this.socket =socket;
}
@Override
public void run() {
this.receiveMsg();
}
/**
* 用于接收对方消息
*/
private void receiveMsg(){
BufferedReader br =null;
try{
br =new BufferedReader(new InputStreamReader(this.socket.getInputStream()));
while(true){
String mr = br.readLine();
System.out.println("他说"+mr);
}
}catch (Exception e){
e.printStackTrace();
}finally {
if (br!=null){
try {
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}服务端代码: import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;
/**
* 发送消息线程
*/
class Send extends Thread{
private Socket socket;
public Send(Socket socket){
this.socket =socket;
}
@Override
public void run() {
this.sendMsy();
}
/**
* 发送消息
*/
private void sendMsy(){
Scanner scanner =null;
PrintWriter pw =null;
try{
scanner =new Scanner(System.in);
pw =new PrintWriter(this.socket.getOutputStream());
while(true){
String str =scanner.nextLine();
pw.println(str);
pw.flush();
}
}catch (Exception e){
e.printStackTrace();
}finally {
if (scanner!=null){
scanner.close();
}
if (pw!=null){
pw.close();
}
if (socket!=null){
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
/**
* 接收消息的线程
*/
class receive extends Thread{
private Socket socket=null;
public receive(Socket socket){
this.socket =socket;
}
@Override
public void run() {
this.receiveMsg();
}
/**
* 用于接收对方消息
*/
private void receiveMsg(){
BufferedReader br =null;
try{
br =new BufferedReader(new InputStreamReader(this.socket.getInputStream()));
while(true){
String mr = br.readLine();
System.out.println("他说"+mr);
}
}catch (Exception e){
e.printStackTrace();
}finally {
if (br!=null){
try {
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
public class ChatSocketServer {
public static void main(String[] args) {
ServerSocket serverSocket =null;
try{
serverSocket =new ServerSocket(8888);
System.out.println("服务端已启动等待连接");
Socket socket = serverSocket.accept();
System.out.println("连接成功!");
new Send(socket).start();
new receive(socket).start();
}catch(Exception e){
e.printStackTrace();
}finally {
if (serverSocket!=null){
try {
serverSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
实现服务端对话框:
其中可自行更改对话框大小样式等配置。
- package com.ex.controller;
import javax.servlet.http.HttpServletRequest;
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进行服务器启动,另一个进行链接即可。就可实现实时对话
效果展示:服务器端:
1.1 服务器端继承JFrame框架,添加组件。
1.2 创建服务器端socket。建立一个哈希表,用于存储客户端的昵称以及服务器端对于每个连接的客户端建立的输出流。
1.3建立一个线程池,为每个连接的客户端分配一个执行线程。
1.3调用服务器端Socket的accept()函数,等待客户端的连接,每连接到一个客户端,线程池给相应的客户端分配一个线程。\n1.4每个线程内,调用服务器端Socket,封装getOutputStream(),获得服务器端Socket相对应于连接的客户端的输出流和输入流。输入流首先读取到客户端发送来的昵称。将客户端的昵称和服务器端的输出流存储在哈希表中,并遍历整个哈希表,将某人上线的通知发送给所有的在线客户端。
1.5客户端将信息发送给服务器端,当服务器端传来的信息符合“@私聊对象:私聊信息”的格式时,服务器端认为这是私聊信息。服务器端将把私聊对象的昵称从信息中提取出来,通过哈希表找到与昵称对应的输出流。将私聊信息通过输出流发送给私聊对象。
当不是私聊信息时,服务器遍历整个哈希表,通过每个客户端对应的输出流发送给每个客户端。
1.6当客户端下线时,服务器端将移除哈希表中对应存储的客户端昵称以及输出流。并通知在线的每个客户端,某人已下线。
(二) 客户端:
2.1 客户端继承框架JFrame,添加各种组件。
2.2 创建客户端Socket,设置请求连接服务器端IP,以及使用的端口号。
2.3 封装客户端Socket的getInputStream()函数,获得客户端Socket的输入流接受服务器端发来的信息,封装Socket的getOutputStream()函数,获得Socket的输出流向服务器端发送信息。
2.4 通过向文本框添加动作事件监听器,监听文本框的输入。
2.5 当与服务器端连接成功时,系统提醒输入昵称。系统将对输入的昵称进行检索。判断是否有重复的昵称。如有重复则创建不成功,继续输入。
2.6 向服务器端发送信息时,如想对某人发送私聊信息,则需输入符合“@私聊对象的呢称:私聊信息”的格式,此时只有私聊对象和本人可以接收到。否则,发送的信息为公聊信息,所有的客户端都能够收到。
2.7 客户端不断接受服务器端的信息显示在文本域。