Socket又称套接字 ,套接字使用TCP提供了两台计算机之间的通信机制。
客户端程序创建一个套接字,并尝试连接服务器的套接字。
当连接建立时,服务器会创建一个 Socket 对象。客户端和服务器现在可以通过对 Socket 对象的写入和读取来进行
通信。ServerSocket类为服务器提供了一种监听客户端并与他们建立连接的机制。
原理上很简单,就是分别开启两个线程,一个作为服务端,一个作为客户端。
客户端作用主要是监听端口上的信息,并以字节流读取并显示。
同时在Server类里面,还需要同时开启 监听和发送信息的线程。每连上一个客户端,就需要对应开启一个监听信息的线程。
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.util.Scanner;
public class Client {
Socket socket;
private static final int MESSAGE_SIZE = 1024;//每次允许接受数据的最大长度
public static void main(String[] args) {
new Client();
}
public Client() {
try {
//监听本地45678端口
socket = new Socket("127.0.0.1", 45678);
if (socket.isConnected() == true) {
System.out.println("连接成功");
new Thread() {//开启一个接受数据的线程
@Override
public void run() {
super.run();
InputStream in;
try {
in = socket.getInputStream();
byte[] b;
while (true) {
b = new byte[MESSAGE_SIZE];
in.read(b);
System.out.println("接收到服务端消息:" + new String(b));
}
} catch (IOException e) {
e.printStackTrace();
}
}
}.start();
}
OutputStream out = null;
while (true) {
Scanner scanner = new Scanner(System.in);
String str = scanner.nextLine();
out = socket.getOutputStream();
out.write(str.getBytes());
out.flush();
if (str.equals("end")) {
System.exit(0);//关闭客户端
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;
public class Server {
List<ReceiveThread> receiveList = new ArrayList<>();//存放已连接客户端类
private final static int MESSAGE_SIZE = 1024;//每次允许接受数据的最大长度
int num = 0;//客户端编号
public static void main(String[] args) {
new Server();
}
//服务端处理逻辑
Server() {
ServerSocket serverSocket = null;
try {
serverSocket = new ServerSocket(45678);//用来监听的套接字,指定端口号
while (true) {
Socket socket = serverSocket.accept();//监听客户端连接,阻塞线程
System.out.println("连接上客户端:" + num);
//在其他线程处理接收来自客户端的消息
ReceiveThread receiveThread = new ReceiveThread(socket, num);
receiveThread.start();
receiveList.add(receiveThread);
//有客户端新上线,服务器就通知其他客户端
String notice = "有新客户端上线,现在在线客户端有:客户端:";
for (ReceiveThread thread : receiveList) {
notice = notice + "" + thread.num;
}
for (ReceiveThread thread : receiveList) {
new SendThread(thread.socket, notice).start();
}
num++;
}
} catch (IOException e) {
e.printStackTrace();
}
}
//接受消息的线程(同时也有记录对应客户端socket的作用)
class ReceiveThread extends Thread {
int num;
Socket socket;//客户端对应的套接字
boolean continueReceive = true;//标识是否还维持连接需要接收
public ReceiveThread(Socket socket, int num) {
this.socket = socket;
this.num = num;
try {
//给连接上的客户端发送,分配的客户端编号的通知
socket.getOutputStream().write(("你的客户端编号是" + num).getBytes());
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void run() {
super.run();
//接收客户端发送的消息
InputStream inputStream = null;
try {
inputStream = socket.getInputStream();
byte[] b;
while (continueReceive) {
b = new byte[MESSAGE_SIZE];
inputStream.read(b);
b = splitByte(b);//去掉数组无用部分
//发送end的客户端断开连接
if (new String(b).equals("end")) {
continueReceive = false;
receiveList.remove(this);
//通知其他客户端
String message = "客户端" + num + "连接断开\n" +
"现在在线的有,客户端:";
for (ReceiveThread receiveThread : receiveList) {
message = message + " " + receiveThread.num;
}
System.out.println(message);
for (ReceiveThread receiveThread : receiveList) {
new SendThread(receiveThread.socket, message).start();
}
} else {
try {
String[] data = new String(b).split(" ", 2);//以第一个空格,将字符串分成两个部分
int clientNum = Integer.parseInt(data[0]);//转换为数字,即客户端编号数字
//将消息发送给指定客户端
for (ReceiveThread receiveThread : receiveList) {
if (receiveThread.num == clientNum) {
new SendThread(receiveThread.socket, "客户端" + num + "发消息:" + data[1]).start();
System.out.println("客户端" + num + "发送消息到客户端" + receiveThread.num + ": " + data[1]);
}
}
} catch (Exception e) {
new SendThread(socket, "输入错误,请重新输入").start();
System.out.println("客户端输入错误");
}
}
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {//关闭资源
if (inputStream != null) {
inputStream.close();
}
if (socket != null) {
socket.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
//发送消息的线程
class SendThread extends Thread {
Socket socket;
String str;
public SendThread(Socket socket, String str) {
this.socket = socket;
this.str = str;
}
@Override
public void run() {
super.run();
try {
socket.getOutputStream().write(str.getBytes());
} catch (IOException e) {
e.printStackTrace();
}
}
}
//去除byte数组多余部分
private byte[] splitByte(byte b[]) {
int i = 0;
for (; i < b.length; i++) {
if (b[i] == 0) {
break;
}
}
byte[] b2 = new byte[i];
for (int j = 0; j < i; j++) {
b2[j] = b[j];
}
return b2;
}
}