1、网络通信

IP地址:设备在网络中的地址,是唯一的标识。

端口:应用程序在设备中唯一的标识。

协议:   数据在网络中传输的规则,常见的协议有UDP协议和TCP协议。

InetAddress 的使用

此类表示Internet协议(IP)地址。

名称

说明

public static InetAddress getLocalHost()

返回本主机的地址对象

public static InetAddress getByName​(String host)

得到指定主机的IP地址对象,参数是域名或者IP地址

public String getHostName​()

获取此IP地址的主机名

public String getHostAddress​()

返回IP地址字符串

public boolean isReachable(int timeout)

在指定毫秒内连通该IP地址对应的主机,连通返回true

import java.net.InetAddress;
/**目标:InetAddress类概述(了解)一个该类的对象就代表一个IP地址对象。InetAddress类成员方法:static InetAddress getLocalHost()* 获得本地主机IP地址对象。static InetAddress getByName(String host)* 根据IP地址字符串或主机名获得对应的IP地址对象。String getHostName()* 获得主机名。String getHostAddress()* 获得IP地址字符串。*/
public class InetAddressDemo01 {public static void main(String[] args) throws Exception {// 1.获取本机地址对象。InetAddress ip1 = InetAddress.getLocalHost();System.out.println(ip1.getHostName());System.out.println(ip1.getHostAddress());// 2.获取域名ip对象InetAddress ip2 = InetAddress.getByName("www.baidu.com");System.out.println(ip2.getHostName());System.out.println(ip2.getHostAddress());// 3.获取公网IP对象。InetAddress ip3 = InetAddress.getByName("112.80.248.76");System.out.println(ip3.getHostName());System.out.println(ip3.getHostAddress());// 4.判断是否能通: ping  5s之前测试是否可通System.out.println(ip3.isReachable(5000));}
}

网络通信协议有两套参考模型

OSI参考模型:世界互联协议标准,全球通信规范,由于此模型过于理想化,未能在因特网上进行广泛推广。
TCP/IP参考模型(或TCP/IP协议):事实上的国际标准。

OSI参考模型

TCP/IP参考模型

各层对应

面向操作

应用层

应用层

HTTP、FTP、DNS、SMTP…

应用程序需要关注的:浏览器,邮箱。程序员一般在这一层开发

表示层

会话层

传输层

传输层

TCP、UDP…

选择使用的TCP , UDP协议

网络层

网络层

IP、ICMP…

封装源和目标IP,进行路径选择

数据链路层

数据链路层+物理

物理寻址、比特流…

物理设备中传输

物理层

传输层的2个常见协议

TCP(Transmission Control Protocol) :传输控制协议
UDP(User Datagram Protocol):用户数据报协议

TCP协议特点

使用TCP协议,必须双方先建立连接,它是一种面向连接的可靠通信协议。
传输前,采用“三次握手”方式建立连接,所以是可靠的 。
在连接中可进行大数据量的传输 。
连接、发送数据都需要确认,且传输完毕后,还需释放已建立的连接,通信效率较低。

UDP协议: Ø

UDP是一种无连接、不可靠传输的协议。
将数据源IP、目的地IP和端口封装成数据包,不需要建立连接
每个数据包的大小限制在64KB内
发送不管对方是否准备好,接收方收到也不确认,故是不可靠的
可以广播发送 ,发送数据结束时无需释放资源,开销小,速度快。

2、UDP通信

UDP是一种无连接、不可靠传输的协议。
将数据源IP、目的地IP和端口以及数据封装成数据包,大小限制在64KB内,直接发送出去即可。

DatagramPacket:数据包对象(韭菜盘子)

构造器

说明

public DatagramPacket(byte[] buf, int length, InetAddress address, int port)

创建发送端数据包对象

buf:要发送的内容,字节数组

length:要发送内容的字节长度

address:接收端的IP地址对象

port:接收端的端口号

public DatagramPacket(byte[] buf, int length)

创建接收端的数据包对象

buf:用来存储接收的内容

length:能够接收内容的长度

DatagramPacket常用方法

方法

说明

public int getLength()

获得实际接收到的字节个数

DatagramSocket发送端和接收端对象(人)

构造器

说明

public DatagramSocket()​

创建发送端的Socket对象,系统会随机分配一个端口号。

public DatagramSocket(int port)

创建接收端的Socket对象并指定端口号

DatagramSocket类成员方法

方法

说明

public void send(DatagramPacket dp)

发送数据包

public void receive(DatagramPacket p)

接收数据包

import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;/**发送端  一发 一收*/
public class ClientDemo1 {public static void main(String[] args) throws Exception {System.out.println("=====客户端启动======");// 1、创建发送端对象:发送端自带默认的端口号(人)DatagramSocket socket = new DatagramSocket(6666);// 2、创建一个数据包对象封装数据(韭菜盘子)/**public DatagramPacket(byte buf[], int length,InetAddress address, int port)参数一:封装要发送的数据(韭菜)参数二:发送数据的大小参数三:服务端的主机IP地址参数四:服务端的端口*/byte[] buffer = "我是一颗快乐的韭菜,你愿意吃吗?".getBytes();DatagramPacket packet = new DatagramPacket( buffer, buffer.length,InetAddress.getLocalHost() , 8888);// 3、发送数据出去socket.send(packet);socket.close();}
}
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;/**接收端*/
public class ServerDemo2 {public static void main(String[] args) throws Exception {System.out.println("=====服务端启动======");// 1、创建接收端对象:注册端口(人)DatagramSocket socket = new DatagramSocket(8888);// 2、创建一个数据包对象接收数据(韭菜盘子)byte[] buffer = new byte[1024 * 64];DatagramPacket packet = new DatagramPacket(buffer, buffer.length);// 3、等待接收数据。socket.receive(packet);// 4、取出数据即可// 读取多少倒出多少int len = packet.getLength();String rs = new String(buffer,0, len);System.out.println("收到了:" + rs);// 获取发送端的ip和端口String ip  =packet.getSocketAddress().toString();System.out.println("对方地址:" + ip);int port  = packet.getPort();System.out.println("对方端口:" + port);socket.close();}
}

使用UDP通信实现:多发多收消息

需求

使用UDP通信方式开发接收端和发送端。

分析

①发送端可以一直发送消息。
②接收端可以不断的接收多个发送端的消息展示。
③发送端输入了exit则结束发送端程序。

发送端可以反复发送数据

需求:客户端实现步骤

①创建DatagramSocket对象(发送端对象)                      扔韭菜的人
②使用while死循环不断的接收用户的数据输入,如果用户输入的exit则退出程序
③如果用户输入的不是exit,  把数据封装成DatagramPacket                      韭菜盘子
④使用DatagramSocket对象的send方法将数据包对象进行发送                   开始抛出韭菜
⑤释放资源
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.MulticastSocket;
import java.util.Scanner;/**发送端  多发 多收*/
public class ClientDemo1 {public static void main(String[] args) throws Exception {System.out.println("=====客户端启动======");// 1、创建发送端对象:发送端自带默认的端口号(人)DatagramSocket socket = new DatagramSocket(7777);Scanner sc = new Scanner(System.in);while (true) {System.out.println("请说:");String msg = sc.nextLine();if("exit".equals(msg)){System.out.println("离线成功!");socket.close();break;}// 2、创建一个数据包对象封装数据(韭菜盘子)byte[] buffer = msg.getBytes();DatagramPacket packet = new DatagramPacket( buffer, buffer.length,InetAddress.getLocalHost() , 8888);// 3、发送数据出去socket.send(packet);}}
}

接收端可以反复接收数据

需求:接收端实现步骤

①创建DatagramSocket对象并指定端口(接收端对象)                      接韭菜的人
②创建DatagramPacket对象接收数据(数据包对象)                 韭菜盘子
③使用while死循环不断的进行第4步
④使用DatagramSocket对象的receive方法传入DatagramPacket对象                 开始接收韭菜
import java.net.DatagramPacket;
import java.net.DatagramSocket;/**接收端*/
public class ServerDemo2 {public static void main(String[] args) throws Exception {System.out.println("=====服务端启动======");// 1、创建接收端对象:注册端口(人)DatagramSocket socket = new DatagramSocket(8888);// 2、创建一个数据包对象接收数据(韭菜盘子)byte[] buffer = new byte[1024 * 64];DatagramPacket packet = new DatagramPacket(buffer, buffer.length);while (true) {// 3、等待接收数据。socket.receive(packet);// 4、取出数据即可// 读取多少倒出多少int len = packet.getLength();String rs = new String(buffer,0, len);System.out.println("收到了来自:" + packet.getAddress() +", 对方端口是" + packet.getPort() +"的消息:" + rs);}}
}

UDP的三种通信方式

单播:单台主机与单台主机之间的通信。

广播:当前主机与所在网络中的所有主机通信。

组播:当前主机与选定的一组主机的通信。

UDP如何实现广播

使用广播地址:255.255.255.255
具体操作:
①发送端发送的数据包的目的地写的是广播地址、且指定端口。 (255.255.255.255  ,   9999)
②本机所在网段的其他主机的程序只要注册对应端口就可以收到消息了。(9999)
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.util.Scanner;/**发送端  多发 多收*/
public class ClientDemo1 {public static void main(String[] args) throws Exception {System.out.println("=====客户端启动======");// 1、创建发送端对象:发送端自带默认的端口号(人)DatagramSocket socket = new DatagramSocket();Scanner sc = new Scanner(System.in);while (true) {System.out.println("请说:");String msg = sc.nextLine();if("exit".equals(msg)){System.out.println("离线成功!");socket.close();break;}// 2、创建一个数据包对象封装数据(韭菜盘子)byte[] buffer = msg.getBytes();// 注意:只要目的地IP是 255.255.255.255 这个消息将以广播的形式对外发送
//            DatagramPacket packet = new DatagramPacket( buffer, buffer.length,
//                    InetAddress.getByName("255.255.255.255") , 8888);DatagramPacket packet = new DatagramPacket( buffer, buffer.length,InetAddress.getByName("224.0.1.1") , 9898);// 3、发送数据出去socket.send(packet);}}
}

UDP如何实现组播

使用组播地址:224.0.0.0 ~ 239.255.255.255
具体操作:
①发送端的数据包的目的地是组播IP  (例如:224.0.1.1,  端口:9999)
②接收端必须绑定该组播IP(224.0.1.1),端口还要注册发送端的目的端口9999 ,这样即可接收该组播消息。
③DatagramSocket的子类MulticastSocket可以在接收端绑定组播IP。
import java.net.*;/**接收端*/
public class ServerDemo3 {public static void main(String[] args) throws Exception {System.out.println("=====服务端启动======");// 1、创建接收端对象:注册端口(人)MulticastSocket socket = new MulticastSocket(9898);// 注意:绑定组播地址(加群)socket.joinGroup(new InetSocketAddress(InetAddress.getByName("224.0.1.1") , 9898),NetworkInterface.getByInetAddress(InetAddress.getLocalHost()));// 2、创建一个数据包对象接收数据(韭菜盘子)byte[] buffer = new byte[1024 * 64];DatagramPacket packet = new DatagramPacket(buffer, buffer.length);while (true) {// 3、等待接收数据。socket.receive(packet);// 4、取出数据即可// 读取多少倒出多少int len = packet.getLength();String rs = new String(buffer,0, len);System.out.println("收到了来自:" + packet.getAddress() +", 对方端口是" + packet.getPort() +"的消息:" + rs);}}
}

3、TCP通信

TCP是一种面向连接,安全、可靠的传输数据的协议
传输前,采用“三次握手”方式,点对点通信,是可靠的

在连接中可进行大数据量的传输

Socket

构造器

说明

public Socket(String host , int port)​

创建发送端的Socket对象与服务端连接,参数为服务端程序的ip和端口。

Socket类成员方法

方法

说明

OutputStream getOutputStream()

获得字节输出流对象

InputStream getInputStream()

获得字节输入流对象

客户端发送消息

需求:客户端实现步骤

①创建客户端的Socket对象,请求与服务端的连接。
②使用socket对象调用getOutputStream()方法得到字节输出流。
③使用字节输出流完成数据的发送。
④释放资源:关闭socket管道。
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import java.net.Socket;/**目标:完成Socket网络编程入门案例的客户端开发,实现1发1收。*/
public class ClientDemo1 {public static void main(String[] args) {try {System.out.println("====客户端启动===");// 1、创建Socket通信管道请求有服务端的连接// public Socket(String host, int port)// 参数一:服务端的IP地址// 参数二:服务端的端口Socket socket = new Socket("127.0.0.1", 7777);// 2、从socket通信管道中得到一个字节输出流 负责发送数据OutputStream os = socket.getOutputStream();// 3、把低级的字节流包装成打印流PrintStream ps = new PrintStream(os);// 4、发送消息ps.println("我是TCP的客户端,我已经与你对接,并发出邀请:约吗?");ps.flush();// 关闭资源。// socket.close();} catch (Exception e) {e.printStackTrace();}}
}

ServerSocket(服务端)

构造器

说明

public ServerSocket(int port)

注册服务端端口

ServerSocket类成员方法

方法

说明

public Socket accept()

等待接收客户端的Socket通信连接

连接成功返回Socket对象与客户端建立端到端通信

服务端实现接收消息

需求:服务端实现步骤

①创建ServerSocket对象,注册服务端端口。
②调用ServerSocket对象的accept()方法,等待客户端的连接,并得到Socket管道对象。
③通过Socket对象调用getInputStream()方法得到字节输入流、完成数据的接收。
④释放资源:关闭socket管道
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;/**目标:开发Socket网络编程入门代码的服务端,实现接收消息*/
public class ServerDemo2 {public static void main(String[] args) {try {System.out.println("===服务端启动成功===");// 1、注册端口ServerSocket serverSocket = new ServerSocket(7777);// 2、必须调用accept方法:等待接收客户端的Socket连接请求,建立Socket通信管道Socket socket = serverSocket.accept();// 3、从socket通信管道中得到一个字节输入流InputStream is = socket.getInputStream();// 4、把字节输入流包装成缓冲字符输入流进行消息的接收BufferedReader br = new BufferedReader(new InputStreamReader(is));// 5、按照行读取消息String msg;if ((msg = br.readLine()) != null){System.out.println(socket.getRemoteSocketAddress() + "说了:: " + msg);}} catch (Exception e) {e.printStackTrace();}}
}

TCP通信实现:多发多收消息、实现可以同时接收多个客户端

需求:使用TCP通信方式实现:多发多收消息。

具体要求:

①可以使用死循环控制服务端收完消息继续等待接收下一个消息。
②客户端也可以使用死循环等待用户不断输入消息。
③客户端一旦输入了exit,则关闭客户端程序,并释放资源。
import java.io.OutputStream;
import java.io.PrintStream;
import java.net.Socket;
import java.util.Scanner;/**目标:实现服务端可以同时处理多个客户端的消息。*/
public class ClientDemo1 {public static void main(String[] args) {try {System.out.println("====客户端启动===");// 1、创建Socket通信管道请求有服务端的连接// public Socket(String host, int port)// 参数一:服务端的IP地址// 参数二:服务端的端口Socket socket = new Socket("127.0.0.1", 7777);// 2、从socket通信管道中得到一个字节输出流 负责发送数据OutputStream os = socket.getOutputStream();// 3、把低级的字节流包装成打印流PrintStream ps = new PrintStream(os);Scanner sc =  new Scanner(System.in);while (true) {System.out.println("请说:");String msg = sc.nextLine();// 4、发送消息ps.println(msg);ps.flush();}// 关闭资源。// socket.close();} catch (Exception e) {e.printStackTrace();}}
}
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;/**目标:实现服务端可以同时处理多个客户端的消息。*/
public class ServerDemo2 {public static void main(String[] args) {try {System.out.println("===服务端启动成功===");// 1、注册端口ServerSocket serverSocket = new ServerSocket(7777);// a.定义一个死循环由主线程负责不断的接收客户端的Socket管道连接。while (true) {// 2、每接收到一个客户端的Socket管道,交给一个独立的子线程负责读取消息Socket socket = serverSocket.accept();System.out.println(socket.getRemoteSocketAddress()+ "它来了,上线了!");// 3、开始创建独立线程处理socketnew ServerReaderThread(socket).start();}} catch (Exception e) {e.printStackTrace();}}
}
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.Socket;public class ServerReaderThread extends Thread{private Socket socket;public ServerReaderThread(Socket socket){this.socket = socket;}@Overridepublic void run() {try {// 3、从socket通信管道中得到一个字节输入流InputStream is = socket.getInputStream();// 4、把字节输入流包装成缓冲字符输入流进行消息的接收BufferedReader br = new BufferedReader(new InputStreamReader(is));// 5、按照行读取消息String msg;while ((msg = br.readLine()) != null){System.out.println(socket.getRemoteSocketAddress() + "说了:: " + msg);}} catch (Exception e) {System.out.println(socket.getRemoteSocketAddress() + "下线了!!!");}}
}

线程池优化

import java.io.OutputStream;
import java.io.PrintStream;
import java.net.Socket;
import java.util.Scanner;/**拓展:使用线程池优化:实现通信。*/
public class ClientDemo1 {public static void main(String[] args) {try {System.out.println("====客户端启动===");// 1、创建Socket通信管道请求有服务端的连接// public Socket(String host, int port)// 参数一:服务端的IP地址// 参数二:服务端的端口Socket socket = new Socket("127.0.0.1", 6666);// 2、从socket通信管道中得到一个字节输出流 负责发送数据OutputStream os = socket.getOutputStream();// 3、把低级的字节流包装成打印流PrintStream ps = new PrintStream(os);Scanner sc =  new Scanner(System.in);while (true) {System.out.println("请说:");String msg = sc.nextLine();// 4、发送消息ps.println(msg);ps.flush();}// 关闭资源。// socket.close();} catch (Exception e) {e.printStackTrace();}}
}
import com.itheima.d7_socket3.ServerReaderThread;import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.*;/**目标:实现服务端可以同时处理多个客户端的消息。*/
public class ServerDemo2 {// 使用静态变量记住一个线程池对象private static ExecutorService pool = new ThreadPoolExecutor(300,1500, 6, TimeUnit.SECONDS,new ArrayBlockingQueue<>(2), Executors.defaultThreadFactory(), new ThreadPoolExecutor.AbortPolicy());public static void main(String[] args) {try {System.out.println("===服务端启动成功===");// 1、注册端口ServerSocket serverSocket = new ServerSocket(6666);// a.定义一个死循环由主线程负责不断的接收客户端的Socket管道连接。while (true) {// 2、每接收到一个客户端的Socket管道,Socket socket = serverSocket.accept();System.out.println(socket.getRemoteSocketAddress()+ "它来了,上线了!");// 任务对象负责读取消息。Runnable target = new ServerReaderRunnable(socket);pool.execute(target);}} catch (Exception e) {e.printStackTrace();}}
}
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.Socket;public class ServerReaderRunnable implements Runnable{private Socket socket;public ServerReaderRunnable(Socket socket){this.socket = socket;}@Overridepublic void run() {try {// 3、从socket通信管道中得到一个字节输入流InputStream is = socket.getInputStream();// 4、把字节输入流包装成缓冲字符输入流进行消息的接收BufferedReader br = new BufferedReader(new InputStreamReader(is));// 5、按照行读取消息String msg;while ((msg = br.readLine()) != null){System.out.println(socket.getRemoteSocketAddress() + "说了:: " + msg);}} catch (Exception e) {System.out.println(socket.getRemoteSocketAddress() + "下线了!!!");}}
}

即时通信-端口转发

服务端

import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;/**目标: 即时通信*/
public class ServerDemo2 {public static List<Socket> onLineSockets = new ArrayList<>();public static void main(String[] args) {try {System.out.println("===服务端启动成功===");// 1、注册端口ServerSocket serverSocket = new ServerSocket(6868);// a.定义一个死循环由主线程负责不断的接收客户端的Socket管道连接。while (true) {// 2、每接收到一个客户端的Socket管道,交给一个独立的子线程负责读取消息Socket socket = serverSocket.accept();System.out.println(socket.getRemoteSocketAddress()+ "它来了,上线了!");// 把当前客户端管道Socket加入到在线集合中去onLineSockets.add(socket);// 3、开始创建独立线程处理socketnew ServerReaderThread(socket).start();}} catch (Exception e) {e.printStackTrace();}}
}
import java.io.*;
import java.net.Socket;public class ServerReaderThread extends Thread{private Socket socket;public ServerReaderThread(Socket socket){this.socket = socket;}@Overridepublic void run() {try {// 3、从socket通信管道中得到一个字节输入流InputStream is = socket.getInputStream();// 4、把字节输入流包装成缓冲字符输入流进行消息的接收BufferedReader br = new BufferedReader(new InputStreamReader(is));// 5、按照行读取消息String msg;while ((msg = br.readLine()) != null){System.out.println(socket.getRemoteSocketAddress() + "说了:: " + msg);// 把这个消息发给当前所有在线socketsendMsgToAll(msg);}} catch (Exception e) {System.out.println(socket.getRemoteSocketAddress() + "下线了!!!");// 从在线集合中抹掉本客户端socketServerDemo2.onLineSockets.remove(socket);}}private void sendMsgToAll(String msg) {try {// 遍历全部的在线 socket给他们发消息for (Socket onLineSocket : ServerDemo2.onLineSockets) {// 除了自己的socket,其他socket我都发!!if(onLineSocket != socket){PrintStream ps = new PrintStream(socket.getOutputStream());ps.println(msg);ps.flush();}}} catch (Exception e) {e.printStackTrace();}}
}

客户端

import java.io.OutputStream;
import java.io.PrintStream;
import java.net.Socket;
import java.util.Scanner;/**拓展:即时通信客户端:发消息的同时,随时有人发消息过来。服务端:接收消息后,推送给其他所有的在线socket*/
public class ClientDemo1 {public static void main(String[] args) {try {System.out.println("====客户端启动===");// 1、创建Socket通信管道请求有服务端的连接// public Socket(String host, int port)// 参数一:服务端的IP地址// 参数二:服务端的端口Socket socket = new Socket("127.0.0.1", 6868);// 马上为客户端分配一个独立的线程负责读取它收到的消息new ClientReaderThread(socket).start();// 2、从socket通信管道中得到一个字节输出流 负责发送数据OutputStream os = socket.getOutputStream();// 3、把低级的字节流包装成打印流PrintStream ps = new PrintStream(os);Scanner sc =  new Scanner(System.in);while (true) {System.out.println("请说:");String msg = sc.nextLine();// 4、发送消息ps.println(msg);ps.flush();}// 关闭资源。// socket.close();} catch (Exception e) {e.printStackTrace();}}
}
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.Socket;public class ClientReaderThread extends Thread{private Socket socket;public ClientReaderThread(Socket socket){this.socket = socket;}@Overridepublic void run() {try {// 3、从socket通信管道中得到一个字节输入流InputStream is = socket.getInputStream();// 4、把字节输入流包装成缓冲字符输入流进行消息的接收BufferedReader br = new BufferedReader(new InputStreamReader(is));// 5、按照行读取消息String msg;while ((msg = br.readLine()) != null){System.out.println(socket.getRemoteSocketAddress() + "收到了: " + msg);}} catch (Exception e) {System.out.println("服务端把你踢出去了~~");}}}

即时通信代码演示

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.net.Socket;/*** @Author xlei(徐磊)* 客户端界面*/
public class ClientChat implements ActionListener {/** 1.设计界面  */private JFrame win = new JFrame();/** 2.消息内容框架 */public JTextArea smsContent =new JTextArea(23 , 50);/** 3.发送消息的框  */private JTextArea smsSend = new JTextArea(4,40);/** 4.在线人数的区域  *//** 存放人的数据 *//** 展示在线人数的窗口 */public JList<String> onLineUsers = new JList<>();// 是否私聊按钮private JCheckBox isPrivateBn = new JCheckBox("私聊");// 消息按钮private JButton sendBn  = new JButton("发送");// 登录界面private JFrame loginView;private JTextField ipEt , nameEt , idEt;private Socket socket ;public static void main(String[] args) {new ClientChat().initView();}private void initView() {/** 初始化聊天窗口的界面 */win.setSize(650, 600);/** 展示登录界面  */displayLoginView();/** 展示聊天界面 *///displayChatView();}private void displayChatView() {JPanel bottomPanel = new JPanel(new BorderLayout());//-----------------------------------------------// 将消息框和按钮 添加到窗口的底端win.add(bottomPanel, BorderLayout.SOUTH);bottomPanel.add(smsSend);JPanel btns = new JPanel(new FlowLayout(FlowLayout.LEFT));btns.add(sendBn);btns.add(isPrivateBn);bottomPanel.add(btns, BorderLayout.EAST);//-----------------------------------------------// 给发送消息按钮绑定点击事件监听器// 将展示消息区centerPanel添加到窗口的中间smsContent.setBackground(new Color(0xdd,0xdd,0xdd));// 让展示消息区可以滚动。win.add(new JScrollPane(smsContent), BorderLayout.CENTER);smsContent.setEditable(false);//-----------------------------------------------// 用户列表和是否私聊放到窗口的最右边Box rightBox = new Box(BoxLayout.Y_AXIS);onLineUsers.setFixedCellWidth(120);onLineUsers.setVisibleRowCount(13);rightBox.add(new JScrollPane(onLineUsers));win.add(rightBox, BorderLayout.EAST);//-----------------------------------------------// 关闭窗口退出当前程序win.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);win.pack();  // swing 加上这句 就可以拥有关闭窗口的功能/** 设置窗口居中,显示出来  */setWindowCenter(win,650,600,true);// 发送按钮绑定点击事件sendBn.addActionListener(this);}private void displayLoginView(){/** 先让用户进行登录*  服务端ip*  用户名*  id*  *//** 显示一个qq的登录框     */loginView = new JFrame("登录");loginView.setLayout(new GridLayout(3, 1));loginView.setSize(400, 230);JPanel ip = new JPanel();JLabel label = new JLabel("   IP:");ip.add(label);ipEt = new JTextField(20);ip.add(ipEt);loginView.add(ip);JPanel name = new JPanel();JLabel label1 = new JLabel("姓名:");name.add(label1);nameEt = new JTextField(20);name.add(nameEt);loginView.add(name);JPanel btnView = new JPanel();JButton login = new JButton("登陆");btnView.add(login);JButton cancle = new JButton("取消");btnView.add(cancle);loginView.add(btnView);// 关闭窗口退出当前程序loginView.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);setWindowCenter(loginView,400,260,true);/** 给登录和取消绑定点击事件 */login.addActionListener(this);cancle.addActionListener(this);}private static void setWindowCenter(JFrame frame, int width , int height, boolean flag) {/** 得到所在系统所在屏幕的宽高 */Dimension ds = frame.getToolkit().getScreenSize();/** 拿到电脑的宽 */int width1 = ds.width;/** 高 */int height1 = ds.height ;System.out.println(width1 +"*" + height1);/** 设置窗口的左上角坐标 */frame.setLocation(width1/2 - width/2, height1/2 -height/2);frame.setVisible(flag);}@Overridepublic void actionPerformed(ActionEvent e) {/** 得到点击的事件源 */JButton btn = (JButton) e.getSource();switch(btn.getText()){case "登陆":String ip = ipEt.getText().toString();String name = nameEt.getText().toString();// 校验参数是否为空// 错误提示String msg = "" ;// 12.1.2.0// \d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\if(ip==null || !ip.matches("\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}")){msg = "请输入合法的服务端ip地址";}else if(name==null || !name.matches("\\S{1,}")){msg = "姓名必须1个字符以上";}if(!msg.equals("")){/** msg有内容说明参数有为空 */// 参数一:弹出放到哪个窗口里面JOptionPane.showMessageDialog(loginView, msg);}else{try {// 参数都合法了// 当前登录的用户,去服务端登陆/** 先把当前用户的名称展示到界面 */win.setTitle(name);// 去服务端登陆连接一个socket管道socket = new Socket(ip, Constants.PORT);//为客户端的socket分配一个线程 专门负责收消息new ClientReader(this,socket).start();// 带上用户信息过去DataOutputStream dos = new DataOutputStream(socket.getOutputStream());dos.writeInt(1); // 登录消息dos.writeUTF(name.trim());dos.flush();// 关系当前窗口 弹出聊天界面loginView.dispose(); // 登录窗口销毁displayChatView(); // 展示了聊天窗口了} catch (Exception e1) {e1.printStackTrace();}}break;case "取消":/** 退出系统 */System.exit(0);break;case "发送":// 得到发送消息的内容String msgSend = smsSend.getText().toString();if(!msgSend.trim().equals("")){/** 发消息给服务端 */try {// 判断是否对谁发消息String selectName = onLineUsers.getSelectedValue();int flag = 2 ;// 群发 @消息if(selectName!=null&&!selectName.equals("")){msgSend =("@"+selectName+","+msgSend);/** 判断是否选中了私法 */if(isPrivateBn.isSelected()){/** 私法 */flag = 3 ;//私发消息}}DataOutputStream dos = new DataOutputStream(socket.getOutputStream());dos.writeInt(flag); // 群发消息  发送给所有人dos.writeUTF(msgSend);if(flag == 3){// 告诉服务端我对谁私发dos.writeUTF(selectName.trim());}dos.flush();} catch (Exception e1) {e1.printStackTrace();}}smsSend.setText(null);break;}}
}class ClientReader extends Thread {private Socket socket;private ClientChat clientChat ;public ClientReader(ClientChat clientChat, Socket socket) {this.clientChat = clientChat;this.socket = socket;}@Overridepublic void run() {try {DataInputStream dis = new DataInputStream(socket.getInputStream());/** 循环一直等待客户端的消息 */while(true){/** 读取当前的消息类型 :登录,群发,私聊 , @消息 */int flag = dis.readInt();if(flag == 1){// 在线人数消息回来了String nameDatas = dis.readUTF();// 展示到在线人数的界面String[] names = nameDatas.split(Constants.SPILIT);clientChat.onLineUsers.setListData(names);}else if(flag == 2){// 群发消息String msg = dis.readUTF() ;clientChat.smsContent.append(msg);//滾動到底端clientChat.smsContent.setCaretPosition(clientChat.smsContent.getText().length());}}} catch (Exception e) {e.printStackTrace();}}
}
public class Constants {/** 常量 */public static final int PORT = 7778 ;/** 协议分隔符 */public static final String SPILIT = "003197♣♣㏘♣④④♣";
}
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.text.SimpleDateFormat;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;/*** @Author* @Email dlei0009@163.com*/
public class ServerChat {/** 定义一个集合存放所有在线的socket  */public static Map<Socket, String> onLineSockets = new HashMap<>();public static void main(String[] args) {try {/** 注册端口   */ServerSocket serverSocket = new ServerSocket(Constants.PORT);/** 循环一直等待所有可能的客户端连接 */while(true){Socket socket = serverSocket.accept();/** 把客户端的socket管道单独配置一个线程来处理 */new ServerReader(socket).start();}} catch (Exception e) {e.printStackTrace();}}
}class ServerReader extends Thread {private Socket socket;public ServerReader(Socket socket) {this.socket = socket;}@Overridepublic void run() {DataInputStream dis = null;try {dis = new DataInputStream(socket.getInputStream());/** 循环一直等待客户端的消息 */while(true){/** 读取当前的消息类型 :登录,群发,私聊 , @消息 */int flag = dis.readInt();if(flag == 1){/** 先将当前登录的客户端socket存到在线人数的socket集合中   */String name = dis.readUTF() ;System.out.println(name+"---->"+socket.getRemoteSocketAddress());ServerChat.onLineSockets.put(socket, name);}writeMsg(flag,dis);}} catch (Exception e) {System.out.println("--有人下线了--");// 从在线人数中将当前socket移出去  ServerChat.onLineSockets.remove(socket);try {// 从新更新在线人数并发给所有客户端 writeMsg(1,dis);} catch (Exception e1) {e1.printStackTrace();}}}private void writeMsg(int flag, DataInputStream dis) throws Exception {
//      DataOutputStream dos = new DataOutputStream(socket.getOutputStream()); // 定义一个变量存放最终的消息形式 String msg = null ;if(flag == 1){/** 读取所有在线人数发给所有客户端去更新自己的在线人数列表 *//** onlineNames = [徐磊,zhangsan,李刚]*/StringBuilder rs = new StringBuilder();Collection<String> onlineNames = ServerChat.onLineSockets.values();// 判断是否存在在线人数 if(onlineNames != null && onlineNames.size() > 0){for(String name : onlineNames){rs.append(name+ Constants.SPILIT);}// 徐磊003197♣♣㏘♣④④♣zhangsan003197♣♣㏘♣④④♣李刚003197♣♣㏘♣④④♣// 去掉最后的一个分隔符 msg = rs.substring(0, rs.lastIndexOf(Constants.SPILIT));/** 将消息发送给所有的客户端 */sendMsgToAll(flag,msg);}}else if(flag == 2 || flag == 3){// 读到消息  群发的 或者 @消息String newMsg = dis.readUTF() ; // 消息// 得到发件人 String sendName = ServerChat.onLineSockets.get(socket);// 李刚 时间//    内容--StringBuilder msgFinal = new StringBuilder();// 时间  SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss EEE");if(flag == 2){msgFinal.append(sendName).append("  ").append(sdf.format(System.currentTimeMillis())).append("\r\n");msgFinal.append("    ").append(newMsg).append("\r\n");sendMsgToAll(flag,msgFinal.toString());}else if(flag == 3){msgFinal.append(sendName).append("  ").append(sdf.format(System.currentTimeMillis())).append("对您私发\r\n");msgFinal.append("    ").append(newMsg).append("\r\n");// 私发 // 得到给谁私发 String destName = dis.readUTF();sendMsgToOne(destName,msgFinal.toString());}}}/*** @param destName 对谁私发 * @param msg 发的消息内容 * @throws Exception*/private void sendMsgToOne(String destName, String msg) throws Exception {// 拿到所有的在线socket管道 给这些管道写出消息Set<Socket> allOnLineSockets = ServerChat.onLineSockets.keySet();for(Socket sk :  allOnLineSockets){// 得到当前需要私发的socket // 只对这个名字对应的socket私发消息if(ServerChat.onLineSockets.get(sk).trim().equals(destName)){DataOutputStream dos = new DataOutputStream(sk.getOutputStream());dos.writeInt(2); // 消息类型dos.writeUTF(msg);dos.flush();}}}private void sendMsgToAll(int flag, String msg) throws Exception {// 拿到所有的在线socket管道 给这些管道写出消息Set<Socket> allOnLineSockets = ServerChat.onLineSockets.keySet();for(Socket sk :  allOnLineSockets){DataOutputStream dos = new DataOutputStream(sk.getOutputStream());dos.writeInt(flag); // 消息类型dos.writeUTF(msg);dos.flush();}}
}
public class User {private Integer id ;private String name ;public User(Integer id, String name) {this.id = id;this.name = name;}public Integer getId() {return id;}public void setId(Integer id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;}@Overridepublic String toString() {return "User [id=" + id + ", name=" + name + "]";}}

TCP通信实战案例-模拟BS系统

import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.*;/**了解:BS-浏览器-服务器基本了解。引入:之前客户端和服务端都需要自己开发。也就是CS架构。接下来模拟一下BS架构。客户端:浏览器。(无需开发)服务端:自己开发。需求:在浏览器中请求本程序,响应一个网页文字给浏览器显示*/
public class BSserverDemo {// 使用静态变量记住一个线程池对象private static ExecutorService pool = new ThreadPoolExecutor(3,5, 6, TimeUnit.SECONDS,new ArrayBlockingQueue<>(2), Executors.defaultThreadFactory(), new ThreadPoolExecutor.AbortPolicy());public static void main(String[] args) {try {// 1.注册端口ServerSocket ss = new ServerSocket(8080);// 2.创建一个循环接收多个客户端的请求。while(true){Socket socket = ss.accept();// 3.交给一个独立的线程来处理!pool.execute(new ServerReaderRunnable(socket));}} catch (Exception e) {e.printStackTrace();}}
}
import java.io.PrintStream;
import java.net.Socket;public class ServerReaderRunnable implements Runnable{private Socket socket;public ServerReaderRunnable(Socket socket){this.socket = socket;}@Overridepublic void run() {try {// 浏览器 已经与本线程建立了Socket管道// 响应消息给浏览器显示PrintStream ps = new PrintStream(socket.getOutputStream());// 必须响应HTTP协议格式数据,否则浏览器不认识消息ps.println("HTTP/1.1 200 OK"); // 协议类型和版本 响应成功的消息!ps.println("Content-Type:text/html;charset=UTF-8"); // 响应的数据类型:文本/网页ps.println(); // 必须发送一个空行// 才可以响应数据回去给浏览器ps.println("<span style='color:red;font-size:90px'>《最牛的149期》 </span>");ps.close();} catch (Exception e) {System.out.println(socket.getRemoteSocketAddress() + "下线了!!!");}}
}

网络编程、通信三要素、UDP快速入门、TCP通信、即时通信、模拟BS系统相关推荐

  1. java网络编程的三要素

    1.IP地址,是设备的标识.Java学习笔记http://www.mobiletrain.org/note/java/ 要想让网络中的计算机能相互通信,必须为每台计算机指定一个标识号,通过此标识号来指 ...

  2. 网络编程-通信协议-三要素

    1.概述:即通过无线网络或者有线网络可以把不同地理位置且相互独立的计算机连同其外部设备连接起来,组成计算机网络.这样就实现了计算机之间的资源共享和信息的传递. 2.网络通信三要素 2.1)ip地址 网 ...

  3. [C# 网络编程系列]专题九:实现类似QQ的即时通信程序

    引言: 前面专题中介绍了UDP.TCP和P2P编程,并且通过一些小的示例来让大家更好的理解它们的工作原理以及怎样.Net类库去实现它们的.为了让大家更好的理解我们平常中常见的软件QQ的工作原理,所以在 ...

  4. [Java网络编程基础]网络编程概述,三要素,IP地址

  5. java网络编程阻塞_Java网络编程由浅入深三 一文了解非阻塞通信的图文代码示例详解...

    本文详细介绍组成非阻塞通信的几大类:Buffer.Channel.Selector.SelectionKey 非阻塞通信的流程ServerSocketChannel通过open方法获取ServerSo ...

  6. 网游中的网络编程系列1:UDP vs. TCP

    原文:UDP vs. TCP,作者是Glenn Fiedler,专注于游戏网络编程相关工作多年. 目录 网游中的网络编程系列1:UDP vs. TCP 网游中的网络编程2:发送和接收数据包 网游中的网 ...

  7. Gradle核心思想(三)Groovy快速入门指南

    本文首发于微信公众号「刘望舒」 关联文章 Gradle核心思想(一)为什么现在要用Gradle? Gradle核心思想(二)Gradle入门前奏 Gradle核心思想(三)Groovy快速入门指南 G ...

  8. Netty网络编程第三卷

    Netty网络编程第三卷 三. Netty 进阶 1. 粘包与半包 1.1 粘包现象 1.2 半包现象 1.3 现象分析 MSS 限制 Nagle 算法 1.4 解决方案 方法1,短链接 方法2,固定 ...

  9. linux网络编程(三)select、poll和epoll

    linux网络编程(三)select.poll和epoll 一.为什么会有多路I/O转接服务器? 二.select 三.poll 三.epoll 一.为什么会有多路I/O转接服务器? 为什么会有多路I ...

  10. Unix网络编程---第三次作业

    Unix网络编程---第三次作业 要求: 利用多线程技术实现如下并发网络程序,要求对上课时的实现进行完善,利用线程专用数据TSD实现. 服务端: 服务器等待客户连接,连接成功后显示客户地址,接着接收该 ...

最新文章

  1. 贪心 Codeforces Round #236 (Div. 2) A. Nuts
  2. 关于release后retainCount还是1的问题
  3. MySQL的约束、多表查询、子查询
  4. 编写代码的工作在哪找_编写事件代码如何帮助我获得了出色的工作
  5. Spring Cloud微服务之Nacos服务发现(八)
  6. marqueeview更改字体颜色_安卓手机上可以编辑字体的便签软件哪个好?
  7. 广域网SDN功能与架构
  8. 网络科学论坛纪要-2012
  9. 计算机组成原理知识点汇总(考研用)——第二章:数据的表示和运算
  10. android 10.0禁用电源键(屏蔽关机短按长按事件)
  11. Linux 安装rabbitMQ guest账号登录总是提示失败
  12. Java实现微信小程序支付(支付,提现,退款)
  13. I.Gree的心房(思维题)
  14. Sqlite中文排序研究
  15. mac apache加载php,Apache 2.4没有加载php5.5与Mac OS 10.8
  16. iOS App从点击到启动
  17. filter 拦截器 获取http请求参数
  18. 分治法之图解最大子序列和
  19. 区块链的跨链技术介绍完整版
  20. 计算机毕业设计JAVA糖助手服务交流平台mybatis+源码+调试部署+系统+数据库+lw

热门文章

  1. 小型机 PC服务器 性能,pc服务器小型机
  2. 【安信可首款4G模组CA-01直连阿里物联网平台③】一型一密认证方式连接
  3. AcWing 303 运输小猫
  4. 9367: 【动态规划】雷涛的小猫
  5. html 滑动刻度尺,js实现移动端H5页面手指滑动刻度尺功能
  6. Unity打Android包报错Error building Player Exception OBSOLETE - Providing Android resources in AssetsPlug
  7. 物流设备厂商名单:卡车、叉车、货架、集装箱、传输机、起重设备、托盘
  8. MFC之菜单栏的相关使用14
  9. 利用线性回归预测波士顿房价
  10. 计算机卷死,一位计算机教师死了,警方在床底发现一张纸,上面写着一些特殊的符号,这... #119615-推理漫画-侦探推理-33IQ...