1:服务器端通过java.net.ServerSocket类的构造方法实例化ServerSocket对象,选择的构造方法是:public ServerSocket(int port) throws IOException //创建绑定到指定端口的服务器套接字
2:客户端通过java.net.Socket类的构造方法创建一个流套接字并将其连接到指定主机上的指定端口号,选择的方法是:public Socket(String host,int port) throws UnknowHostException,IOException
3:服务器端的ServerSocket对象调用accept()方法监听请求连接指定端口号的该服务器端的客户端,并在接收到客户端请求后返回服务器端的流套接字,即Socket对象,从而服务器端与客户端成功建立连接
4:客户端与服务器端之间的通信操作,java.net.Socket类就是提供客户端与服务器端相互通信的套接字
5:服务器端与客户端之间通信结束后,需要关闭套接字,调用close()方法即可
public void close() throws IOException
创建服务器端与客户端之间的连接以及通信操作
package chat.room.server;
import java.net.Socket;
import java.net.ServerSocket;
import java.util.Scanner;
import java.io.PrintStream;
import java.io.IOException;
public class SingleServer {
public static void main(String[] args) throws IOException{
//1.创建服务器端的ServerSocket对象,等待客户端连接其接口
ServerSocket serverSocket = new ServerSocket(6666);
System.out.println("服务器的端口号为:6666,正在等待客户端连接……");
//2.监听并接受服务器端的连接,返回套接字Socket对象
Socket socket = serverSocket.accept();
//3.获取客户端的输入流,读取客户端输入的内容
Scanner scanner = new Scanner(socket.getInputStream());
scanner.useDelimiter("\n");
if(scanner.hasNext()){
System.out.println("客户端发来信息"+scanner.next());
}
//4.获取客户端的输出流,向客户端输出内容
PrintStream printStream = new PrintStream(socket.getOutputStream());
printStream.println("客户端你好,我是服务器端:"+serverSocket.getLocalPort());
//5.关闭流
serverSocket.close();
}
}
package chat.room.server;
import java.net.Socket;
import java.io.IOException;
import java.io.PrintStream;
import java.util.Scanner;
public class SingleClient {
public static void main(String[] args) throws IOException{
//1.客户端连接服务器端,返回套接字Socket对象
Socket socket = new Socket("127.0.0.1",6666);
//2.获取服务器端的输出流,向服务器端输出内容
PrintStream printStream = new PrintStream(socket.getOutputStream());
printStream.println("我是客户端"+socket.getLocalPort());
//3.获取服务器端的输入流,读取服务器端的内容
Scanner scanner = new Scanner(socket.getInputStream());
scanner.useDelimiter("\n");
if(scanner.hasNext()){
System.out.println(scanner.next());
}
//4.关闭流
socket.close();
}
}
客户端只需要做两件事,所以创建两个线程:
客户端需要读取服务器端信息就需要客户端获取服务器端的输入流,从而将信息显示到客户端
//客户端读取服务器端信息的线程
class ClientReadServer implements Runnable{
private Socket socket;
public ClientReadServer(Socket socket){
this.socket = socket;
}
public void run(){
//获取服务器端输入流
Scanner scanner;
try {
scanner = new Scanner(socket.getInputStream());
while(scanner.hasNext()){
System.out.println(scanner.next());
}
scanner.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
客户端需要向服务器端发送信息就需要客户端获取服务器端的输出流,从而将信息发送到服务器端
class ClientSendServer implements Runnable{
private Socket socket;
public ClientSendServer(Socket socket){
this.socket = socket;
}
public void run(){
try {
//获取服务器端的输出流
PrintStream printStream = new PrintStream(socket.getOutputStream());
//从键盘输入信息
Scanner scanner = new Scanner(System.in);
while(true){
String msg = null;
if(scanner.hasNext()){
msg = scanner.next();
printStream.println(msg);
}
if(msg.equals("exit")){
scanner.close();
printStream.close();
break;
}
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
在客户端的主方法中,首先创建连接服务器端的Socket对象,然后通过Thread类的start()方法启动两个子线程,从而完成客户端的操作
public class MultiClient{
public static void main(String[] args) throws IOException{
//客户端连接服务器端,返回套接字Socket对象
Socket socket = new Socket("127.0.0.1",6666);
//创建读取服务器端信息的线程和发送服务器端信息的线程
Thread read = new Thread(new ClientReadServer(socket));
Thread send = new Thread(new ClientSendServer(socket));
//启动线程
read.start();
send.start();
}
}
private void userRegist(String userName,Socket socket){
map.put(userName,socket);
System.out.println("[用户名为"+userName+"][客户端为"+socket+"]上线了!");
System.out.println("当前在线人数为:"+map.size()+"人");
}
private void userExit(Socket socket){
//利用socket取得对应的key值
String userName = null;
for(String key:map.keySet()){
if(map.get(key).equals(socket)){
userName = key;
break;
}
}
//将userName,Socket元素从map集合中删除
map.remove(userName,socket);
//提醒服务器客户端已下线
System.out.println("用户:"+userName+"已下线");
}
private void privateChat(Socket socket,String userName,String msg) throws IOException{
//取得当前客户端的用户名
String curUser = null;
Set> set=map.entrySet();
for(Map.Entry entry:set){
if(entry.getValue().equals(socket)){
curUser = entry.getKey();
break;
}
}
//取得私聊用户名对应的客户端
Socket client = map.get(userName);
//获取私聊客户端的输出流,将私聊信息发送到指定客户端
PrintStream printStream = new PrintStream(client.getOutputStream());
printStream.println(curUser+"私聊说:"+msg);
}
private void groupChat(Socket socket,String mag) throws IOException{
//将Map集合转换为Set集合
Set> set = map.entrySet();
//遍历Set集合找到发起群聊信息的用户
String userName = null;
for(Map.Entry entry:set){
if(entry.getValue().equals(socket)){
userName = entry.getKey();
break;
}
}
//遍历Set集合将群聊信息发给每一个客户端
for(Map.Entry entry:set){
//取得客户端的Socket对象
Socket client = entry.getValue();
//取得client客户端的输出流
PrintStream printStream = new PrintStream(client.getOutputStream());
printStream.println(userName+"群聊说:"+msg);
}
}
只需调用ConcurrentHashMap对象的size()方法即可
在完成了所有的功能模块之后,首先需要创建服务器端的套接字ServerSocket对象,从而客户端能够进行连接。其次由于服务器端处理多个客户端时需要创建多个线程,故可创建固定大小的线程池处理多个客户端
public class MultiServer{
public static void main(String[] args){
try{
//1.创建服务器端的ServerSocket对象,等待客户端连接
ServerSocket serverSocket = new ServerSocket(6666);
//2.创建线程池,从而可以处理多个客户端
ExecutorService executorService = Executors.newFixedThreadPool(20);
for(int i=0;i<20;i++){
System.out.println("欢迎来到我的聊天室……");
//3.监听客户端
Socket socket = serverSocket.accept();
System.out.println("有新的朋友加入……");
//启动线程
executorServie.execute(new Server(socket));
}
//关闭线程池
executorService.shutdown();
//关闭服务器
serverSocket.close();
} catch(IOException e){
e.printStackTrace();
}
}
}