Java网络编程之TCP

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;
	}
}

运行结果:
Java网络编程之TCP_服务器
Java网络编程之TCP_客户端_02