计算机网络实验4 - TCP套接字编程 - 点对点聊天 - 代码实现

  时间:2021-06-10 05:10:51  阅读量:441  评论数:0  作者:

本文描述了计算机网络实验4 - TCP套接字编程 - 点对点聊天 - 代码实现的相关知识,对类似的源码问题比较有借鉴作用,感兴趣的可以了解细看。

客户端

package chat;

import java.io.IOException;
import java.io.PrintStream;
import java.net.Socket;
import java.util.Scanner;
 
// 客户端读取服务器端信息的线程
class ClientReadServer implements Runnable{
	
    private Socket socket;
    public ClientReadServer(Socket socket){
        this.socket=socket;
    }
    @Override
    public void run() {
        // 获取服务器端输入流
        try {
            Scanner scanner=new Scanner(socket.getInputStream());
            System.out.println("test");
            while(scanner.hasNext()){
            	// System.out.println("内容:"+scanner.nextLine());
            	String meg=scanner.nextLine();
                System.out.println("服务端返回:"+meg);
                Frame.back(meg);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}


// 客户端向服务器端发送信息的线程
class ClientSendServer implements Runnable{
	private static boolean sendFlag = false; //发送标记 点击发送后变true
	private static String msg;
    private Socket socket;
    public ClientSendServer(Socket socket){
        this.socket=socket;
    }
    @Override
    public void run() {
        try {
            // 获取服务器端的输出流
            PrintStream printStream=new PrintStream(socket.getOutputStream());

            while(true){
                if(sendFlag){
                    printStream.println(msg);
                    System.out.println("线程中run 发送:"+msg);
                    sendFlag = false; //发送完成后置为false
                }
                //暂停进程 
				try {
					Thread.sleep(1000);
				} catch (InterruptedException e1) {
					// TODO Auto-generated catch block
					e1.printStackTrace();
				}
                // System.out.println("进程进行中!");
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    public static void change(String m,boolean b) {
    	msg = m;
    	sendFlag = true;
    }
}
public class Client {
    public  void mainClient () throws IOException{
        //1.客户端连接服务器端,返回套接字Socket对象
        Socket socket=new Socket("127.0.0.1",9527);
        //2.创建读取信息的线程和发送信息的线程
        Thread read=new Thread(new ClientReadServer(socket));
        Thread send=new Thread(new ClientSendServer(socket));
        //3.启动线程
        read.start();
        send.start();
    }
    public void send(String mes) {
    	//发送信息
    	System.out.println("准备发送的数据:"+mes);
    	ClientSendServer.change(mes, true);
    }
}

用户界面

package chat;

import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.IOException;

import javax.swing.*;

public class Frame extends JFrame {
	private static Client client;
	private static String backMeg = ""; //服务器返回消息
	private static JTextArea area;
	private static final long serialVersionUID = 1L;
	
	public static void back(String m) {
		backMeg = m;
		if(backMeg.contains(":")) {
			String context = backMeg;
			String name ="\n"+ backMeg.split("@")[1].split("\\:")[0] + ":";
			context=context.replace('@','\n'); //时间隔开
			context=context.replace("$",name);
			area.append(context+"\n"); //显示文本内容增加
		}
	}

	public Frame() {
		//JFrame frame = new JFrame("Chat2die"); //创建窗体对象
		setTitle("ChatRoom"); //标题
		
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		setBounds(200, 200, 650, 600);  //位置 长宽
		setResizable(false); //大小是否可变
		
		Container c = getContentPane(); //获取窗体颜色
		c.setBackground(new Color(255, 221, 221,200)); //背景色
		c.setLayout(null);
		
		Font f1=new Font("黑体",Font.PLAIN,16);//根据指定字体名称、样式和磅值大小,创建一个新 Font。
		Font f2=new Font("楷体",Font.BOLD,18);
		Font f4=new Font("楷体",Font.BOLD,16);
		Font f3=new Font("黑体",Font.PLAIN,14);
		
		//发送文本域
		JTextArea jt = new JTextArea();  //文本域
		// jt.setText("点击右边发送键发送"); //提示语
		jt.setFont(f4); //设置字体格式
		JScrollPane jp = new JScrollPane(jt);  //滚动条
		jp.setBounds(50, 490, 420, 40);  //设置绝对位置
		c.add(jp);
		
		//显示文本域
		area = new JTextArea();
		area.setFont(f2); //设置字体格式
		area.setEditable(false);
		JScrollPane jpArea = new JScrollPane(area);  //滚动条
		jpArea.setBounds(50, 190, 510, 250);  //设置绝对位置
		c.add(jpArea);
		
		//消息区
		JLabel message = new JLabel();
		message.setFont(f2);
		message.setText("接收消息区");
		message.setBounds(50, 125, 140, 80);
		c.add(message);

		//在此输入您要发送的消息,点击右侧发送按钮发送
		JLabel sendmessage = new JLabel();
		sendmessage.setFont(f2);
		sendmessage.setText("在此输入您要发送的消息,点击右侧发送按钮发送");
		sendmessage.setBounds(50, 430, 500, 80);
		c.add(sendmessage);
				
		//目的IP
		JLabel desIp = new JLabel();
		desIp.setFont(f2);
		desIp.setText("目标IP:");
		desIp.setBounds(50, 20, 100, 80);
		c.add(desIp);
		JTextField desjt = new JTextField();
		desjt.setFont(f4);
		desjt.setBounds(140, 45, 150, 30);
		
		desjt.setText("127.0.0.1"); //设置默认IP地址
		desjt.setEditable(false);
		c.add(desjt);
		
		//目的端口
		JLabel desPo = new JLabel();
		desPo.setFont(f2);
		desPo.setText("目标端口:");
		desPo.setBounds(320, 20, 100, 80);
		c.add(desPo);
		JTextField desJtPo = new JTextField();
		desJtPo.setFont(f4);
		desJtPo.setBounds(405, 45, 150, 30);
		
		desJtPo.setText("0000"); //设置默认端口
		c.add(desJtPo);
		
		//你的昵称
		JLabel name = new JLabel();
		name.setFont(f2);
		name.setText("你的昵称:");
		name.setBounds(50, 75, 100, 80);
		c.add(name);
		JTextField jtName = new JTextField();
		jtName.setFont(f4);
		jtName.setBounds(140, 100, 150, 30);
		int num = (int) (Math.random()*100);
		String t = "未命名用户["+num+"]";
		jtName.setText(t);
		c.add(jtName);
		
		//本机端口
		JLabel myPo = new JLabel();
		myPo.setFont(f2);
		myPo.setText("本机端口:");
		myPo.setBounds(320, 75, 100, 80);
		c.add(myPo);
		JTextField jtmyPo = new JTextField();
		jtmyPo.setFont(f4);
		jtmyPo.setBounds(405, 100, 150, 30);
		jtmyPo.setEditable(false);
		c.add(jtmyPo);
		
		//绑定端口 建立连接
		JButton btnBind = new JButton("绑定端口 建立连接");
		btnBind.setBackground(new Color(255,221,221));
		btnBind.addActionListener(new ActionListener() 
		{
			public void actionPerformed(ActionEvent e) 
			{
				try {
					client.mainClient();
					//登录
					client.send("userName:"+jtName.getText());
				} catch (IOException e1) {
					// TODO Auto-generated catch block
					e1.printStackTrace();
				}
				//暂停进程 等待服务器返回数据
				try {
					Thread.sleep(1000);
				} catch (InterruptedException e1) {
					// TODO Auto-generated catch block
					e1.printStackTrace();
				}
				System.out.println("***"+backMeg);
				//拿到绑定的本机端口号
				jtmyPo.setText(backMeg);
			}
		});
		btnBind.setBounds(405, 130, 150, 30);
		btnBind.setFont(f3);
		c.add(btnBind);
		
		//发送按钮
		JButton btn = new JButton("发送");
		btn.addActionListener(new ActionListener() 
		{
			public void actionPerformed(ActionEvent e) 
			{
				//String sendName = jtName.getText().equals("")?"I AM NULL":jtName.getText();
				String context = jt.getText().equals("")?"空空如也":jt.getText();
				context=context.replace('\n','$');
				jt.setText(""); //清空输入
				jt.requestFocus(); //获取光标
				String sendPort = desJtPo.getText(); //获取发送端口
				if(sendPort.equals("")) return;
				String name = jtName.getText();
				String sendMes = "P:" + sendPort +"-"+ name + "-" + context;
				System.out.println("发送框中的内容是:"+sendMes);
				
				client.send(sendMes);
				
				//暂停进程 等待服务器返回数据
				try {
					Thread.sleep(1000);
				} catch (InterruptedException e1) {
					// TODO Auto-generated catch block
					e1.printStackTrace();
				}
				//System.out.println("发送后接收到的内容是:"+backMeg);
				//area.append(backMeg+"\n"); //文本内容增加
			}
		});
		btn.setBounds(490, 490, 70, 40);
		btn.setBackground(new Color(255,221,221));
		btn.setFont(f1);
		c.add(btn);
		
		setVisible(true); //可见
	}
	//美化图标
	public static void beautiful() {
		try {
            for (javax.swing.UIManager.LookAndFeelInfo info : javax.swing.UIManager.getInstalledLookAndFeels()) {
                if ("Nimbus".equals(info.getName())) {
                    javax.swing.UIManager.setLookAndFeel(info.getClassName());
                    break;
                }
            }
        }catch(Exception e) {
        	System.out.println(e);
        }
	}
	
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		beautiful();
		client = new Client();
		new Frame();
	}
}


服务器端

package chat;

import java.io.IOException;
import java.io.PrintStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Map;
import java.util.Scanner;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
 
class Server implements Runnable{
    private static Map<String,Socket> map=new ConcurrentHashMap<>();
    private Socket socket;
    public Server(Socket socket){
        this.socket=socket;
    }
    @Override
    public void run() {
        // 获取客户端的输入流
        try {
            Scanner scanner=new Scanner(socket.getInputStream());
            String msg=null;
            while(true){
                if(scanner.hasNextLine()){
                    //处理客户端输入的字符串
                    msg=scanner.next();
                    
                    System.out.println("服务器接收:"+msg);
                    //用户登录流程,注册用户的格式为:userName:用户名
                    if(msg.startsWith("userName:")){
                        //将用户名或者端口号保存在userName中
                        String userName=msg.split("\\:")[1];
                        //注册该用户
                        userRegist(userName,socket);
                        continue;
                    }
                    //聊天信息,输入的格式为:P:port-userName-聊天信息
                    else if(msg.startsWith("P:")&&msg.contains("-")){
                      
                        //保存需要聊天的目标端口、发送者昵称、发送的信息
                        String port=msg.split("\\:")[1].split("-")[0];
                        String name = msg.split("\\:")[1].split("-")[1];
                        String str=msg.split("\\:")[1].split("-")[2];
                        
                        //发送聊天信息
                        privateChat(port,name,str);
                        continue;
                    }
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
 
    //登录 建立连接
    private void userRegist(String userName,Socket socket) throws IOException{
        
        //返回socket 值
        PrintStream printStream=new PrintStream(socket.getOutputStream());
        String port=socket.toString().split(",")[1].split("=")[1];
        
        printStream.println(port);
        map.put(port,socket); //将 [端口号,套接字] 存一份
        
        System.out.println("[用户名为"+userName+"][客户端为"+socket+"]上线了!");
        System.out.println("当前在线人数为:"+map.size()+"人");
    }
 
    /**
     * 私聊流程(利用port取得目标客户端的Socket对象,从而取得对应输出流,将私聊信息发送到指定客户端)
     * @param port 目标客户端端口号
     * @param userName 发送方的用户名
     * @param msg 要发送的信息
     */
    private void privateChat(String port,String userName,String msg) throws IOException {
        //1.当前发送方客户端的用户名
        String curUser=userName;
        
        //2.取得私聊用户名对应的客户端
        Socket client=map.get(port);
        
        //3.获取私聊客户端的输出流,将私聊信息发送到指定客户端
        PrintStream printStream=new PrintStream(client.getOutputStream());
        Date date = new Date();
        SimpleDateFormat formatter = new SimpleDateFormat("dd-MM-yyyy HH:mm:ss");  //获得当前时间
        String m = formatter.format(date)+"@"+curUser+":"+msg;
        System.out.println(formatter.format(date));
        System.out.println(m);
        printStream.println(m);
        //socket.close();
    }
 
}
public class MultiServer {
    public static void main(String[] args){
        try {
            //1.创建服务器端的ServerSocket对象,等待客户端连接
            ServerSocket serverSocket=new ServerSocket(9527);
            //2.创建线程池,从而可以处理多个客户端
            ExecutorService executorService= Executors.newFixedThreadPool(50);
            for(int i=0;i<50;i++){
                System.out.println("欢迎来到我的聊天室......");
                //3.侦听客户端
                Socket socket=serverSocket.accept();
                System.out.println("有新的朋友加入.....");
                //4.启动线程
                executorService.execute(new Server(socket));
            }
            //5.关闭线程池
            executorService.shutdown();
            //6.关闭服务器
            serverSocket.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
 

详细解析在另一篇给出:
[计算机网络实验4 - TCP套接字编程 - 点对点聊天 - 分析] - 找不到我吧我独一无二的博客 - CSDN博客

关键词:socket java 网络 多线程,计算,计算机,计算机网,计算机网络,网络,实验,tcp,套接字,编程,点对点,聊天,代码,实现