一、什么是网络编程
二、基本的通信架构
三、网络通信的三要素
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.com
System.out.println ( ip2.getHostAddress () );//39.156.66.14
System.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];//64kb
DatagramPacket 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];//64kb
DatagramPacket 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;
}
@Override
public 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;
}
@Override
public 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;
}
@Override
public 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 );//离线时抹掉Socket
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.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 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 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;
}
@Override
public 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 ("黑马666
");
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 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;
}
@Override
public 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 ("黑马666
");
socket.close ();//响应网页短连接
} catch (Exception e) {
e.printStackTrace ();
}
}
}