1:网络编程简介
1:网络编程是什么?
网络编程可以让程序与网络上的其他设备中的程序进行数据交互。
2:网络通信基本模式(C/S、B/S)
常见的通信模式有如下2种形式:Client-Server(CS) 、 Browser/Server(BS)
C/S架构 QQ 微信 性能高,用户体验好 成本高,升级不方便
s=Server服务器
c=Client客户端
B/S架构 开发维护成本低,服务升级方便
S=Server服务器
B=Broser浏览器
2:网络通信三要素
IP地址:设备在网络中的地址,是唯一的标识。(127.0.0.1)
端口:应用程序在设备中唯一的标识。(8080)
协议: 数据在网络中传输的规则,常见的协议有UDP协议和TCP协议。(http,tcp)
1:IP地址(网络中每台计算机的一个标识号)
-
IP(Internet Protocol):全称”互联网协议地址”,是分配给上网设备的唯一标志。
-
常见的IP分类为:IPv4和IPv6
IP地址形式:
-
公网地址、和私有地址(局域网使用)。
-
192.168. 开头的就是常见的局域网地址,范围即为192.168.0.0--192.168.255.255,专门为组织机构内部使用。
IP地址分类:
IP地址分为5类,A类保留给政府机构,B类分配给中等规模的公司,C类分配给任何需要的人,D类用于组播,E类用于实验,各类可容纳的地址数据不同。
NO. | 地址分类 | 地址范围 |
---|---|---|
1 | A类地址 | 1.0.0.1——126.255.255.254 |
2 | B类地址 | 128.0.0.1——191.255.255.254 |
3 | C类地址 | 192.0.0.1——223.255.255.254 |
4 | D类地址 | 224.0.0.1——239.255.255.254 |
5 | E类地址 | 240.0.0.1——255.255.255.254 |
如何查看本机IP地址,如何看是否与对方互通 ipcofig ping 192.168.10.23
特殊IP地址:
本机IP: 127.0.0.1或者localhost:称为回送地址也可称本地回环地址,只会寻找当前所在本机。
2:端口号(唯一标识正在计算机设备上运行的进程(程序))
-
端口号:标识正在计算机设备上运行的进程(程序),被规定为一个 16 位的二进制,范围是 0~65535。
-
端口类型 周知端口:0~1023,被预先定义的知名应用占用(如:HTTP占用 80,FTP占用21)
-
注册端口:1024~49151,分配给用户进程或某些应用程序。(如:Tomcat占 用8080,MySQL占用3306)
-
动态端口:49152到65535,之所以称为动态端口,是因为它 一般不固定分配某种进程,而是动态分配。
注意:我们自己开发的程序选择注册端口,且一个设备中不能出现两个程序的端口号一样,否则出错。
端口号的作用是什么? 唯一标识正在计算机设备上运行的进程(程序)
端口与协议有关:TCP和UDP的端口互不相干
3:协议TCP/IP协议
连接和通信数据的规则被称为网络通信协议
1:传输层的2个常见协议
-
TCP(Transmission Control Protocol) :传输控制协议
-
UDP(User Datagram Protocol):用户数据报协议
2:TCP协议特点
TCP/IP协议(定义了电子设备(比如计算机)如何连入因特网,以及数据如何在它们之间传输的标准):
Internet上不同系统之间互联的一组协议
为分散和不同类型的硬件提供通用的编程接口。
TCP/IP协议使Internet尽可能成为一个分散、无序的网络。
TCP是基于(面向)连接的协议,也就是说,在正式收发数据前,必须和对方建立可靠的连接。
-
使用TCP协议,必须双方先建立连接,它是一种面向连接的可靠通信协议。
-
传输前,采用“三次握手”方式建立连接,所以是可靠的 。
-
在连接中可进行大数据量的传输 。
-
连接、发送数据都需要确认,且传输完毕后,还需释放已建立的连接,通信效率较低。
3:TCP三次握手确立连接
4:TCP四次挥手断开连接
4:UDP协议
1:UDP协议:
-
UDP是一种无连接、不可靠传输的协议。
-
将数据源IP、目的地IP和端口封装成数据包,不需要建立连接
-
每个数据包的大小限制在64KB内
-
发送不管对方是否准备好,接收方收到也不确认,故是不可靠的
-
可以广播发送 ,发送数据结束时无需释放资源,开销小,速度快。
2:UDP协议的特点
-
用户数据报协议(User Datagram Protocol)
-
UDP是面向无连接,不可靠传输的通信协议。
-
速度快,有大小限制一次最多发送64K,数据不安全,易丢失数据。
3:UDP通信实现:发送消息、接收消息
5:网络通信的实现
思考网络编程要解决的问题:
1.如何建立两个节点(电脑)之间的网络连接?
2.如何向另外一个节点(电脑)发送信息?
3.如何从外部节点(电脑)接收一个请求并给预响应?
4.如何利用网络协议(TCP,UDP)?
1:socket编程简介
套接字使用TCP提供了两台计算机之间的通信机制。 客户端程序创建一个套接字,并尝试连接服务器的套接字。
当连接建立时,服务器会创建一个 Socket 对象。客户端和服务器现在可以通过对 Socket 对象的写入和读取来进行通信。
java.net.Socket 类代表一个套接字,并且 java.net.ServerSocket 类为服务器程序提供了一种来监听客户端,并与他们建立连接的机制。
当然,这里的ServerSocket只是socket通信中的一种,实际上,socket通信有三种模式来让我们实现:
1.流式套接字
提供了一个面向连接,可靠的数据传输服务,数据无差错,无重复的发送,且按发送顺序接收,其实他对应使用的是TCP协议
2.数据报式套接字
面向无连接,数据报以独立包形式发送,不提供无差错保证,数据可能丢失或重复,并且接收顺序无序,其实他对应使用的是UDP协议
3.原始式套接字
该接口允许对较低层次协议,如IP直接访问。可以接收本机网卡上的数据帧或数据包,对监听网络流量和分析很有用
2:TCP协议通信
1:普通实现代码
客户端代码:
package tcp;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import java.net.Socket;
import java.util.Scanner;
public class Client {
public static void main(String[] args) throws IOException {
// 创建一个socket管道 与 服务器连接
Socket socket = new Socket("127.0.0.1", 7777);
// 获取到字节输出流
OutputStream outputStream = socket.getOutputStream();
// 创建一个打印流,进行输出内容
PrintStream printStream = new PrintStream(outputStream);
Scanner scanner = new Scanner(System.in);
while (true){
System.out.println("请说");
String s = scanner.nextLine();
printStream.println(s);
printStream.flush();
}
}
}
服务端代码:
package tcp;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;
public class Server {
public static void main(String[] args) throws IOException {
// 创建一个serversocket
ServerSocket serverSocket = new ServerSocket(7777);
Socket socket = serverSocket.accept();
// 将通道交给一个新的线程去处理
// 获取字节输入流
InputStream inputStream = socket.getInputStream();
// 将字节流转化为字符流
InputStreamReader reader = new InputStreamReader(inputStream);
BufferedReader bufferedReader = new BufferedReader(reader);
String line = null ;
while((line=bufferedReader.readLine())!=null){
System.out.println(line);
}
}
}
2:多线程
Client
package tcp2;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import java.net.Socket;
import java.util.Scanner;
public class Client {
public static void main(String[] args) throws IOException {
// 创建一个socket管道 与 服务器连接
Socket socket = new Socket("127.0.0.1", 7777);
// 获取到字节输出流
OutputStream outputStream = socket.getOutputStream();
// 创建一个打印流,进行输出内容
PrintStream printStream = new PrintStream(outputStream);
Scanner scanner = new Scanner(System.in);
while (true){
System.out.println("请说");
String s = scanner.nextLine();
printStream.println(s);
printStream.flush();
}
}
}
Server:
package tcp2;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
public class Server {
public static void main(String[] args) throws IOException {
// 创建一个serversocket
ServerSocket serverSocket = new ServerSocket(7777);
// 不断的去接收请求
while(true) {
Socket socket = serverSocket.accept();
// 将连接通道交给一个新的线程来处理
new ServerNewThread(socket).start();
}
}
}
ServerNewThread:
package tcp2;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.Socket;
//创建一个线程类
public class ServerNewThread extends Thread{
private Socket socket ;
public ServerNewThread(Socket socket){
this.socket = socket ;
}
@Override
public void run() {
// 获取字节输入流
InputStream inputStream = null ;
try {
inputStream = socket.getInputStream();
// 将字节流转化为字符流
InputStreamReader reader = new InputStreamReader(inputStream);
BufferedReader bufferedReader = new BufferedReader(reader);
String line = null ;
while((line=bufferedReader.readLine())!=null){
System.out.println(line);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
3:线程池
Client:
package tcp3;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import java.net.Socket;
import java.util.Scanner;
public class Client {
public static void main(String[] args) throws IOException {
// 创建一个socket管道 与 服务器连接
Socket socket = new Socket("127.0.0.1", 7777);
// 获取到字节输出流
OutputStream outputStream = socket.getOutputStream();
// 创建一个打印流,进行输出内容
PrintStream printStream = new PrintStream(outputStream);
Scanner scanner = new Scanner(System.in);
while (true){
System.out.println("请说");
String s = scanner.nextLine();
printStream.println(s);
printStream.flush();
}
}
}
Server
package tcp3;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class Server {
// 创建一个连接池对象
/*int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler*/
static ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
5,
20,
5,
TimeUnit.SECONDS,
new ArrayBlockingQueue<>(2),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy()
);
public static void main(String[] args) throws IOException {
// 创建一个serversocket
ServerSocket serverSocket = new ServerSocket(7777);
// 不断的去接收请求
while(true) {
Socket socket = serverSocket.accept();
System.out.println(socket.getRemoteSocketAddress()+"它来了");
// 将连接通道交给一个新的线程来处理
Runnable runnable = new ServerNewThread2(socket);
threadPoolExecutor.execute(runnable);
}
}
}
Thread
package tcp3;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.Socket;
//创建一个线程类
public class ServerNewThread2 extends Thread{
private Socket socket ;
public ServerNewThread2(Socket socket){
this.socket = socket ;
}
@Override
public void run() {
// 获取字节输入流
InputStream inputStream = null ;
try {
inputStream = socket.getInputStream();
// 将字节流转化为字符流
InputStreamReader reader = new InputStreamReader(inputStream);
BufferedReader bufferedReader = new BufferedReader(reader);
String line = null ;
while((line=bufferedReader.readLine())!=null){
System.out.println(line);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
4:客户端和服务端交互,多线程接收和发送同时进行(max)
Server:
package tcp_practise3;
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;
public class Server {
private PrintWriter writer;
private BufferedReader reader;
private ServerSocket server;
private Socket socket;
private Scanner scanner;
public void getServer() {
try {
//设置端口号
server = new ServerSocket(6666);
System.out.print("服务器端已经创建成功\n");
while (true) {
System.out.print("等待客户端的连接......\n");
//接收客户端连接
socket = server.accept();
//进行IO的输入和输出
reader = new BufferedReader(new InputStreamReader(socket
.getInputStream()));
//autoFlush是允许加权
writer = new PrintWriter(socket.getOutputStream(), true);
//调用函数
getClientInfo();
}
} catch (Exception e) {
e.printStackTrace();
}
}
//接收数据
private void getClientInfo() {
try {
while (true) {
//读取一行数据
String line = reader.readLine();
//判断是否为空
if (line != null)
// 获得客户端信息
System.out.print("接收到客户端发送的信息:" + line + "\n");
System.out.print("请回复:");
}
} catch (Exception e) {
System.out.print("客户端已退出。\n");
} finally {
try {
if (reader != null) {
reader.close();// 关闭流
}
if (socket != null) {
socket.close(); // 关闭套接字
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
//发送数据
private void sendInfoToClient(){
while(true){
scanner=new Scanner(System.in);
System.out.println("请输入要发送的消息:");
String msg=scanner.nextLine();
//判断是否为quit退出
if(msg.equals("quit")){
break;
}
writer.println(msg);
}
}
//多线程的方式进行数据的交互
public static void main(String[] args) {
//创建server对象
Server server = new Server();
//此线程用于监听接收消息
new Thread(new Runnable() {
@Override
public void run() {
server.getServer();
}
}).start();
//此线程用发送消息给客户端
new Thread(new Runnable() {
@Override
public void run() {
server.sendInfoToClient();
}
}).start();
}
}
Client:
package tcp_practise3;
import java.io.*;
import java.net.Socket;
import java.util.Scanner;
public class Client {
private PrintWriter writer;
private BufferedReader reader;
private Socket socket;
private Scanner scanner;
private void connect() {
System.out.print("尝试连接......\n");
try {
// 连接Socket地址和端口号
socket = new Socket("127.0.0.1", 6666);
while (true) {
//IO流的输入和输出
writer = new PrintWriter(socket.getOutputStream(), true);
reader = new BufferedReader(new InputStreamReader(socket
.getInputStream()));
System.out.print("完成连接。\n");
getClientInfo();
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 对服务器的发送消息进行监听
*/
private void getClientInfo() {
try {
while (true) {
if (reader != null) {
//读取一行数据
String line = reader.readLine();
if (line != null)
System.out.print("接收到服务器端发送的信息:" + line + "\n");
System.out.print("请回复:");
}
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (reader != null) {
reader.close();
}
if (socket != null) {
socket.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 向服务器发送消息方法
*/
private void sendInfoToServer(){
while(true){
scanner=new Scanner(System.in);
System.out.println("请输入要发送的消息:");
String msg=scanner.nextLine();
//判断是否离开
if(msg.equals("quit")){
break;
}
writer.println(msg);
}
}
//多线程的方式进行数据交互
public static void main(String[] args) {
Client clien = new Client();
//此线程用于监听接收消息
new Thread(new Runnable() {
@Override
public void run() {
clien.connect();
}
}).start();
//此线程用发送消息给服务器
new Thread(new Runnable() {
@Override
public void run() {
clien.sendInfoToServer();
}
}).start();
}
}
3:UDP协议通信
UDP是一个面向无连接,不可靠的传输层协议.
需要用到两个类:DatagramSocket,DatagramPacket
DatagramSocket类的作用:发送和接收数据包的套接字,不维护连接状态,不产生输入输出流
DatagramPacket类:数据包,封装了数据,数据长度
DatagramPacket构造方法:
构造方法 | 说明 |
---|---|
DatagramPacket(byte [] buf,int length,InetAddress address,int port) | Buf是数据的字节数组,length是字节数组的长度,address是目标主机的IP地址,port是目标主机的端口 该构造用来构造对象,将长度为length的包发送到指定主机上的指定端口号 |
DatagramSocket的构造方法:
构造方法 | 说明 |
---|---|
DatagramSocket() | 创建DatagramSocket对象,并将其与本地主机上任何可用的端口绑定 |
DatagramSocket(int port) | 创建一个DatagramSocket对象,并将其与本地主机上的指定端口绑定 |
注意:在UDP通信中,通信的双方中,至少有一方需要指定端口号
DatagramSocket的常用方法:
常用方法 | 说明 |
---|---|
void send(DatagramPacket p) | 发送指定的数据报 |
void receive(DatagramPacket p) | 接收数据报.收到数据以后,存放在指定的DatagramPacket对象中 |
void close() | 关闭当前的DatagramSocket对象 |
1:服务端实现
UDP通信中并不需要明确的服务器概念,双方几乎是一样的操作,如下见服务器代码实现:
package cn.gl.no1;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.net.UnknownHostException;
public class Client1 {
public static void main(String[] args) {
DatagramSocket socket = null;
try {
//参与通讯的两方,至少应该有一边指定端口号
socket = new DatagramSocket();
String msg = "我要发送的短信内容";
//dp对象,是用来把要发送的信息打成一个数据包
//数据长度不能超过64K
//构造中可以传递四个参数:
//第一个参数,表示要发送的内容,需要转换成一个byte数组
byte [] b = msg.getBytes();
//第二个参数,表示发送内容的字节数
int len = b.length;
//第三个参数,表示IP地址封装的一个对象,类型是InetAddress,是接收方的地址
InetAddress ia = InetAddress.getByName("localhost");
//第四个参数,表示对方的端口号
int port = 8888;
DatagramPacket dp = new DatagramPacket(b, len, ia, port);
socket.send(dp);
byte [] bs = new byte[1024];
int length = bs.length;
DatagramPacket dpReceive = new DatagramPacket(bs, length);
socket.receive(dpReceive);
String reply = new String(bs,0,dpReceive.getLength());
System.out.println("我是client1,收到信息:"+reply);
socket.close();
} catch (SocketException e) {
e.printStackTrace();
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
2:客户端实现
package cn.gl.no1;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.net.SocketException;
public class Client2 {
public static void main(String[] args) {
DatagramSocket socket = null;
try {
socket = new DatagramSocket(8888);
//这里为了接收数据,也需要一个空的数据包
//两个参数:
//第一个参数,表示一个空的byte数组,用来接收信息内容
byte [] b = new byte[1024];
//第二个参数,表示数组长度
int len = b.length;
DatagramPacket dp = new DatagramPacket(b, len);
//接收信息
socket.receive(dp);
String msg = new String(b,0,dp.getLength());
System.out.println("我是Client2,接收到信息:"+msg);
String reply = "我很好,收到你的信息,很高兴,谢谢";
byte [] bs = reply.getBytes();
int length = bs.length;
InetAddress ia = dp.getAddress();
/*InetSocketAddress isa = (InetSocketAddress) dp.getSocketAddress();
System.out.println("isa.getHostName():"+isa.getHostName());
System.out.println("isa.getHostString():"+isa.getHostString());
System.out.println("isa.getPort():"+isa.getPort());*/
int port = dp.getPort();
DatagramPacket dpReply = new DatagramPacket(bs, length, ia, port);
socket.send(dpReply);
socket.close();
} catch (SocketException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
3:总结
1:网络通信三要素:ip , 端口号,协议
2:tcp的三挥四握
3:关于socket编程的客服端和服务端叫交互信息:tcp的多线程,线程池。udp的客户端服务端实现方法
4、建议采纳
如有建议或者错误请私信我进行修改,感谢!!!