·TCP协议的特点:

传输控制协议,提供的是面向连接、可靠的字节流服务。
当客户和服务器彼此交换数据前,必须先在双方之间建立一个TCP连接,之后才能传输数据。
TCP提供超时重发,丢弃重复数据,检验数据,流量控制等功能,保证数据能从一端传到另一端。

·聊天室原理:
Socket连接套接字,Java分别为TCP和UDP提供了相应的类,TCP是java.net.ServerSocket(用于服务器端)和java.net.Socket(用于客户端)

服务器端:

package com;
 import java.io.IOException;
 import java.net.ServerSocket;
 import java.net.Socket;
 public class Server {
 public static void main(String[] args) {
 ServerSocket server = null;
 ServerInfo si = new ServerInfo();
 //1.创建服务器,接收多个客户端的连接
 try {
 //创建服务器
 server = new ServerSocket(9999); 
 //3.把接收到的消息进行转发
 Thread tt = new TransmitThread(si);
 tt.setDaemon(true);
 tt.start();
 while(true) {
 //等待客户端连接
 Socket socket = server.accept();
 System.out.println(socket.getInetAddress() + “连接成功!”);
 //2.创建线程处理客户端的功能
 //1.接收客户端的信息
 Thread t = new RunServerThread(socket, si);
 t.setDaemon(true);
 t.start();
 }
} catch (IOException e) {

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

}

package com;
 import java.io.BufferedReader;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.InputStreamReader;
 import java.net.Socket;
 public class RunServerThread extends Thread {
 private Socket socket;
 private ServerInfo serverInfo;
 public RunServerThread(Socket socket, ServerInfo serverInfo) {
 super();
 this.socket = socket;
 this.serverInfo = serverInfo;
 }
 @Override
 public void run() {
 InputStream is = null;
 InputStreamReader isr = null;
 BufferedReader br = null;
 User user = null;
 try {
 is = socket.getInputStream();
 isr = new InputStreamReader(is, “utf-8”);
 br = new BufferedReader(isr);
 // 接收用户名
 String name = br.readLine();
 user = new User(name, socket);
 serverInfo.getUsers().add(user);
 UserMessage um = new UserMessage(user, user.getName() + “加入了聊天室!”);
 serverInfo.getMessages().add(um);
 synchronized (serverInfo) {
 serverInfo.notify();
 }
 //通知某用户加入聊天室
 String message = null;
 while (true) {
 message = br.readLine();
 if(“exit”.equals(message.toLowerCase())) {
 //输出某某下线了
 um = new UserMessage(new User(“系统”,null), user.getName() + “下线了”);
 serverInfo.getMessages().add(um);
 synchronized (serverInfo) {
 serverInfo.notify();
 }
 serverInfo.getUsers().remove(user);
 break;
 }
 //把消息发送
 um = new UserMessage(user, message);
 serverInfo.getMessages().add(um);
 //唤醒消息发送的线程
 synchronized (serverInfo) {
 serverInfo.notify();
 }
 }
 } catch (Exception e) {
 //退出处理
 UserMessage um = new UserMessage(new User(“系统”,null), user.getName() + “下线了”);
 serverInfo.getMessages().add(um);
 synchronized (serverInfo) {
 serverInfo.notify();
 }
 serverInfo.getUsers().remove(user);
 } finally {
 try {
 br.close();
 isr.close();
 is.close();
 socket.close();
 } catch (IOException e) {
 // TODO Auto-generated catch block
 e.printStackTrace();
 }
 }
 }


}

package com;
 import java.util.ArrayList;
 public class ServerInfo {
 private ArrayList users;
 private ArrayList messages; 
 public ServerInfo() {
 this.users = new ArrayList();
 this.messages = new ArrayList();
 } 
 public ArrayList getUsers() {
 return users;
 } 
 public ArrayList getMessages() {
 return messages;
 }
 }
package com;
 import java.io.IOException;
 import java.io.OutputStream;
 import java.io.OutputStreamWriter;
 import java.net.Socket;
 //转发消息
 public class TransmitThread extends Thread {
 private ServerInfo si;
 public TransmitThread(ServerInfo si) {
 super();
 this.si = si;
 }
 @Override
 public void run() {
 while(true) {
 if(si.getMessages().size() > 0) {
 //取出第一个消息
 UserMessage um = si.getMessages().remove(0);
 for (int i = 0; i < si.getUsers().size(); i++) {
 if(si.getUsers().get(i) != um.getUser()) {
 Socket socket = si.getUsers().get(i).getSocket();
 OutputStream os = null;
 OutputStreamWriter osw = null;
 try {
 os = socket.getOutputStream();
 osw = new OutputStreamWriter(os,“utf-8”);
 osw.write(um.getUser().getName() + “:” +um.getMessage() + “\n”);
 osw.flush();
 } catch (IOException e) {
 // TODO Auto-generated catch block
 e.printStackTrace();
 }
 }
 }
 }else {
 try {
 synchronized (si) {
 si.wait();
 } 
 } catch (InterruptedException e) {
 // TODO Auto-generated catch block
 e.printStackTrace();
 }
 }
 }
 }
 }
package com;
 import java.net.Socket;
 public class User {
 private String name;
 private Socket socket;
 public User() {
 // TODO Auto-generated constructor stub
 }
 public User(String name, Socket socket) {
 super();
 this.name = name;
 this.socket = socket;
 }
 public String getName() {
 return name;
 }
 public void setName(String name) {
 this.name = name;
 }
 public Socket getSocket() {
 return socket;
 }
 public void setSocket(Socket socket) {
 this.socket = socket;
 }
 }
package com;
 public class UserMessage {
 private User user;
 private String message;
 public UserMessage(User user, String message) {
 super();
 this.user = user;
 this.message = message;
 }
 public User getUser() {
 return user;
 }
 public void setUser(User user) {
 this.user = user;
 }
 public String getMessage() {
 return message;
 }
 public void setMessage(String message) {
 this.message = message;
 }
 }


客户端:

package com;
 import java.io.IOException;
 import java.io.OutputStream;
 import java.io.OutputStreamWriter;
 import java.net.Socket;
 import java.util.Scanner;
 public class Client {
 public static void main(String[] args) {
 // TODO Auto-generated method stub
 Scanner scanner = new Scanner(System.in); 
 Socket socket = null;
 OutputStream os = null;
 OutputStreamWriter osw = null;
 try {
 socket = new Socket(“192.168.5.254”,9999);
Thread thread = new ReadMessageThread(socket);
		thread.setDaemon(true);
		thread.start();		
		os = socket.getOutputStream();
		osw = new OutputStreamWriter(os,"utf-8");
		//发送用户名
		System.out.print("请输入您的聊天昵称:");
		String name = scanner.nextLine();
		osw.write(name + "\n");
		osw.flush();
		String message = null;
		//发送消息
		while(true) {
			System.out.print(">");
			message = scanner.nextLine();
			osw.write(message+"\n");
			osw.flush();
			if("exit".equals(message.toLowerCase())) {
				break;
			}
		}
	} catch (IOException e) {
		// TODO Auto-generated catch block
		e.printStackTrace();
	}finally {
		try {
			osw.close();
			os.close();
			socket.close();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}

}

package com;
 import java.io.BufferedReader;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.InputStreamReader;
 import java.net.Socket;
 public class ReadMessageThread extends Thread {
 private Socket socket = null;
 public ReadMessageThread(Socket socket) {
 super();
 this.socket = socket;
 }
 @Override
 public void run() {
 InputStream is = null;
 InputStreamReader isr = null;
 BufferedReader br = null;
 try {
 is = socket.getInputStream();
 isr = new InputStreamReader(is, “utf-8”);
 br = new BufferedReader(isr);
 // 通知某用户加入聊天室
 String message = null;
 while (true) {
 message = br.readLine();
 System.out.println(message);
 }
 } catch (IOException e) {
 // 退出处理
 } finally {
 try {
 br.close();
 isr.close();
 is.close();
 } catch (IOException e) {
 // TODO Auto-generated catch block
 e.printStackTrace();
 }
 }
 }
 }


·类Socket介绍:

客户端要与服务器建立连接,必须先创建一个Socket对象,它的常用构造方法有: 
  Socket(String host, int port):创建一个流套接字并将其连接到指定主机上的指定端口号。 
Socket(InetAddress address, int port):创建一个流套接字并将其连接到指定 IP 地址的指定端口号。 
Socket(InetAddress address, int port, InetAddress localAddr, int localPort):创建一个套接字并将其连接到指定远程端口上的指定远程地址。 
Socket(String host, int port, InetAddress localAddr, int localPort):创建一个套接字并将其连接到指定远程主机上的指定远程端口。 
对于通常情况的应用,使用第1个构造方法来创建客户端的Socket对象,并与服务器建立连接,是非常简单和方便的.
服务器端程序调用ServerSocket.accept方法等待客户端的连接请求,一旦accept接收了客户端连接请求,该方法返回一个与该客户端建立了专线连接的Socket对象,不用程序去创建这个Socket对象.建立了连接的两个Socket是以IO流的方式进行数据交换的,Java提供了Socket.getInputStream返回Socket的输入流对象,Socket.getOutputStream返回Socket的输出流对象.