TCP主要需要两个类:Socket和ServerSocket,Socket是客户端连接服务器时创建,参数需要指定服务器的ip和端口,ServerSocket是服务器端创建,参数指定端口,如下:
Socket socket = new Socket("localhost",8888);//Client.java客户端 ServerSocket serverSocket = new ServerSocket(8888);//Server.java服务器端 //服务器ip为本机,端口为8888
我的想法:要实现单聊和群聊,首先,我得为标识每个客户端,我选择姓名,这样每个用户都知道是谁发的消息,那么,我需要每个姓名对应一个客户端Socket,用map存储
废话不多说,直接上代码:
1. 客户端:
```java public class TcpClient { public static void main(String[] args) throws Exception { Scanner scanner = new Scanner(System.in); System.out.print("请输入用户名》》"); String userName = scanner.next(); Socket socket = new Socket("localhost",8888); DataOutputStream out = new DataOutputStream(socket.getOutputStream()); out.writeUTF(userName); new Thread(new SendMsg(socket)).start(); new Thread(new ReceiveMsg(socket)).start(); } } ```
2. 客户端发送消息线程:
public class SendMsg implements Runnable{ private Socket socket; public SendMsg(Socket socket) { this.socket = socket; } @Override public void run() { try { while(true) { BufferedReader buffer = new BufferedReader(new InputStreamReader(System.in)); String[] msg = buffer.readLine().split("@"); MessageBean messageBean = new MessageBean(msg[0],msg[1]); // DataOutputStream out = new DataOutputStream(socket.getOutputStream()); // out.writeUTF(msg); ObjectOutputStream out = new ObjectOutputStream(socket.getOutputStream()); out.writeObject(messageBean); } } catch (Exception e) { e.printStackTrace(); } } }
3. 客户端接收消息线程:
public class ReceiveMsg implements Runnable{ private Socket socket; public ReceiveMsg(Socket socket) { this.socket = socket; } @Override public void run() { try { while(true) { DataInputStream in = new DataInputStream(socket.getInputStream()); System.out.println(in.readUTF()); } } catch (Exception e) { e.printStackTrace(); } } }
4. 服务器端:
public class TcpServer { public static void main(String[] args) throws Exception { try { //List<Socket> sockets = new ArrayList<>(); Map<String,Socket> map = new HashMap<>(); ServerSocket serverSocket = new ServerSocket(8888); while(true) { Socket socket = serverSocket.accept(); //服务器第一次接收到的信息一定是用户名 DataInputStream in = new DataInputStream(socket.getInputStream()); String username = in.readUTF(); synchronized (map) { map.put(username, socket); } //能够保证在同一时刻最多只有一个线程执行该段代码,以达到保证并发安全的效果 // synchronized (sockets) { // sockets.add(socket); // } new Thread(new ServerHandler(socket,map)).start(); } } catch (Exception e) { e.printStackTrace(); } } }
5. 服务器处理事务线程端:
public class ServerHandler implements Runnable { private Socket socket; private Map<String, Socket> map; public ServerHandler(Socket socket,Map<String, Socket> map) { this.socket = socket; this.map = map; } public String findKey(Map<String, Socket> map,Socket socket) { Iterator<String> it = map.keySet().iterator(); while (it.hasNext()) { String key = it.next(); if (map.get(key).equals(socket)) return key; } return null; } @Override public void run() { InetAddress address = socket.getInetAddress(); String ip = address.getHostAddress(); String username = findKey(map, socket); System.out.println("ip为"+ip+"的"+username+":上线了----"); try { while(true) { ObjectInputStream in = new ObjectInputStream(socket.getInputStream()); MessageBean messageBean = (MessageBean) in.readObject(); String msg = messageBean.getMsg(); String toPerson = messageBean.getToPerson(); if ("all".equals(toPerson)) { //群聊 msg = username+":"+msg; for(String key : map.keySet()){ if (username.equals(key)) { continue; } DataOutputStream out = new DataOutputStream(map.get(key).getOutputStream()); out.writeUTF(msg); } }else { //私聊 if (map.get(toPerson) == null) { DataOutputStream out = new DataOutputStream(map.get(username).getOutputStream()); out.writeUTF("抱歉,"+toPerson+"不在线。。。。"); }else { msg = username+"对您私聊说:"+msg; DataOutputStream out = new DataOutputStream(map.get(toPerson).getOutputStream()); out.writeUTF(msg); } } } } catch (Exception e) { //e.printStackTrace(); System.out.println(username+"下线了。。。。。"); synchronized (map) { map.remove(username, socket); } } } }
6. 消息体类:
public class MessageBean implements Serializable{ private String msg; private String toPerson; public MessageBean() { } public MessageBean(String msg,String toPerson) { this.msg = msg; this.toPerson = toPerson; } public String getMsg() { return msg; } public String getToPerson() { return toPerson; } public void setMsg(String msg) { this.msg = msg; } public void setToPerson(String toPerson) { this.toPerson = toPerson; } }
运行结果: