网络编程
一,网络编程概述
网络编程就是在网络通信协议下,不同计算机上运行的程序,进行的数据传送。
本质就是不同计算机之间通过网络进行数据传送。
java中可用java.net包下的技术,开发出常见的网络应用程序。
常见的软件架构:
-
B/S架构:Browser/Server,即浏览器/服务器的架构
只需要一个浏览器,用户通过不同的网址,客户访问不同的服务器。
优点:
- 不需要开发客户端,只需要页面+服务的
- 用户不需要下载,打开浏览器即可使用
缺点:如果应用过大,用户体验会受到影响
-
C/S架构:Client/Server,即客户端/服务的架构
在用户本地需要下载并安装客户端程序,在远程有一个服务器端程序。
优点:画面可做的非常精美,用户体验很好
缺点:需要开发客户端,也需要开发服务的,维护开发部署很麻烦,用户需要下载和更新的时候很麻烦。
网络编程三要素:ip,端口,协议。(具体去学计网)
二,InetAddress
InetAddress是java用来操作ip的类,它底层有两个子类Inet4Address和Inet6Address根据你传入的ip地址的格式来创建ipv4或ipv6的对象。
InetAddress获取的对象就代表了一台电脑的IP的对象。
常用方法:

举例:
public static void main(String[] args) throws IOException {
//1.获取本机ip地址
InetAddress localHost = InetAddress.getLocalHost();
//2.根据ip地址或域名返回一个InetAddress对象
InetAddress addressByName = InetAddress.getByName("www.baidu.com");
//3.获取该ip地址对象对应的主机名
String hostName = localHost.getHostName();
System.out.println(hostName);
//4.获取ip地址对象对应的ip地址
String hostAddress = localHost.getHostAddress();
System.out.println(hostAddress);
//在指定毫秒内,判断主机与该ip地址对应的主机之间能否连通
if (addressByName.isReachable(3000)) {
System.out.println("可以访问");
}else{
System.out.println("不可以访问");
}
}
三,协议
UDP协议:
-
用户数据报协议(User Datagram Protocol)
-
UDP是面向无连接通信协议
速度块,有大小限制一次最多64k,数据不安全,易丢失
面向无连接:数据直接发送,不确认是否连接成功。
应用场景:在线视频,语音通话,网络会议。
TCP协议:
-
传输控制协议TCP(Transmission Control Protocol)
-
TCP协议是面向连接的通信协议
速度慢,没有大小限制,数据安全。
面向连接:数据发送之前会确定是否连接成功再发送数据
应用场景:下载软件,文字聊天,发送邮件。
四,UDP通信程序
- 特点:无连接、不可靠通信。
- 不事先建立连接;发送端每次把要发送的数据(限制在64KB内)、接收端IP、等信息封装成一个数据包,发出去就不管了。
- Java提供了一个
java.net.Datagramsocket类来实现UDP通信:
DatagramSocket:用于创建客户端、服务端
| 构造器 | 说明 |
|---|---|
| public DatagramSocket() | 创建客户端的Socket对象,系统会随机分配一个端口号 |
| public DatagramSocket(int port) | 创建服务端的Socket对象,并指定端口号 |
常用方法:
| 方法 | 说明 |
|---|---|
| public void send(DatagramPacket dp) | 发送数据包 |
| public void receive(DatagramPacket p) | 使用数据包接受数据 |
DatagramPacket:创建数据包
| 构造器 | 说明 |
|---|---|
| public DatagramPacket(byte[] buf,int length, InetAddress address, int port) | 创建发送出去的数据包对象 |
| public DatagramPacket(byte[] buf,int length) | 创建用来接受数据的数据包 |
常用方法
| 方法 | 说明 |
|---|---|
| public int getLength() | 获取数据包,实际接收到的字节个数 |
| public InetAddress getAddress() | 获取到发送方的ip对象 |
| public int getPort() | 获取到发送方的端口 |
举例:
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
//接收方
public class ReceiveMessage {
public static void main(String[] args) {
//1.创建接受数据的服务端
try(DatagramSocket ds=new DatagramSocket(8888)){
//2.创建接受数据的数据包
byte[] bytes=new byte[1024];
while(true){
DatagramPacket dp=new DatagramPacket(bytes,bytes.length);
ds.receive(dp);
//3.将接收到的数据转换为字符串并输出
//获取到发送方的ip对象
InetAddress address = dp.getAddress();
//获取到发送方的端口
int port = dp.getPort();
//4.输出接收到的数据
System.out.println("从"+address+":"+port+"接收到的数据是:"+new String(dp.getData(),0,dp.getLength()));
}
}catch(Exception e){
e.printStackTrace();
}
}
}
import java.net.*;
import java.util.Scanner;
import static java.lang.Thread.sleep;
//发送方
public class SendMessage {
public static void main(String[] args) {
//1.创建发送数据的客户端
try(DatagramSocket ds=new DatagramSocket()){
//2.创建服务端的InetAddress对象
InetAddress byName = InetAddress.getByName("127.0.0.1");
//3.创建要发送数据的数据包
while(true){
Scanner sc=new Scanner(System.in);
String dataStr = sc.nextLine();
//4.创建要发送给服务端的数据包
DatagramPacket dp=new DatagramPacket(dataStr.getBytes(),dataStr.getBytes().length,byName,8888);
//5.发送数据
ds.send(dp);
}
}catch(Exception e){
e.printStackTrace();
}
}
}
UDP的三种通信方式
-
单播:发送端只给一台设备发送数据(一对一)
上述写的代码就是单波形式
-
组播:发送端给一组设备发送数据(一对多)
组波地址:224.0.0.0到239.255.255.255,其中224.0.0.0到224.0.0.255为预留的组播地址。
我们只能用这部分预留的组播地址。
组播和IP的区别是,ip只能表示一台设备,组播可以表示多台设备。
-
广播:发送端给局域网所有的设备发送数据(一对所有)
广播地址:255.255.255.255
组波代码实现:
组播和单波代码实现的区别:
- 发送端和接收端使用
MulticastSocket对象来代替DatagramSocket - 发送端使用组播地址代替ip地址
- 接收端需要额外的将当前本机加入指定组播地址当中
- 发送数据
package Text2;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.InetAddress;
import java.net.MulticastSocket;
public class Send {
public static void main(String[] args) throws IOException {
//1.创建MulticastSocket对象
//MulticastSocket和DatagramSocket使用方式一样
MulticastSocket ms=new MulticastSocket(1010);
//创建DatagramPacket对象
String s="hello world";
byte[] bytes = s.getBytes();
InetAddress address = InetAddress.getByName("224.0.0.1");//指定的是组播地址
int port=1000;
DatagramPacket dp=new DatagramPacket(bytes,bytes.length,address,port);
ms.send(dp);
ms.close();
}
}
- 接收数据
package Text2;
import javax.xml.crypto.Data;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.MulticastSocket;
public class Receive {
public static void main(String[] args) throws IOException {
//创建MulticastSocket对象
MulticastSocket ms=new MulticastSocket(1000);
//将当前本机,添加到224.0.0.1这一组
InetAddress address=InetAddress.getByName("224.0.0.1");
ms.joinGroup(address);
//创建DatagramPacket数据包对象
byte[] bytes = new byte[1024];
DatagramPacket dp=new DatagramPacket(bytes,bytes.length);
//接收数据
ms.receive(dp);
//解析数据
byte[] data = dp.getData();
int len=dp.getLength();
String ip=dp.getAddress().getHostAddress();
String name=dp.getAddress().getHostName();
System.out.println("ip为:"+ip+"主机名为:"+name+"的人,发送了数据:"+new String(data,len));
//释放资源
ms.close();
}
}
广播的代码实现:
只需要在单波的代码中发送端额发送地址修改为255.255.255.255即可实现广播
五,TCP通信程序
5.1 TCP的三次握手和四次挥手
-
三次握手:目的是保证连接的建立
过程如图:

-
四次挥手:目的是确保连接端口,且通道内数据处理完毕。
过程如图:

5.2 TCP通信快速入门
- 特点:面向连接、可靠通信,
- 通信双方事先会采用“三次握手”方式建立可靠连接,实现端到端的通信;底层能保证数据成功传给服务端
- Java提供了一个
java.net.Socket类来实现TCP通信。

5.2.1 客户端
| 构造器 | 说明 |
|---|---|
| public Socket(String host,int port) | 根据指定的服务器ip,端口号请求与服务端建立连接,连接通过就获得了客户端socket |
方法:
| 方法 | 说明 |
|---|---|
| public OutputStream getOutputStream() | 获得字节输出流对象 |
| public InputStream getInputStream() | 获得字节输入流对象 |
| public InetAddressgetInetAddress() | 获取客户端的ip对象 |
代码实现:
import java.io.*;
import java.net.Socket;
import java.util.Scanner;
public class Client {
public static void main(String[] args) {
//1.创建Socket对象,同时请求与服务器程序的连接
try(Socket socket=new Socket("127.0.0.1",8888)){
//2.从socket通道中获取输出流
OutputStream outputStream = socket.getOutputStream();
//3.包装
DataOutputStream dataOutputStream = new DataOutputStream(outputStream);
Scanner sc=new Scanner(System.in);
//4.输入数据
while(true){
System.out.print("请输入内容:");
String msg=sc.nextLine();
if("exit".equals(msg)){
break;
}
dataOutputStream.writeUTF(msg);
//刷新缓存
dataOutputStream.flush();
}
}catch(Exception e){
System.out.println("服务端访问失败!");
}
}
}
5.2.2 服务端
服务端是通过java.net包下的Serversocket类来实现的
ServerSocket:
| 构造器 | 说明 |
|---|---|
| public ServerSocket(int port) | 为服务端程序注册端口 |
常用方法:
| 方法 | 说明 |
|---|---|
| public Socket accept() | 阻塞等待客户端的连接请求,一旦与某个客户端成功连接,则返回服务端这边的Socket对象 |
代码:
import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
public class Server {
public static void main(String[] args) {
System.out.println("服务器启动");
//1.创建ServerSocket对象,同时为服务器注册端口
try(ServerSocket serverSocket = new ServerSocket(8888)){
//2.使用serverSocket调用accept方法获取到客户端Socket
Socket accept = serverSocket.accept();
//3.从客户端socket中获得输入流
InputStream inputStream = accept.getInputStream();
//4.包装成数据输入流
DataInputStream ds = new DataInputStream(inputStream);
//5.读取数据
System.out.println("客户端:"+accept.getRemoteSocketAddress()+"上线!");
while(true){
try {
String msg = ds.readUTF();
System.out.println("客户端说:"+msg);
} catch (Exception e) {
//捕获到异常代表客户端断开连接
System.out.println("客户端:"+accept.getRemoteSocketAddress()+"客户端断开连接!");
ds.close();
break;
}
}
}catch(Exception e){
e.printStackTrace();
}finally{
System.out.println("服务器关闭");
}
}
}
当我们客户端直接退出了,服务端就抛出异常
5.3 与多个客户端同时通信
上述快速入门案例只能实现了一对一通信,通过多线程来实现与多个客户端同时通信
代码实现:
只需将服务端接受socket改成循环即然后配套使用多线程即可
import java.net.ServerSocket;
import java.net.Socket;
public class Server {
public static void main(String[] args) {
System.out.println("服务器启动");
//1.创建ServerSocket对象,同时为服务器注册端口
try(ServerSocket serverSocket = new ServerSocket(8888)){
//2.使用循环来接受客户端请求
while(true){
Socket accept = serverSocket.accept();
ServerReadThread serverReadThread = new ServerReadThread(accept);
serverReadThread.start();
}
}catch(Exception e){
e.printStackTrace();
}finally{
System.out.println("服务器关闭");
}
}
}
import java.io.DataInputStream;
import java.io.InputStream;
import java.net.Socket;
public class ServerReadThread extends Thread{
private Socket accept;
public ServerReadThread(Socket accept) {
this.accept = accept;
}
@Override
public void run() {
try{
//1.从客户端socket中获得输入流
InputStream inputStream = accept.getInputStream();
//2.包装成数据输入流
DataInputStream ds = new DataInputStream(inputStream);
//3.读取数据
System.out.println("客户端:"+accept.getRemoteSocketAddress()+"上线!");
while(true){
try {
String msg = ds.readUTF();
System.out.println("客户端说:"+msg);
} catch (Exception e) {
//捕获到异常代表客户端断开连接
System.out.println("客户端:"+accept.getRemoteSocketAddress()+"客户端断开连接!");
ds.close();
break;
}
}
}catch(Exception e){
e.printStackTrace();
}
}
}
5.4 实现群聊

代码实现:
服务端:
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;
public class Server {
//代表所有在线的客户端socket
public static List<Socket> onlineSocketList=new ArrayList<Socket>();
public static void main(String[] args) {
System.out.println("服务器启动");
//1.创建ServerSocket对象,同时为服务器注册端口
try(ServerSocket serverSocket = new ServerSocket(8888)){
//2.使用循环来接受客户端请求
while(true){
Socket accept = serverSocket.accept();
onlineSocketList.add(accept);
ServerReadThread serverReadThread = new ServerReadThread(accept);
serverReadThread.start();
}
}catch(Exception e){
e.printStackTrace();
}finally{
System.out.println("服务器关闭");
}
}
}
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.net.SocketAddress;
public class ServerReadThread extends Thread{
private Socket accept;
public ServerReadThread(Socket accept) {
this.accept = accept;
}
@Override
public void run() {
try{
//1.从客户端socket中获得输入流
InputStream inputStream = accept.getInputStream();
//2.包装成数据输入流
DataInputStream ds = new DataInputStream(inputStream);
//3.读取数据
System.out.println("客户端:"+accept.getRemoteSocketAddress()+"上线!");
while(true){
try {
String msg = ds.readUTF();
sendMsgAll(msg);
// System.out.println("客户端说:"+msg);
} catch (Exception e) {
//捕获到异常代表客户端断开连接
String msg = "客户端:"+accept.getRemoteSocketAddress()+"客户端断开连接!";
System.out.println(msg);
//从集合中删除掉该客户端
Server.onlineSocketList.remove(accept);
ds.close();
break;
}
}
}catch(Exception e){
e.printStackTrace();
}
}
/**
* 把消息发送给所有在线客户端
* @param msg
*/
private void sendMsgAll(String msg) {
for (Socket socket : Server.onlineSocketList) {
try {
//1.从客户端socket中获得输出流
OutputStream outputStream = socket.getOutputStream();
//2.包装成数据输出流
DataOutputStream ds = new DataOutputStream(outputStream);
//3.发送数据
ds.writeUTF(msg);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
客户端:
import java.io.*;
import java.net.Socket;
import java.util.Scanner;
public class Client {
public static void main(String[] args) {
//1.创建Socket对象,同时请求与服务器程序的连接
try(Socket socket=new Socket("127.0.0.1",8888)){
//2.从socket通道中获取输出流
OutputStream outputStream = socket.getOutputStream();
//3.包装
DataOutputStream dataOutputStream = new DataOutputStream(outputStream);
Scanner sc=new Scanner(System.in);
//创建一个独立的线程随时从服务端接受消息
new ClientReadThread(socket).start();
//4.输入数据
while(true){
String msg=sc.nextLine();
if("exit".equals(msg)){
break;
}
dataOutputStream.writeUTF(msg);
//刷新缓存
dataOutputStream.flush();
}
}catch(Exception e){
System.out.println("服务端访问失败!");
}
}
}
import java.io.DataInputStream;
import java.io.InputStream;
import java.net.Socket;
public class ClientReadThread extends Thread{
private Socket socket;
public ClientReadThread(Socket socket) {
this.socket = socket;
}
@Override
public void run() {
try{
//1.从客户端socket中获得输入流
InputStream inputStream = socket.getInputStream();
//2.包装成数据输入流
DataInputStream ds = new DataInputStream(inputStream);
//3.读取数据
while(true){
try {
String msg = ds.readUTF();
System.out.println(msg);
} catch (Exception e) {
//捕获到异常代表客户端断开连接
String msg = "客户端:"+ socket.getRemoteSocketAddress()+"客户端断开连接!";
System.out.println(msg);
ds.close();
break;
}
}
}catch(Exception e){
e.printStackTrace();
}
}
}
5.5 实现简易BS架构

代码举例:
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class BsServer {
public static void main(String[] args) {
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
10,
20,
10,
TimeUnit.SECONDS,
new LinkedBlockingQueue<>(10),
new ThreadPoolExecutor.CallerRunsPolicy()
);
try {
ServerSocket serverSocket = new ServerSocket(8080);
while (true) {
Socket accept = serverSocket.accept();
threadPoolExecutor.execute(new BsServerReadThread(accept));
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.Socket;
import java.net.SocketAddress;
public class BsServerReadThread implements Runnable {
private Socket socket;
public BsServerReadThread(Socket socket) {
this.socket=socket;
}
@Override
public void run() {
SocketAddress remoteSocketAddress = socket.getRemoteSocketAddress();
System.out.println("客户端"+remoteSocketAddress+"已连接");
try {
OutputStream outputStream = socket.getOutputStream();
PrintWriter writer = new PrintWriter(outputStream);
//输出http协议
writer.println("HTTP/1.1 200 OK");//响应行
writer.println("Content-Type:text/html;charset=utf-8");//响应头
writer.println("<html><head><title>bs</title></head><body><h1>测试内容</h1></body></html>");//响应体
writer.flush();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}

浙公网安备 33010602011771号