多线程python 客户端fuwuq实现方式_Java学习之结合多线程技术,实现客户端和服务端任意通讯的“聊天室”功能...

e0e989019811ccf3262f22433ae3012f.png

1.、首先,是基于TCP的Socket的单次通信,简单的发送一次消息,得到一次消息

代码如下:

客户端

package com.chenpeng.client;

import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;

public class Client {

	public static void main(String[] args) throws IOException {
		//(1)创建Socket对象
		Socket client=new Socket("localhost",9999);
		//(2)从键盘获取数据
		BufferedReader br=new BufferedReader(new InputStreamReader(System.in));
		//(3)获取输出流
		DataOutputStream dos=new DataOutputStream(client.getOutputStream());
		//(4)获取输入流
		DataInputStream dis=new DataInputStream(client.getInputStream());
		
	
			String str=br.readLine();
			dos.writeUTF(str);//向服务器端发送数据
			System.out.println(dis.readUTF());//接收服务器端的数据
		
		
		
		
	   
	}
}

服务器端:

package com.chenpeng.server;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;

public class Server {
	
	public static void main(String[] args) throws IOException {
		System.out.println("----------------服务器端已开启---------------------");
		//(1)创建ServerSocket对象
		ServerSocket server=new ServerSocket(9999);
		
		//(2)监听客户端是否有客户端请求连接
		Socket socket=server.accept();
		
		//获取输入流
		DataInputStream dis=new DataInputStream(socket.getInputStream());
		//获取输出流
		DataOutputStream dos=new DataOutputStream(socket.getOutputStream());
		
			String str=dis.readUTF();
			System.out.println("客户端发送了:"+str);	
			dos.writeUTF("服务器端收到了:"+str);
			
		//(5)关闭流
		CloseUtil.closeAll(dos,dis,socket);
	}
}

工具类:省略,主要是关闭流和socket

缺点:

只能实现客户端发一次消息,服务器端发一次信息

如果我们要实现像QQ一样聊天那样,可以一直发,一直回复怎么办呢?

答:使用循环就可以一直发,使用多线程,发送消息和接收消息使用线程来就可以解决不用先发之后,再等服务器端的回复,而是发送和回复都可以同时进行。

接收数据的方法:

package com.chenpeng.util;

import java.io.DataInputStream;
import java.io.IOException;
import java.net.Socket;



/**
 * 接收数据的线程
 * @author chenpeng
 *
 */
public class Receive implements Runnable {
	//用于接收数据的数据流
	private DataInputStream dis;
	private boolean flag= true;
	//构造方法
	public Receive(Socket client) {
		try {
			dis = new DataInputStream(client.getInputStream());
		} catch (IOException e) {
			//有异常,则关闭资源
			flag = false;
			CloseUtil.closeAll(dis,client);
		}
	}
	//接收数据的方法
	private String getMessage() {
		String str ="";
		try {
			str = dis.readUTF();
		} catch (IOException e) {
			flag = false;
			CloseUtil.closeAll(dis);
		}
		return str;
	}
	@Override
	public void run() {
		while(flag) {
			System.out.println(this.getMessage());
		}
		
	}

}

发送数据的方法:

package com.chenpeng.util;

import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Socket;



/**
 * 发送消息的线程
 * @author chenpeng
 *
 */
public class Send implements Runnable{
 //从键盘获取数据
	private BufferedReader br;
	//发送数据使用数据输出流
	private DataOutputStream dos;
	private boolean flag = true;//默认为true
	//无参构造方法
	public Send() {
		br = new BufferedReader(new InputStreamReader(System.in));
	}
	//有参构造器
	public Send(Socket client) {
		this();//调用本类的无参构造方法
		try {
			dos = new DataOutputStream(client.getOutputStream());
		} catch (IOException e) {//有问题则关闭
			flag= false;
			CloseUtil.closeAll(dos,client);
		}
	}
	//从键盘得到信息
	private String getMessage() {
		String str="";
		try {
			str =br.readLine();
		} catch (IOException e) {
			flag = false;//
			CloseUtil.closeAll(br);
		}
		return str;
	}
	//发送给服务端
	private void send(String str) {
		try {
			dos.writeUTF(str);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		try {
			dos.flush();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	@Override
	public void run() {
		while(flag) {
			//调用发送信息的方法
			this.send(getMessage());
		}
		
	}

}

服务端方法:

/**
 * 服务端
 * 
 * @author chenpeng
 *
 */
public class Server {

	public static void main(String[] args) throws IOException {
		System.out.println("----------服务端-----------");
		// 1.创建一个ServerSocket对象
		ServerSocket server = new ServerSocket(8848);
		// 2.监听客户端是否发请求过来
		Socket client = server.accept();
			// 创建接收数据的线程
			Receive receive = new Receive(client);
			// 创建发送数据回去的线程
			Send send = new Send(client);
			//启动线程
			new Thread(receive).start();
			new Thread(send).start();
		
	}
}

客户端的方法:

package com.chenpeng.Thread;

import java.io.IOException;
import java.net.Socket;
import java.net.UnknownHostException;

/**
 * 客户端
 * @author chenpeng
 *
 */
public class Client {
	public static void main(String[] args) throws UnknownHostException, IOException {
		System.out.println("--------客户端---------");
		//创建Socket对象
		Socket client= new Socket("localhost",8848);
		//创建发送的线程类对象
		Send send = new Send(client);
		//创建接收的线程类对象
		Receive receive = new Receive(client);
		//创建Thread类对象的线程并启动线程
		new Thread(send).start();
		new Thread(receive).start();
	}
}

这时候就可以实现客服端发消息,服务器端也能发消息,简单实现了QQ的聊天功能,原理就是两边都使用发送数据和接收数据的线程,然后一直循环。

201ed14cf10b18b6ddc69dbed8c9b4fd.png

24a11d32f42ed0306976cc9e4c27cf8f.png

7e4731e623ee733447602da66e8768f5.png

c9eef87bb7c235d6a492e54054d70484.png

但是有个小问题,如果我再启动一个客户端发送消息,是不会到服务端的?这里等我思考之后什么回事再解决吧,今天主要是实现“群聊”的功能

3、现在,到了实现“群聊”的时候了

思路:

1、首先基本上和上面客户端和服务器端一直聊没区别,也是使用了发送消息的线程和接收消息的线程

2、但是这里问题来了,一个客户端发送消息,如何让其他的客户端接收到消息呢?这时候,我们需要在服务器端来一个线程类MyChannel,用来记录每个客户端发送的数据,然后再使用群发的功能sendOther(),不能发给自己,所以我们在服务器端上面创建了一个Arraylist集合,用于存放运行着的线程。

package com.chenpeng.util;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;
import java.util.List;

/**
 * 每一个客户端都是一条道路
 * 1.输入流
 * 2.输出流
 * 3.接收数据
 * 4.发送数据
 * @author chenpeng
 *
 */
public class MyChannel implements Runnable {
	private DataInputStream dis;
	private DataOutputStream dos;
	private boolean flag = true;
	//构造方法
	public MyChannel(Socket client) {
		try {
			dis = new DataInputStream(client.getInputStream());
			dos = new DataOutputStream(client.getOutputStream());
		} catch (IOException e) {
			flag = false;
			CloseUtil.closeAll(dis,dos);
		}
	}
	//接收数据的方法
	private String receive() {
		String str ="";
		try {
			str = dis.readUTF();
		} catch (IOException e) {
			flag = false;
			CloseUtil.closeAll(dis,dos);
			Server.list.remove(this);
		}
		return str;
	}
	//发送数据的方法
	private void send(String str) {
		if(str!=null&&str.length()!=0) {
			try {
				dos.writeUTF(str);
				dos.flush();
			} catch (IOException e) {
				flag= false;
				CloseUtil.closeAll(dos,dis);
				Server.list.remove(this);
			}
		}
	}
	//转发的方法
	private void sendOther() {
		String str = this.receive();
		List<MyChannel> list = Server.list;
		for(MyChannel other: list) {
			if(other == this) {
				continue;//不发给自己
			}
			other.send(str);//发送数组
		}
	}
	@Override
	public void run() {
		while(flag) {
			//调用发送数据的方法
			sendOther();
		}
		
	}

}

d580e0422e23d91301786983832bfc8f.png

注意:客户端这里使用了while循环,一直监听者客户端有没有客户端连接

总结:

单次聊天只能先发数据,然后才能接收数据,如果不发就收不到,因为接收和收到是在一个线程里面实现的,不符合实际的情况

解决方案:

1、想要多次聊天可以使用循环

2、先发后收的的问题可以使用线程来解决,一个接收数据的线程,一个发送数据的线程

实现多用户登录和多人聊天需要使用网络编程和多线程技术。下面给出一个简单的示例,包含客户端服务端的源代码。 服务端: ```python import socket import threading # 定义服务器地址和端口号 HOST = 'localhost' PORT = 8888 # 创建socket对象 server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) server_socket.bind((HOST, PORT)) server_socket.listen(5) # 定义一个字典,存储在线用户信息 user_dict = {} # 处理客户端连接的函数 def handle_client(client_socket, client_address): # 获取客户端用户名 username = client_socket.recv(1024).decode('utf-8') # 将客户端加入在线用户字典 user_dict[username] = client_socket print('用户 %s 已连接!' % username) # 向所有用户广播有新用户连接 broadcast('%s 已加入聊天室!' % username) # 循环接收客户端消息并广播给其他用户 while True: try: message = client_socket.recv(1024).decode('utf-8') if message: broadcast('%s 说:%s' % (username, message)) except: # 客户端断开连接,从在线用户字典中移除 user_dict.pop(username) print('用户 %s 已断开连接!' % username) # 向所有用户广播有用户断开连接 broadcast('%s 已退出聊天室!' % username) client_socket.close() return # 广播消息给所有在线用户 def broadcast(message): for username, client_socket in user_dict.items(): try: client_socket.sendall(message.encode('utf-8')) except: pass # 循环接收客户端连接 while True: client_socket, client_address = server_socket.accept() # 创建线程处理客户端连接 client_thread = threading.Thread(target=handle_client, args=(client_socket, client_address)) client_thread.start() ``` 客户端: ```python import socket import threading import tkinter as tk from tkinter import messagebox # 定义服务器地址和端口号 HOST = 'localhost' PORT = 8888 # 创建socket对象 client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) client_socket.connect((HOST, PORT)) # 定义GUI窗口 root = tk.Tk() root.title('多人聊天室') root.geometry('400x300') # 定义GUI控件 username_label = tk.Label(root, text='用户名:') username_label.pack() username_entry = tk.Entry(root) username_entry.pack() message_listbox = tk.Listbox(root) message_listbox.pack(fill=tk.BOTH, expand=True) message_entry = tk.Entry(root) message_entry.pack(fill=tk.X) # 定义发送消息的函数 def send_message(event): message = message_entry.get() if message: client_socket.sendall(message.encode('utf-8')) message_entry.delete(0, tk.END) # 定义接收消息的函数 def recv_message(): while True: try: message = client_socket.recv(1024).decode('utf-8') message_listbox.insert(tk.END, message) except: messagebox.showerror('错误', '连接已断开!') client_socket.close() root.quit() # 定义登录的函数 def login(event): username = username_entry.get() if username: client_socket.sendall(username.encode('utf-8')) username_entry.config(state=tk.DISABLED) message_entry.bind('<Return>', send_message) login_button.config(state=tk.DISABLED) username_label.config(text='用户名:%s' % username) threading.Thread(target=recv_message).start() # 定义GUI控件 login_button = tk.Button(root, text='登录') login_button.pack() # 绑定事件 login_button.bind('<Button-1>', login) # 进入主循环 root.mainloop() ``` 运行以上代码,即可实现一个简单的多人聊天室。注意,这只是一个示例,实际应用中还需要考虑许多安全和性能问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值