如果你经过了一段时间的JavaSE学习,对面向对象、流与文件、多线程、网络、GUI等有所了解,那么你可以试着做几个综合性的程序练练手.
3.客户端主程序
4.客户端线程类
首先,模仿别人的思路和代码;然后,独立设计完成;在此基础之上,使自己的代码逐步完善、功能不断强大——这就是一个总结与升华的过程,是学习遭遇瓶颈之后的必要阶段。
在这里笔者会把自己练习的项目源代码、注释、开发工具、相关知识、经验等分享给大家,希望可以为大家提供帮助,也希望可以获得大家的批评与指导。
爱Java,爱Share。i java,i,share.
这里实现了命令行界面的C/S聊天系统,一共涉及到4个类,
在服务器端,每个Socket对应一个线程,实现每个连接的用户发送的消息向所有客户广播一边的功能。
在客户端,可以读取数据并将其在控制台打印出来,可以发送数据广播给连接服务器的每位用户。
笔者开发环境:
jdk-8u40-windows-i586
Windows Server 2003
Notepad++
测试时请注意包名!
1.服务器端主程序
package project_2.chatUtil.v1.server;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/*
* 服务器端的ChatServer
* 创建ServertSocket监听的主类
* */
public class ChatServer
{
public static int count=1;
//创建保存所有的客户Socket的ArryList并将其包装成线程安全的
public static List<Socket> socketList=Collections.synchronizedList(new ArrayList<Socket>());
//本程序的服务端口
public static final int SERVER_PORT=51289;
//本程序所在的主机
public static final String SERVER_HOST="127.0.0.1";
//主方法
public static void main(String[] args) throws IOException
{
//ServerSocket对象监听本机的SERVER_PORT端口
ServerSocket server=new ServerSocket(SERVER_PORT);
System.out.println("服务器启动............");
//采用循环监听
while(true)
{
//accept方法会一直阻塞线程,直到有Socket连接,才会执行下面的代码
Socket socket=server.accept();
//把连接进来的Socket存入集合
socketList.add(socket);
//每当连接成功一个Socket,就启动一个ServerThread为其服务
new Thread(new ServerThread(socket)).start();
}
}
}
2.服务器端线程类
package project_2.chatUtil.v1.server;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.Socket;
import javax.print.PrintService;
//负责每个Socket通信的线程类
public class ServerThread implements Runnable
{
//定义当前线程所服务的Socket
Socket socket=null;
//该Socket所对应的输入流
BufferedReader br=null;
//构造方法,初始化当前线程的Socket和输入流
public ServerThread(Socket socket) throws IOException
{
this.socket=socket;
//通过包装,获取带缓冲的字符输入流
br=new BufferedReader(new InputStreamReader(socket.getInputStream()));
}
//获取客户端数据的方法
private String readFromClient()
{
try
{
return br.readLine();
}
// 如果捕捉到异常,则表示该客户已经离开
catch (IOException e)
{
//从集合中删除此客户
ChatServer.socketList.remove(socket);
}
return null;
}
@Override
public void run()
{
try
{
String content=null;
System.out.println("开启一条新的服务线程!!");
//采用循环,不断从Socket读取客户发送来的数据
while((content=readFromClient())!=null)
{
for(Socket s : ChatServer.socketList)
{
//获取此Socket的输出流
PrintStream ps=new PrintStream(s.getOutputStream());
//向此Socket输出广播的消息
ps.println(content);
}
}
}
catch (IOException e)
{
System.out.println("服务器端消息广播异常!");
}
}
}
3.客户端主程序
package project_2.chatUtil.v1.client;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.Socket;
import java.net.UnknownHostException;
import project_2.chatUtil.v1.server.ChatServer;
/**
* 客户端
*/
public class ChatClient
{
public static void main(String[] args)
throws UnknownHostException, IOException
{
//创建连接到服务器的Socket
Socket s=new Socket(ChatServer.SERVER_HOST,ChatServer.SERVER_PORT);
//客户端启动客户端线程不断读取服务器发来的数据
new Thread(new ClientThread(s)).start();
//获取该Socket对应的输出流
PrintStream ps=new PrintStream(s.getOutputStream());
String line=null;
//不断读取键盘输入
BufferedReader br=new BufferedReader(new InputStreamReader(System.in));
System.out.println("请输入您的用户名");
String userName="用户 [NAME:"+br.readLine()+",ID:"+System.currentTimeMillis()+"] 说:\t";
System.out.println("下面就开始广播消息吧!如需推出,请输入: @EXIT$ ");
while((line=br.readLine())!=null)
{
//将客户向键盘输入的内容不断写入Socket的输出流
ps.println(userName+line);
if(line.endsWith("@EXIT$"))
{
System.out.println("拜拜!");
System.exit(1);
}
}
}
}
4.客户端线程类
package project_2.chatUtil.v1.client;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Socket;
/*客户端线程类
* 负责读取数据并将其在控制台打印出来*/
public class ClientThread implements Runnable
{
//该线程负责处理的Socket
Socket socket=null;
//该线程负责处理的Socket对应的输入流
BufferedReader br=null;
public ClientThread(Socket socket) throws IOException
{
this.socket=socket;
//通过包装,获取带缓冲的字符输入流
br=new BufferedReader(new InputStreamReader(socket.getInputStream()));
}
@Override
public void run()
{
try
{
//用于存放读取的内容
String content=null;
//采用循环,不断读取Socket输入流中的内容
while((content=br.readLine())!=null)
{
System.out.println(content);
}
}
catch (IOException e)
{
System.out.println("客户端读取数据异常!");
}
}
}