一、什么是网络编程
二、基本的通信架构
三、网络通信的三要素
1.IP地址
1.IPv4,IPv6
2.IP域名
3.公网IP ,内网IP,本机IP
4.InetAddress
import java.net.InetAddress;public class InetAddressTest {public static void main(String[] args) throws Exception {//获取本机IP地址对象InetAddress ip1 = InetAddress.getLocalHost ();System.out.println ( ip1.getHostAddress () );//获取本机IP地址System.out.println ( ip1.getHostName () );//获得主机名//2.获取指定IP地址或者域名的对象InetAddress ip2 = InetAddress.getByName ( "WWW.baidu.com" );System.out.println ( ip2.getHostName () );//WWW.baidu.comSystem.out.println ( ip2.getHostAddress () );//39.156.66.14System.out.println ( ip2.isReachable ( 6000 ) );//判断能否联通}
}
2.端口号
3.协议
1.什么是协议
2.UDP协议
3.TCP协议
四、网络通信
1.UDP通信
1.常用API
2.代码
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;public class Client {public static void main(String[] args) throws Exception {//1.创建客户端对象(发送方)DatagramSocket socket = new DatagramSocket (7777);//2.创建数据包对象封装要发出去的数据
// DatagramPacket(byte[], int length,InetAddress,int port)
/** byte[]:代表要传输的数据* int length:发出去数据的大小* InetAddress:服务端的IP地址* int port:服务端程序的端口号**/byte[] bytes = "我是快乐的客户端,我爱你abc".getBytes ();DatagramPacket packet = new DatagramPacket (bytes,bytes.length, InetAddress.getLocalHost (),6666 );//3.开始正式发送数据包出去socket.send ( packet );System.out.println ("客户端数据发送完毕");socket.close ();//释放资源}
}import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;public class Server {public static void main(String[] args) throws Exception {//1.创建一个服务端对象并注册端口DatagramSocket server = new DatagramSocket ( 6666 );//2.创建一个数据包对象,用于接受数据byte[] bytes = new byte[1024*64];//64kbDatagramPacket packet = new DatagramPacket (bytes,bytes.length);//3.接收客户端发来的数据server.receive ( packet );//4.把字节数组中,接受的数据打印出来//读取多少倒出多少//获取本次数据包接收了多少数据int length = packet.getLength ();//通过数据包拿到客户端的IP地址System.out.println ( packet.getAddress ().getHostAddress () );//通过数据包拿到客户端的端口号System.out.println ( packet.getPort () );String s = new String ( bytes,0,length );System.out.println ( s );server.close ();}
}
3.UDP通信-多发多收
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.util.Scanner;public class Client {public static void main(String[] args) throws Exception {//1.创建客户端对象(发送方)DatagramSocket socket = new DatagramSocket ();//2.创建数据包对象封装要发出去的数据
// DatagramPacket(byte[], int length,InetAddress,int port)
/** byte[]:代表要传输的数据* int length:发出去数据的大小* InetAddress:服务端的IP地址* int port:服务端程序的端口号**/Scanner sc=new Scanner ( System.in );while (true) {System.out.println ("请说:");String msg = sc.nextLine ();byte[] bytes1 = msg.getBytes ();byte[] bytes = "我是快乐的客户端,我爱你abc".getBytes ();DatagramPacket packet = new DatagramPacket (bytes1,bytes1.length, InetAddress.getLocalHost (),6666 );//3.开始正式发送数据包出去socket.send ( packet );if ("exit".equals ( msg )){System.out.println ("欢迎下次光临,退出成功");break;}}}
}import java.net.DatagramPacket;
import java.net.DatagramSocket;public class Server {public static void main(String[] args) throws Exception {//1.创建一个服务端对象并注册端口DatagramSocket server = new DatagramSocket ( 6666 );//2.创建一个数据包对象,用于接受数据byte[] bytes = new byte[1024*64];//64kbDatagramPacket packet = new DatagramPacket (bytes,bytes.length);//3.接收客户端发来的数据while (true) {server.receive ( packet );//4.把字节数组中,接受的数据打印出来//读取多少倒出多少//获取本次数据包接收了多少数据int length = packet.getLength ();//通过数据包拿到客户端的IP地址System.out.println ( packet.getAddress ().getHostAddress () );//通过数据包拿到客户端的端口号System.out.println ( packet.getPort () );String s = new String ( bytes,0,length );System.out.println ( s );System.out.println ("--------------------------------------------------------------------");}}
}
2.TCP通信
1.如何实现TCP通信
2.客户端开发
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.Socket;public class Client {public static void main(String[] args) throws Exception {//1.创建Socket对象,并同时请求服务器端程序的连接Socket socket = new Socket ("127.0.0.1",8888);//2.从socket管道中得到一个字节输出流,用来发送数据给服务端OutputStream os = socket.getOutputStream ();//3.把低级的字节输出流,包装成数据输出流DataOutputStream dos = new DataOutputStream ( os );//4.开始写数据出去dos.writeUTF ( "在一起好吗?" );dos.close ();socket.close ();}
}
3.服务端开发
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) throws Exception {System.out.println ("----------服务端启动成功");//1.创建一个服务端对象并设置端口号ServerSocket serverSocket = new ServerSocket ( 8888 );//2.使用serverSocket对象,调用一个accept方法,等待客户端的连接请求Socket socket = serverSocket.accept ();//3.从socket通信管道中得到一个字节输入流InputStream is = socket.getInputStream ();//4.把原始字字节输入流包装成数据输入流DataInputStream dis = new DataInputStream ( is );//5.使用数据输入流读取客户端发送的消息String s = dis.readUTF ();System.out.println ( s );//获取客户端的IP地址System.out.println ( socket.getRemoteSocketAddress () );dis.close ();socket.close ();}
}
4.多发多收
import java.io.DataOutputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.util.Scanner;public class Client {public static void main(String[] args) throws Exception {//1.创建Socket对象,并同时请求服务器端程序的连接Socket socket = new Socket ("127.0.0.1",8888);//2.从socket管道中得到一个字节输出流,用来发送数据给服务端OutputStream os = socket.getOutputStream ();//3.把低级的字节输出流,包装成数据输出流DataOutputStream dos = new DataOutputStream ( os );//4.开始写数据出去Scanner scanner = new Scanner (System.in);while (true) {System.out.println ("请说");String s = scanner.nextLine ();dos.writeUTF ( s );dos.flush ();if ("exit".equals ( s )){System.out.println ("欢迎下次使用,成功退出");dos.close ();socket.close ();}}}
}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) throws Exception {System.out.println ("----------服务端启动成功");//1.创建一个服务端对象并设置端口号ServerSocket serverSocket = new ServerSocket ( 8888 );//2.使用serverSocket对象,调用一个accept方法,等待客户端的连接请求Socket socket = serverSocket.accept ();//3.从socket通信管道中得到一个字节输入流InputStream is = socket.getInputStream ();//4.把原始字字节输入流包装成数据输入流DataInputStream dis = new DataInputStream ( is );//5.使用数据输入流读取客户端发送的消息while (true) {try {String s = dis.readUTF ();System.out.println ( s );//获取客户端的IP地址System.out.println ( socket.getRemoteSocketAddress () );System.out.println ("--------------------------------------");} catch (IOException e) {System.out.println ( socket.getRemoteSocketAddress () + "离线了" );socket.close ();dis.close ();break;}}}
}
5.支持与多个客户端同时通信(上下线逻辑)
import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.Socket;public class ServerReaderThread extends Thread{private Socket socket;public ServerReaderThread(){}public ServerReaderThread(Socket socket){this.socket=socket;}@Overridepublic void run() {try {InputStream is = socket.getInputStream ();DataInputStream dis = new DataInputStream ( is );while (true) {try {String s = dis.readUTF ();System.out.println ( s );} catch (Exception e) {System.out.println ("下线了"+socket.getRemoteSocketAddress ());dis.close ();socket.close ();break;}}} catch (Exception e) {e.printStackTrace ();}}
}import java.io.DataOutputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.util.Scanner;public class Client {public static void main(String[] args) throws Exception {//1.创建Socket对象,并同时请求服务器端程序的连接Socket socket = new Socket ("127.0.0.1",8888);//2.从socket管道中得到一个字节输出流,用来发送数据给服务端OutputStream os = socket.getOutputStream ();//3.把低级的字节输出流,包装成数据输出流DataOutputStream dos = new DataOutputStream ( os );//4.开始写数据出去Scanner scanner = new Scanner (System.in);while (true) {System.out.println ("请说");String s = scanner.nextLine ();dos.writeUTF ( s );dos.flush ();if ("exit".equals ( s )){System.out.println ("欢迎下次使用,成功退出");dos.close ();socket.close ();}}}
}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) throws Exception {System.out.println ("----------服务端启动成功");//1.创建一个服务端对象并设置端口号ServerSocket serverSocket = new ServerSocket ( 8888 );while (true) {//2.使用serverSocket对象,调用一个accept方法,等待客户端的连接请求Socket socket = serverSocket.accept ();System.out.println (socket.getRemoteSocketAddress ()+"上线了");//3.把这个客户端对应的socket通信管道,交给一个独立的线程负责处理new ServerReaderThread (socket).start ();}}
}
五、TCP 通信综合案例
1.即使通信-群聊
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;public class ClientReaderThread extends Thread{private Socket socket;public ClientReaderThread(){}public ClientReaderThread(Socket socket){this.socket=socket;}@Overridepublic void run() {try {InputStream is = socket.getInputStream ();DataInputStream dis = new DataInputStream ( is );while (true) {try {String s = dis.readUTF ();System.out.println ( s );//把这个消息发给全部的客户端进行接受sendMsgAll(s);} catch (Exception e) {System.out.println ("自己下线了"+socket.getRemoteSocketAddress ());dis.close ();socket.close ();break;}}} catch (Exception e) {e.printStackTrace ();}}private void sendMsgAll(String s) throws Exception {//发送给全部在线的Socket对象for (Socket onLineSocket : Server.onLineSockets) {OutputStream os = onLineSocket.getOutputStream ();DataOutputStream dos = new DataOutputStream ( os );dos.writeUTF ( s );dos.flush ();}}
}import java.io.*;
import java.net.Socket;public class ServerReaderThread extends Thread{private Socket socket;public ServerReaderThread(){}public ServerReaderThread(Socket socket){this.socket=socket;}@Overridepublic void run() {try {InputStream is = socket.getInputStream ();DataInputStream dis = new DataInputStream ( is );while (true) {try {String s = dis.readUTF ();System.out.println ( s );//把这个消息发给全部的客户端进行接受sendMsgAll(s);} catch (Exception e) {System.out.println ("下线了"+socket.getRemoteSocketAddress ());Server.onLineSockets.remove ( socket );//离线时抹掉Socketdis.close ();socket.close ();break;}}} catch (Exception e) {e.printStackTrace ();}}private void sendMsgAll(String s) throws Exception {//发送给全部在线的Socket对象for (Socket onLineSocket : Server.onLineSockets) {OutputStream os = onLineSocket.getOutputStream ();DataOutputStream dos = new DataOutputStream ( os );dos.writeUTF ( s );dos.flush ();}}
}import java.io.DataOutputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.util.Scanner;public class Client {public static void main(String[] args) throws Exception {//1.创建Socket对象,并同时请求服务器端程序的连接Socket socket = new Socket ("127.0.0.1",8888);//创建一个独立的线程,负责随时从socket中接受来自服务端的消息new ClientReaderThread (socket).start ();//2.从socket管道中得到一个字节输出流,用来发送数据给服务端OutputStream os = socket.getOutputStream ();//3.把低级的字节输出流,包装成数据输出流DataOutputStream dos = new DataOutputStream ( os );//4.开始写数据出去Scanner scanner = new Scanner (System.in);while (true) {System.out.println ("请说");String s = scanner.nextLine ();dos.writeUTF ( s );dos.flush ();if ("exit".equals ( s )){System.out.println ("欢迎下次使用,成功退出");dos.close ();socket.close ();}}}
}import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;public class Server {public static List<Socket> onLineSockets=new ArrayList<> ();public static void main(String[] args) throws Exception {System.out.println ("----------服务端启动成功");//1.创建一个服务端对象并设置端口号ServerSocket serverSocket = new ServerSocket ( 8888 );while (true) {//2.使用serverSocket对象,调用一个accept方法,等待客户端的连接请求Socket socket = serverSocket.accept ();onLineSockets.add ( socket );//有连接就存储Socket管道System.out.println (socket.getRemoteSocketAddress ()+"上线了");//3.把这个客户端对应的socket通信管道,交给一个独立的线程负责处理new ServerReaderThread (socket).start ();}}
}
2.实现一个简易的BS架构
B/S架构
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;public class Server {public static List<Socket> onLineSockets=new ArrayList<> ();public static void main(String[] args) throws Exception {System.out.println ("----------服务端启动成功");//1.创建一个服务端对象并设置端口号ServerSocket serverSocket = new ServerSocket ( 8888 );while (true) {//2.使用serverSocket对象,调用一个accept方法,等待客户端的连接请求Socket socket = serverSocket.accept ();onLineSockets.add ( socket );//有连接就存储Socket管道System.out.println (socket.getRemoteSocketAddress ()+"上线了");//3.把这个客户端对应的socket通信管道,交给一个独立的线程负责处理new ServerReaderThread (socket).start ();}}
}import java.io.*;
import java.net.Socket;public class ServerReaderThread extends Thread{private Socket socket;public ServerReaderThread(){}public ServerReaderThread(Socket socket){this.socket=socket;}@Overridepublic void run() {//立即响应一个网页内容:“黑马程序员”给浏览器提示try {OutputStream os = socket.getOutputStream ();PrintStream ps = new PrintStream ( os );ps.println ("HTTP/1.1 200 OK");ps.println ("Content-Type:text/html;charset=UTF-8");ps.println ();//必须换行ps.println ("<div >黑马666</div>");socket.close ();//响应网页短连接} catch (Exception e) {e.printStackTrace ();}}}
使用线程池优化
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;public class Server {public static List<Socket> onLineSockets=new ArrayList<> ();public static void main(String[] args) throws Exception {System.out.println ("----------服务端启动成功");//1.创建一个服务端对象并设置端口号ServerSocket serverSocket = new ServerSocket ( 8888 );//创建一个线程池,负责处理通信管道的任务ThreadPoolExecutor pool = new ThreadPoolExecutor (16*2,16*2,0, TimeUnit.SECONDS,new ArrayBlockingQueue<> ( 8 ), Executors.defaultThreadFactory (),new ThreadPoolExecutor.AbortPolicy () );while (true) {//2.使用serverSocket对象,调用一个accept方法,等待客户端的连接请求Socket socket = serverSocket.accept ();onLineSockets.add ( socket );//有连接就存储Socket管道System.out.println (socket.getRemoteSocketAddress ()+"上线了");//3.把这个客户端对应的socket通信管道,交给一个独立的线程负责处理//把通信管道封装成任务对象pool.execute ( new ServerReaderRunnable (socket));}}
}import java.io.OutputStream;
import java.io.PrintStream;
import java.net.Socket;public class ServerReaderRunnable implements Runnable{private Socket socket;public ServerReaderRunnable(){}public ServerReaderRunnable(Socket socket){this.socket=socket;}@Overridepublic void run() {//立即响应一个网页内容:“黑马程序员”给浏览器提示try {OutputStream os = socket.getOutputStream ();PrintStream ps = new PrintStream ( os );ps.println ("HTTP/1.1 200 OK");ps.println ("Content-Type:text/html;charset=UTF-8");ps.println ();//必须换行ps.println ("<div >黑马666</div>");socket.close ();//响应网页短连接} catch (Exception e) {e.printStackTrace ();}}}