мирской скиталец: Программисты, занимающиеся техническими исследованиями.
Говоря о предыдущем
Теоретическая база без реальных боевых дел - это все хулиганство, поэтому сегодня я в основном хочу, чтобы все углубили свое понимание прошлого через дела здесь
В этом разделе мы шаг за шагом реализуем апплет однорангового чата.
Реализация сокета в Java
InetAddress
InetAddress
Это инкапсуляция IP-адресов в Java. Этот класс является базовым классом.ServerSocket
а такжеDatagramSocket
Не могу без этого класса
InetAddress
не могу пройтиnew
Способ инициализации может быть вызван только путем предоставления статического метода, который он предоставляет:
// 获取本地地址
InetAddress localHost = InetAddress.getLocalHost();
вотInetAddress
Некоторые методы:
// 主机名:DESKTOP-ATG4KKE
System.out.println("主机名:" + localHost.getHostName());
// IP地址:192.168.87.1
System.out.println("IP地址:" + localHost.getHostAddress());
// 是否正常:true
System.out.println("是否正常:" + localHost.isReachable(5000));
Вот результат, когда я тестирую,
оisReachable()
метод, чтобы определить, доступен ли адрес, поэтому мы можем выполнить некоторые операции проверки работоспособности, такие как:
// 通过主机IP或者域名来得到InetAddress对象
InetAddress inetAddress = InetAddress.getByName("192.168.87.139");
System.out.println("是否正常:" + inetAddress.isReachable(5000));
Старайтесь подключаться к хосту как можно чаще в течение 5 с. Если нет, хост считается недоступным. Это ограниченомежсетевой экрана такжеконфигурация сервера
Конечно, метод проверки работоспособности все еще немного низок, и он определенно не будет выполняться в производственной среде.
PS: Сетевые операции в производственной среде не будут использовать вещи из этого раздела.В большинстве случаевNetty
ServerSocket
ServerSocket
это серверный сокет, основанный наTCP/IP
Достигнуто по соглашению
инициализация
Обычно мы строим так:
ServerSocket serverSocket = new ServerSocket(9999);
ServerSocket serverSocket = new ServerSocket();
serverSocket.bind(new InetSocketAddress(9999));
На этом инициализация сервера завершена, и порт9999
связывать
ожидание соединения
Если клиент хочет иServerSocket
Чтобы установить соединение, нам нужно сделать это
for(;;) {
Socket socket = serverSocket.accpet();
// Socket[addr=/0:0:0:0:0:0:0:1,port=62445,localport=9999]
System.out.println(socket);
}
accpet()
слушает сServerSocket
Установленное соединение, этот метод является методом блокировки и всегда будет ждать установления соединения.
Если есть входящее соединение, мы можем получить текущий доступ через возвращаемое значениеSocket
коммуникация
Передача данных в сети фактически соответствуетIO
Способ передать поток, но мы можем получить только поток байтов:
InputStream inputStream = socket.getInputStream();
OutputStream outputStream = socket.getOutputStream();
InputStream
читать данные,OutputStream
Выписывайте данные. Мы ввели эти основные операции в предыдущем потоке IO, поэтому я больше не скажу здесь.
Здесь для повышения эффективности можно использовать包装流
или处理流
чтобы справиться с этим, который также был введен ранее
Полный небольшой пример
Собственно вот,ServerSocket
Ключевое введение закончено, давайте сделаем небольшой пример:
- Когда клиент подключается, вернитесь к клиенту:
Hello World
public class _ServerSocket {
// 用来存储请求客户端和Socket之间的对应关系
static Map<String, Socket> MAP = new HashMap<>();
public static void main(String[] args) {
try {
ServerSocket serverSocket = new ServerSocket();
serverSocket.bind(new InetSocketAddress(9999));
for (; ; ) {
String token = UUID.randomUUID().toString().replace("-", "").toLowerCase();
Socket socket = serverSocket.accept();
// 对应
MAP.put(token, socket);
outHtml(socket);
}
} catch (IOException e) {
e.printStackTrace();
}
}
public static void outHtml(Socket socket) {
OutputStream outputStream = null;
try {
outputStream = socket.getOutputStream();
outputStream.write(("HTTP/1.1 200 OK\n\nHello World").getBytes("UTF-8"));
} catch (IOException e) {
e.printStackTrace();
} finally {
if (null != outputStream) {
try {
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
HTTP/1.1 200 OK\n\nHello World\n
Это тип возвращаемого значения в протоколе HTTP. Фронтальная часть представляет собой фиксированный формат ответа.
Hello World
то, что на самом деле возвращается, так что нашServerSocket
можно получить через браузер
Socket
Socket
Он принадлежит клиентскому сокету, и другие операции можно выполнять, только предварительно установив соединение с серверным сокетом.Socket
очень прост в использовании
установить соединение
Socket socket = new Socket("127.0.0.1", 9999);
// 验证是否连接成功
if (socket.isConnected()) {
System.out.println("到服务端连接成功");
}
Это один из способов построения, чаще так
После того, как соединение с сервером будет успешно установлено, последующие операции аналогичныServerSocket
из通信步骤
Это то же самое, здесь больше нет ерунды
Вот полный пример для консолидации
Пример: одноранговый чат TCP
Сервер
public class Server {
/**
* 将客户端标识和socket关联起来
*/
private static final Map<String, Socket> SOCKET_MAP = new HashMap<>();
/**
* 反向关联,用来获取标识
*/
private static final Map<Socket, String> SOCKET_TOKEN_MAP = new HashMap<>();
public static void main(String[] args) throws IOException {
/**
* 开启ServerSocket并监听9999端口
*/
ServerSocket serverSocket = new ServerSocket(9999);
for (;;) {
/**
* 等待客户端连接
*/
Socket socket = serverSocket.accept();
/**
* IO读取是阻塞式方法,所以需要开启新线程,这里可以优化成线程池
*/
new Thread(() -> {
try {
saveToMap(socket);
getClientMsg(socket);
} catch (IOException e) {
e.printStackTrace();
}
}).start();
}
}
/**
* 绑定SOCKET
*/
private static void saveToMap(Socket socket) throws IOException {
String token = StringUtil.uuid();
SOCKET_MAP.put(token, socket);
SOCKET_TOKEN_MAP.put(socket, token);
System.out.println("---客户端连接成功,编号:" + token);
System.out.println("当前用户:" + SOCKET_MAP.size());
/**
* 因为没有登录,所以这里要告知客户端自己的标识
*/
send(token, token, token);
}
/**
* 获取客户端发送过来的消息,并发送出指定指定的客户端
*/
private static void getClientMsg(Socket socket) throws IOException {
BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
String line = "";
while ((line = reader.readLine()) != null) {
// 读取到一行以后,从这里发送出去
send(socket, line);
}
}
/**
* 发送消息
*/
private static void send(Socket socket, String line) throws IOException {
String[] s = line.split("#");
final String from = SOCKET_TOKEN_MAP.get(socket);
send(s[0], s[1], from);
}
/**
* 发送消息
* @param token
* @param msg
* @param from 这里在目标客户端展示
* @throws IOException
*/
private static void send(String token, String msg, String from) throws IOException {
Socket sk = SOCKET_MAP.get(token);
if (null == sk)
return;
String s = from + ":" + msg;
System.out.println("---发送给客户端:" + s );
// 字符流输出
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(sk.getOutputStream()));
writer.write(s);
writer.newLine();
writer.flush();
}
}
клиент
public class Client {
public static void main(String[] args) throws IOException {
/**
* 连接到服务端
*/
Socket socket = new Socket("127.0.0.1", 9999);
/**
* 开新线程读取消息,可以优化
*/
new Thread(() -> {
try {
BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
String line = "";
while (StringUtil.isNotBlank(line = reader.readLine())) {
System.out.println(line);
}
} catch (IOException e) {
e.printStackTrace();
}
}).start();
/**
* 从控制台写入消息并发送出去
*/
Scanner scanner = new Scanner(System.in);
while (scanner.hasNext()) {
String next = scanner.next();
send(next, socket);
}
}
private static void send(String msg, Socket socket) throws IOException {
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
writer.write(msg);
writer.newLine();
writer.flush();
}
}
Код прошел проверку, комментарии очень понятные, можете попробовать.标识#消息
формат может быть点对点聊天
.
Если хочешь群聊
:
- Сохраните Socket в коллекцию, а затем зациклите коллекцию, это очень просто
давно бесполезно
Socket
Я написал программу для чата и чуть не сдалсяиспользовать в следующий раз
Netty
написать,Netty
СравниватьSocket
намного удобнее
DatagramSocket
DatagramSocket
сокет для отправки и получения пакетов дейтаграмм, основанный наUDP协议
реализация ниже. Согласно официальному представлению в классе:
Сокет дейтаграммы — это точка отправки или получения для службы доставки пакетов. Каждый пакет, отправленный или полученный через сокет дейтаграммы, адресуется и маршрутизируется индивидуально. Несколько пакетов, отправленных с одной машины на другую, могут маршрутизироваться по-разному и могут поступать в любом порядке.
мы также можем понятьUDP协议
характеристики.
DatagramPacket
Этот класс представляет数据报包
,существуетDatagramSocket
Этот класс используется для передачи и получения данных, таких как:
- Получить данные
byte[] buffer = new byte[1024];
DatagramPacket p = new DatagramPacket(buffer, buffer.length);
- отправить данные
DatagramPacket p = new DatagramPacket("123".getBytes(), "123".getBytes().length, InetAddress.getByName("localhost"), 9999);
отправить данные,DatagramPacket
Вам нужно указать IP и порт принимающей стороны, чтобы вы могли отправить его
Давайте посмотрим, как использовать
инициализация
DatagramSocket socket = new DatagramSocket(9999);
DatagramSocket s = new DatagramSocket(null);
s.bind(new InetSocketAddress(9999));
Инициализировать можно обоими способами, разницы нет
получить сообщение
byte[] buffer = new byte[1024];
DatagramPacket p = new DatagramPacket(buffer, buffer.length);
socket.receive(p);
System.out.println(new String(p.getData(), 0, p.getLength()));
согласно сDatagramPacket
Получить параметры, построитьbyte[]
, а затем позвонитеreceive()
, так что сообщение получено
receive()
Это метод блокировки, и он будет продолжать выполняться только при наличии сообщения.
отправлять сообщения
DatagramPacket p = new DatagramPacket("123".getBytes(), "123".getBytes().length, InetAddress.getByName("localhost"), 9999);
socket.send(p);
Создайте пакет отправки, затем вызовитеsend()
Метод может завершить передачу пакета данных
UDP не требует подключения, данные можно отправлять напрямую через IP+PORT
Кейс: UDP-чат
public class _DatagramPacket {
public static void main(String[] args) throws IOException {
// 从命令行得到需要绑定的端口和发送数据的端口
DatagramSocket datagramSocket = new DatagramSocket(Integer.parseInt(args[0]));
System.out.println("已启动");
new Thread(() -> {
byte[] buffer = new byte[1024];
DatagramPacket p = new DatagramPacket(buffer, buffer.length);
try {
for (;;) {
// 构建接收数据
datagramSocket.receive(p);
System.out.println(p.getPort() + ":" + new String(buffer, 0, p.getLength()));
}
} catch (IOException e) {
e.printStackTrace();
}
}).start();
Scanner scanner = new Scanner(System.in);
DatagramPacket p = new DatagramPacket(new byte[0], 0, new InetSocketAddress("127.0.0.1", Integer.parseInt(args[1])));
while (scanner.hasNext()) {
String next = scanner.next();
// 构建发送数据包
p.setData(next.getBytes());
datagramSocket.send(p);
}
}
Есть недостатки, пробелы будут перенесены, и я оставлю их здесь, чтобы вы могли их изменить.
последние слова
здесь, оSocket编程
Мы закончили говорить об аспектах, и методов API введено не так много, они одинаковы при использовании.
Ниже приведеныjava.net
Каталог, в котором находится документ: