java TCP Socket实现聊天室功能

1、用户 --------> 服务器

用户与服务器间交互,而不是用户与用户间交互,也可以理解成只有自己一个人的聊天室,此时不用加入多线程。
过程:client A向server发送数据B,server接收此数据B,并将数据B返回发送至A,A再接收server返回回来的数据。
(1)server

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
/**
 * 使用TCP协议:简易聊天室
 * 多人聊天多线程加到服务器端
 * @author majinbuu
 *
 */
public class TCPChat_server {
	public static void main(String[] args) throws IOException {
		System.out.println("启动服务器......");
		//1、创建服务器端套接字,指定接口
		ServerSocket ssk=new ServerSocket(9100);
		//2、由于多个用户一起聊天,while循环多个accept(),甚至同时发送消息,这里要使用多线程
		Socket client=ssk.accept();
		DataInputStream data=new DataInputStream(client.getInputStream());
		DataOutputStream data2=new DataOutputStream(client.getOutputStream());
		boolean isRunning=true;
		while(isRunning) {
			String data1=data.readUTF();
			//System.out.println(data1);
			//3、将数据返回至客户端显示(聊天时,自己和别人都能看到自己发送的消息)
			data2.writeUTF(data1);
			data2.flush(); //writeUTF后必须清空
		}
		data.close();
		data2.close();
		ssk.close();
	}
}

(2)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;

public class TCPChat_client2 {
	public static void main(String[] args) throws IOException{
		System.out.println("启动客户端2......");
		//1、创建客户端套接字,并指明目的地,端口
		Socket sk=new Socket("localhost",9100);
		BufferedReader data1=new BufferedReader(new InputStreamReader(System.in));
		DataOutputStream data3=new DataOutputStream(sk.getOutputStream());
		DataInputStream getd=new DataInputStream(sk.getInputStream());
		boolean isRunning=true;
		while(isRunning) {
			//2、发送数据
			String data2=data1.readLine();
			data3.writeUTF(data2);
			data3.flush();
			
			//3、接收数据
			String getdata=getd.readUTF();
			System.out.println(getdata);
	   }
		//4、释放资源123
		data1.close();
		data3.close();
		getd.close();
		sk.close();
		
	}
}

2、加入多线程并《封装》

对服务器端进行改进:上面的代码只允许有一个客户端,这里加入while后,允许多个客户端的存在。然后加入多线程并封装,以简化代码:

package com.chatroom.tcp;

import java.io.Closeable;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;

public class chatServer {
	public static void main(String[] args) throws IOException {
		ServerSocket ssk=new ServerSocket(8889);
		boolean isRunning=true;
		while(isRunning) {
			Socket channel=ssk.accept();
			new Thread(new Channels(channel)).start();
		}
	}
}

class Channels implements Runnable{
	private DataInputStream data1;
	private DataOutputStream getback;
	private Socket channel;
	private boolean isRunning=true;
	
	Channels(Socket channel){
		this.channel=channel;
		try {
			data1=new DataInputStream(channel.getInputStream());
			getback=new DataOutputStream(channel.getOutputStream());
		} catch (IOException e) {
			e.printStackTrace();
			release(data1,getback,channel);
		}
	}
	
	//封装接收数据
	public String receive() {
		String rec="";
		try {
			rec=data1.readUTF();
		} catch (IOException e) {
			e.printStackTrace();
			release(data1,getback,channel);
		}
		return rec;
	}
	
	public void send(String str) {
		try {
			getback.writeUTF(str);
			getback.flush();
		} catch (IOException e) {
			e.printStackTrace();
			release(data1,getback,channel);
		}
	}
	
	public void release(Closeable... items) { //... 表示可以传入可变个数量的参数
		isRunning=false;
		for(Closeable item:items) {
			try {
				item.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}
	
	@Override
	public void run() {
		while(isRunning) {
			String recv=receive();
			if(!recv.equals(""))
				send(recv);
		}
		
	}
	
}

在客户端,这里暂时不考虑用户收、发消息的同时性(如果考虑,则要在客户端收发处 加入多线程),故不做修改。下一节会添加。

3、群聊

在上面的基础上改进:上面两个部分都只允许client与server交流,这里通过再服务器端加入容器,来使得不同的client之间能够传递信息。容器不使用ArrayList,而使用CopyOnWriteArrayList,它具有线程安全等优势。
CopyOnWriteArrayList参考1
CopyOnWriteArrayList参考2

对于客户端,其收发操作也需要加入多线程,否则只能按照默认的顺序(比如先发送数据,后接收数据)来执行单个线程。这样就会出现一个问题,即当A客户端启动并发送消息给B客户端时,B客户端此时阻塞在了发送数据的阶段,无法进行到接收数据的阶段,只有当B客户端发送一个数据后,才能执行接收数据的阶段,此时的A客户端才会收到B传过来的数据。

//假如不加入多线程,则客户端之间的收发无法同步
while(isRunning) {
			//2、发送数据
			String data2=data1.readLine();
			data3.writeUTF(data2);
			data3.flush();
			
			//3、接收数据
			String getdata=getd.readUTF();
			System.out.println(getdata);
	   }

因此,假如客户端收发程序段不加入多线程,则客户端之间的收发无法同步。
具体代码如下:
1、服务器端

package com.chatroom.tcp;

import java.io.Closeable;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.CopyOnWriteArrayList;

public class chatServer {
	static CopyOnWriteArrayList<Channels> allsoc=new CopyOnWriteArrayList<Channels>();
	public static void main(String[] args) throws IOException {
		System.out.println("enter the server......");
		ServerSocket ssk=new ServerSocket(9093);
		boolean isRunning=true;
		while(isRunning) {
			Socket channel=ssk.accept();
			Channels its=new Channels(channel,allsoc);
			allsoc.add(its);
			new Thread(its).start();
		}
	}
}

class Channels implements Runnable{
	private DataInputStream data1;
	private DataOutputStream getback;
	private Socket channel;
	private boolean isRunning=true;
	private CopyOnWriteArrayList<Channels> allsoc;
	
	Channels(Socket channel,CopyOnWriteArrayList<Channels> allsoc){
		this.channel=channel;
		this.allsoc=allsoc;
		try {
			data1=new DataInputStream(channel.getInputStream());
			getback=new DataOutputStream(channel.getOutputStream());
		} catch (IOException e) {
			e.printStackTrace();
			release(data1,getback,channel);
		}
	}
	
	//为了实现群聊功能,这里再封装一个方法,把消息发送到全部用户
	public void sendToEveryone(String str) {
		for(Channels i:allsoc) {
			if(this==i) continue; //不用把自己的消息返回给自己
			//这里是i.send(),不是send()
			i.send(str);  //调用send方法
		}
	}
	
	//封装接收数据
	public String receive() {
		String rec="";
		try {
			rec=data1.readUTF();
		} catch (IOException e) {
			e.printStackTrace();
			release(data1,getback,channel);
		}
		return rec;
	}

	public void send(String str) {
		try {
			getback.writeUTF(str);
			getback.flush();
		} catch (IOException e) {
			e.printStackTrace();
			release(data1,getback,channel);
		}
	}
	
	public void release(Closeable... items) { //... 表示可以传入可变个数量的参数
		isRunning=false;
		for(Closeable item:items) {
			try {
				item.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}
	
	@Override
	public void run() {
		while(isRunning) {
			String recv=receive();
			if(!recv.equals(""))
				sendToEveryone(recv);
		}	
	}	
}

2、客户端:

package com.chatroom.tcp;

import java.io.BufferedReader;
import java.io.Closeable;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Socket;
import java.net.UnknownHostException;

public class chatClient {
	public static void main(String[] args) throws UnknownHostException, IOException{
		System.out.println("enter the client......");
		//1、创建客户端套接字,并指明目的地,端口
		Socket sk=new Socket("localhost",9093);
		new Thread(new Send(sk)).start();
		new Thread(new Receive(sk)).start();
	}
}

class Send implements Runnable{
	private Socket sk;
	BufferedReader data1;
	DataOutputStream data3;
	boolean isRunning;
	Send(Socket sk) throws IOException{
		this.sk=sk;
		this.isRunning=true;
		data1=new BufferedReader(new InputStreamReader(System.in));
		data3 = new DataOutputStream(sk.getOutputStream());
	}
	@Override
	public void run() {
		while(isRunning) {
			String data2;
			try {
				data2 = data1.readLine();
				data3.writeUTF(data2);
				data3.flush();
			} catch (IOException e) {
				e.printStackTrace();
				release(sk,data1,data3);
			}
		}
	}
	
	public void release(Closeable... items) {
		isRunning=false;
		for(Closeable item:items) {
			try {
				item.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}
}

class Receive implements Runnable{
	private Socket sk;
	DataInputStream getd;
	boolean isRunning;
	Receive(Socket sk){
		this.sk=sk;
		this.isRunning=true;
		try {
			getd=new DataInputStream(sk.getInputStream());
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	@Override
	public void run() {
		while(isRunning) {
			try {
				String getdata=getd.readUTF();
				System.out.println(getdata);
			} catch (IOException e) {
				e.printStackTrace();
				release(sk,getd);
			}
		}
	}
	
	public void release(Closeable... items) {
		for(Closeable item:items) {
			try {
				item.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}
}

效果:
在这里插入图片描述

  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java TCP 实现聊天室需要通过网络套接字实现,具体步骤如下: 1. 创建服务器端和客户端的 Socket 连接。服务器端需要绑定一个端口号来监听客户端的连接请求。 2. 服务器端需要开启一个线程来接收客户端的连接请求,并创建一个对应的 Socket 连接。客户端需要连接服务器端的 Socket 连接。 3. 服务器端将客户端的 Socket 连接保存在一个集合中,以便后续广播消息给所有客户端。 4. 客户端和服务器端都需要开启一个线程来处理接收和发送消息。 5. 客户端发送消息时,需要将消息发送给服务器端,服务器端再将消息广播给所有客户端。 下面是一个简单的 Java TCP 实现聊天室的示例代码: 服务器端代码: ```java import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.ServerSocket; import java.net.Socket; import java.util.ArrayList; import java.util.List; public class Server { private ServerSocket serverSocket; private List<Socket> clients = new ArrayList<>(); public Server(int port) { try { serverSocket = new ServerSocket(port); } catch (IOException e) { e.printStackTrace(); } } public void start() { System.out.println("Server started!"); while (true) { try { Socket client = serverSocket.accept(); clients.add(client); new Thread(new ServerHandler(client)).start(); } catch (IOException e) { e.printStackTrace(); } } } private class ServerHandler implements Runnable { private Socket client; public ServerHandler(Socket client) { this.client = client; } @Override public void run() { try { InputStream is = client.getInputStream(); byte[] buffer = new byte[1024]; int len; while ((len = is.read(buffer)) != -1) { String message = new String(buffer, 0, len); System.out.println("Received from client: " + message); broadcast(message); } } catch (IOException e) { e.printStackTrace(); } finally { try { client.close(); clients.remove(client); } catch (IOException e) { e.printStackTrace(); } } } } private void broadcast(String message) { for (Socket client : clients) { try { OutputStream os = client.getOutputStream(); os.write(message.getBytes()); os.flush(); } catch (IOException e) { e.printStackTrace(); } } } public static void main(String[] args) { Server server = new Server(8888); server.start(); } } ``` 客户端代码: ```java import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.Socket; import java.util.Scanner; public class Client { private Socket socket; public void connect(String host, int port) { try { socket = new Socket(host, port); new Thread(new ClientHandler(socket)).start(); } catch (IOException e) { e.printStackTrace(); } } public void send(String message) { try { OutputStream os = socket.getOutputStream(); os.write(message.getBytes()); os.flush(); } catch (IOException e) { e.printStackTrace(); } } private class ClientHandler implements Runnable { private Socket socket; public ClientHandler(Socket socket) { this.socket = socket; } @Override public void run() { try { InputStream is = socket.getInputStream(); byte[] buffer = new byte[1024]; int len; while ((len = is.read(buffer)) != -1) { String message = new String(buffer, 0, len); System.out.println("Received from server: " + message); } } catch (IOException e) { e.printStackTrace(); } } } public static void main(String[] args) { Client client = new Client(); client.connect("localhost", 8888); Scanner scanner = new Scanner(System.in); while (true) { String message = scanner.nextLine(); client.send(message); } } } ``` 这个简单的聊天室支持多个客户端连接,客户端发送的消息会被广播给所有客户端。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值