java TCP/IP实现简单的多人聊天功能

TCP/IP是可靠的网络协议,数据的传输需要服务端和客户端之间三次“握手”,比较适合文本等一些可靠性要求高的数据传输,但是它的效率较UDP低。下面通过一张图来简要说明使用 ServerSocket 创建 TCP 服务器端和使用Sock创建客户端通信的流程:


这只是实现了单个服务端与客户端的通讯,要想实现与多个客户端的通讯,要在服务端的发送信息线程里把信息转发给各个客户端,在服务端里循环监听客户端的连接,每当有一个客户端连接则把该客户端放进一个集合里面(转发信息时用),然后为该客户端新建一个接收线程和一个发送线程。


具体实现代码:

Server端

package project20150806;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.Scanner;

//发送信息线程
class SendThreat implements Runnable {
	Socket socket;
	//在这里使用PrintWriter流来向客户端发送信息,也可以用其它流
	PrintWriter pWriter;
	//接收来自主线程的客户端集合
	private ArrayList<Socket> socketList;
	
	//从键盘输入获取信息
	Scanner scanner = new Scanner(System.in);

	public SendThreat(Socket socket,ArrayList<Socket> socketList) {
		super();
		this.socket = socket;
		this.socketList=socketList;
		try {
			//接收socket的字节输出流,用OutputStreamWriter把字节输出流转化为字符流,再传给PrintWriter
			pWriter = new PrintWriter(new OutputStreamWriter(socket.getOutputStream()));
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	@Override
	public void run() {

		while (true) {
			//获取从键盘输入的信息
			String strMsg =socket.getInetAddress().getHostAddress()+":"+ scanner.nextLine();
			if (strMsg == "b") {
				break;
			}
			
			//把服务器收到的信息转发给各个客户端
			for (Socket clientSock : socketList) {
				PrintWriter pWriter;
				try {
					//获取socket的输出流,用来向客户端发送信息
					pWriter = new PrintWriter(clientSock.getOutputStream());
					//输出信息给客户端
					pWriter.println(strMsg);
					//刷新输出流
					pWriter.flush();
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				
				// pWriter.close();
			}
//			pWriter.println(strMsg);
//			pWriter.flush();
//			pWriter.close();
			// System.out.println(strMsg);
		}

	}
}

//接收信息线程
class ReceiveThreat implements Runnable {
	Socket socket;
	BufferedReader bReader;
	private ArrayList<Socket> socketList;

	public ReceiveThreat(Socket socket, ArrayList<Socket> socketList) {
		super();
		this.socket = socket;
		this.socketList = socketList;
		try {
			//获取socket的输入流
			bReader = new BufferedReader(new InputStreamReader(socket.getInputStream()));

		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	@Override
	public void run() {
		while (true) {

			try {
				String strMsg = bReader.readLine();
				System.out.println(strMsg);

				for (Socket clientSock : socketList) {
					PrintWriter pWriter = new PrintWriter(clientSock.getOutputStream());
					pWriter.println(strMsg);
					pWriter.flush();
					// pWriter.close();
				}

			} catch (IOException e) {
				// TODO Auto-generated catch block
				ChatServer.socketList.remove(socket);
			}
		}
	}
}

public class ChatServer {

	//定义一个集合用来存放 监听到的客户端socket
	public static ArrayList<Socket> socketList = new ArrayList<>();

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		ServerSocket serverSocket = null;

		try {
			//新建一个服务端ServerSocket,锁定端口号为30000,端口号建议锁定大一点的
			serverSocket = new ServerSocket(30000);
			System.out.println("等待客户端连接...");
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		while (true) {
			Socket socket = null;
			while (true) {
				try {
					//监听客户端的连接
					socket = serverSocket.accept();
					//加入集合
					socketList.add(socket);
					System.out.println("客户端 " + socket.getInetAddress().getHostAddress() + "连接成功!");

					// showHello(socket);
					//为该客户端分别开启一个发送信息线程和接收信息线程
					new Thread(new SendThreat(socket,socketList)).start();
					new Thread(new ReceiveThreat(socket, socketList)).start();

				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}
	}

}


客户端:

package project20150806;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Scanner;

//发送信息线程
class SendClientThreat implements Runnable {
	Socket socket;
	PrintWriter pWriter;
	Scanner scanner;

	public SendClientThreat(Socket socket) {
		super();
		this.socket = socket;
		this.scanner = new Scanner(System.in);
		try {
			pWriter = new PrintWriter(new OutputStreamWriter(socket.getOutputStream()));
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	@Override
	public void run() {

		while (true) {
			String strMsg =socket.getInetAddress().getHostAddress()+":"+ scanner.nextLine();
			pWriter.println(strMsg);
			pWriter.flush();
			
//			System.out.println(strMsg);
		}

	}
}

//接收信息线程
class ReceiveClientThreat implements Runnable {
	Socket socket;
	BufferedReader bReader;

	public ReceiveClientThreat(Socket socket) {
		super();
		this.socket = socket;
		try {
			bReader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	@Override
	public void run() {
		try {

			while (true) {
				String strMsg = bReader.readLine();
				System.out.println(strMsg);
			}

		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

	}
}

public class ChatClient {

	private static Socket socket;

	public static void main(String[] args) {
		//服务端的IP
		String IPAdress="192.168.1.199";
		//创建一个客户端socket,指定服务端的IP和端口号
		try {
			socket = new Socket(IPAdress, 30000);
			System.out.println("连接主机成功! ");
			
			new Thread(new ReceiveClientThreat(socket)).start(); 
			new Thread(new SendClientThreat(socket)).start();
			
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		
	}

}


这样就可以实现简单的聊天室功能了。把从键盘获取的信息换成从文件获取的流,还可以实现文件传输功能。

如有不足之处请多多指教,谢谢!



  • 2
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值