网络编程

网络编程

1.网络编程的基本常识
目前主流的网络通信软件:QQ,微信,MSN,...
2.七层协议
ISO(国际标准委员会组织),将数据的传输从逻辑上划分了以下七层
	应用层、表示层、会话层、传输层、网络层、数据链路层、物理层
	
当发送消息时,需要按照上述从前向后的次序对发送的内容进行层层加包,然后发送过去
当接收消息时,需要按照上述相反的次序对接收到的内容层层拆包,然后再解析出来
3.常见的协议
http协议-超文本传输协议,在浏览网站的时候使用该协议
ftp协议-文件传输协议,上传,下载文件的时候使用该协议
tcp协议-传输控制协议,主要是一种面向连接的协议,比如说类似于打电话,一般网络通信的时候需要使用该协议
udp协议-用户数据报协议,是一种面向非链接的协议,类似于写信或者发短信,网络通信时也使用该协议
ip协议-互联网协议,是上述协议的底层协议

协议-就是一种约定/规则。是通信双方需要去遵循的一种机制
4.网络通信三要素
案例:比如说,我要和你说话,首先
	第一个条件就是找到你的位置
	第二个条件就是你要有接受数据的耳朵
	第三个条件就是我和你说话你能够接收到,按照什么方式接收,说的是什么语言

IP:
	InetAddress:此类表示互联网协议(IP)地址
	IP地址的底层是32位组成的整数IPv4,也有128位二进制组成的整数IPv6

端口:
	IP地址 - 可以定位到具体的一台设备
	端口号 - 可以定位到设备上具体进程
	网络编程中需要提供:IP地址+端口号已经被系统占用,因此编程需要从1025开始使用
	
	端口号:本质上是16位二进制组成的整数,范围是:0-65535,其中0-1024之间

协议:
	TCP:
		面向连接、安全可靠、效率稍低、通过三次握手建立连接
			举例:
				下载、打电话、QQ聊天
	
		编程模型:
			服务器端:
				1.创建服务器socket服务。通过ServerSocket对象
             	2.服务器必须对外提供一个端口,否则客户端无法连接
              	3.获取连接过来的客户端对象
              	4.通过客户端对象获取socket流读取客户端发来的数据打印
              	5.关闭资源、关闭流、关闭服务器
            客户端:
            	1.创建TCP客户端socket服务,使用的是socket对象
                2.如果连接成功,说明数据传输通过建立
                  通过就是socket流,是底层建立好的
                  是流就应该有输入或者输出
                  想要获取输入或者输出对象,可以找socket获取
                  可以通过getOutputStream()和getInputStream()来获取两字字节流
                3.使用输入流,将数据写入
                4.关闭资源
	
	UDP:
		面向无连接、不可靠、速度快、将数据封装包传输,数据包最大64k
			举例:
				发短信、在线视频
//1.获取本机主机地址
InetAddress inetAddress = InetAddress.getLocalHost();
//自动调用toString();字符串格式:主机名/IP地址
System.out.println(inetAddress);
//拆分地址信息,分别获取主机名和IP地址 分别打印
System.out.println(inetAddress.getHostName());
System.out.println(inetAddress.getHostAddress());
//2.获取指定主机名中的地址信息
InetAddress byName = InetAddress.getByName("DESKTOP-K7TP4JH");
System.out.println(byName);
System.out.println(inetAddress.getHostName());
System.out.println(inetAddress.getHostAddress());
5.Socket套接字
Socket套接字:
	网络上具有唯一标识的IP地址和端口号组合在一起才能构成唯一能标识符套接字
Socket原理机制:
	通信的两端都有Socket
	网络通信其实就是Socket间的通信
	数据在两个Socket间通过IO传输

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZuRTNf5z-1580969373981)(C:\Users\王龙涛\AppData\Roaming\Typora\typora-user-images\image-20200202174654642.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ORb84tkU-1580969373985)(C:\Users\王龙涛\AppData\Roaming\Typora\typora-user-images\image-20200202175534151.png)]

6.相关类的方法解析
ServerSocket类
	ServerSocket(int port)
	accept():用于监听并接收到服务器套接字的连接请求
	
	close():关闭套接字
	
Socket类
	getInputStream:用户获取此套接字的输入流
	getOutputStream:用户获取此套接字的输出流
	close:用户关闭套接字
7.编程模型代码

服务器端

//1.创建服务器socket服务。通过ServerSocket对象
//2.服务器必须对外提供一个端口,否则客户端无法连接
ServerSocket serverSocket = new ServerSocket(8888);
//3.获取连接过来的客户端对象
Socket socket = serverSocket.accept();
//4.通过客户端对象获取socket流读取客户端发来的数据打印
BufferedReader bufferedReader = new BufferedReader(newInputStreamReader(socket.getInputStream()));
String readLine = bufferedReader.readLine();
//5.关闭资源、关闭流、关闭服务器
socket.close();

客户端

//1.创建TCP客户端socket服务,使用的是socket对象,指定IP地址和端口号
socket = new Socket("localhost", 8888);
//2.如果连接成功,说明数据传输通道建立,通道就是socket流,是底层建立好的,是流就应该有输入或者输出,想要获取输入或者输出对象,可以找socket获取,可以通过getOutputStream()和getInputStream()来获取两字字节流
BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
//3.使用输入流,将数据写入
bufferedWriter.write("你好,服务器");
//4.关闭资源
bufferedWriter.flush();
bufferedWriter.close();

完整代码

/*Server.java*/
public class Server {
	public static void main(String[] args) {
		try {
			ServerSocket serverSocket = new ServerSocket(10006);
			System.out.println("等待客户端连接");
			//等待客户端连接,使用accept()
			Socket socket = serverSocket.accept();
			System.out.println("客户端连接成功");
			
			//使用字符缓冲输入流接收客户端消息
			BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
			String readLine = bufferedReader.readLine();
			System.out.println("客户端发来的消息是:"+readLine);
			
			socket.close();
			
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}

/*Client.java*/
public class Client {
	public static void main(String[] args) {
		try {
			Socket socket = new Socket("localhost", 10006);
			//建立连接之后,通过socket中的IO流进行数据传输
			//向服务器发送 你好,服务器
			//如果想要使用字符流需要使用OutputStreamWriter/InputStreamWriter 转换流
			BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
			bufferedWriter.write("你好,服务器");
			bufferedWriter.flush();
			socket.close();
			
		} catch (UnknownHostException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}

运行结果

等待客户端连接
客户端连接成功
客户端发来的消息是:你好,服务器
8.TCP传输容易出现的问题
客户端连接上服务器,两端都在等待,没有任何数据传输。

通过例程分析:因为read方法或者readLine方法时阻塞式。

解决方法:
1.自定义结束标记
2.使用shutdownInput,shutdownOutput方法。

在上述代码之中添加如下字段之后将会出现阻塞

//在Server之中添加如下字段
BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
bufferedWriter.write("客户端,你好呀");
bufferedWriter.newLine();

bufferedReader.close();
bufferedWriter.close();

//在Client之中添加如下字段
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
String readLine = bufferedReader.readLine();
System.out.println("服务器端发来的消息是:"+readLine);

上述代码中,Client想要获得服务器发来的消息,Server也想要获得客户端发来的消息,因此出现了阻塞现象

public class Server {
	public static void main(String[] args) {
		try {
			ServerSocket serverSocket = new ServerSocket(10006);
			System.out.println("等待客户端连接");
			//等待客户端连接,使用accept()
			Socket socket = serverSocket.accept();
			System.out.println("客户端连接成功");
			
			//使用字符缓冲输入流接收客户端消息
			BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
			String readLine = bufferedReader.readLine();
			System.out.println("客户端发来的消息是:"+readLine);
			
			BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
			bufferedWriter.write("客户端,你好呀");
			bufferedWriter.newLine();
			
			bufferedReader.close();
			bufferedWriter.close();
			socket.close();
			
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}
-----------------------------------------------------------------------------------------
public class Client {
	public static void main(String[] args) {
		try {
			Socket socket = new Socket("localhost", 10006);
			//建立连接之后,通过socket中的IO流进行数据传输
			//向服务器发送 你好,服务器
			//如果想要使用字符流需要使用OutputStreamWriter/InputStreamWriter 转换流
			BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
			bufferedWriter.write("你好,服务器");
			bufferedWriter.flush();
            
            //bufferedWriter.shutdownOutput();添加该行则不出现阻塞
			
			BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
			String readLine = bufferedReader.readLine();
			System.out.println("服务器端发来的消息是:"+readLine);
			
			socket.close();
			
		} catch (UnknownHostException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}
9.基于TCP上传文件
public class Server {
	public static void main(String[] args) {
		try {
			ServerSocket serverSocket = new ServerSocket(8888);
			System.out.println("等待客户端上传文件");
			Socket socket = serverSocket.accept();
			InputStream inputStream = socket.getInputStream();
			//创建字节输出流向指定服务器中输入用户上传的文件
			BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(new FileOutputStream("6.md"));
			BufferedInputStream bufferedInputStream = new BufferedInputStream(socket.getInputStream());
			//创建缓冲区
			byte[] by = new byte[1024*8];
			int num = 0 ;
			while ((num=bufferedInputStream.read())!=-1) {
				bufferedOutputStream.write(by,0,num);
				bufferedOutputStream.flush();
			}
			bufferedInputStream.close();
			bufferedOutputStream.close();
			socket.close();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}
-----------------------------------------------------------------------------------------
public class Client {
	public static void main(String[] args) {
		try {
			Socket socket = new Socket("localhost",8888);
			//读取本地文件
			BufferedInputStream bufferedInputStream = new BufferedInputStream(new FileInputStream("C://Users//王龙涛//Desktop//Java笔记//泛型.md"));
			BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(socket.getOutputStream());
			//创建缓冲区
			byte[] by = new byte[1024*8];
			int num = 0 ;
			//读取本地文件
			while ((num=bufferedInputStream.read())!=-1) {
				bufferedOutputStream.write(by,0,num);
				bufferedOutputStream.flush();
			}
			bufferedInputStream.close();
			bufferedOutputStream.close();
			socket.close();
		} catch (UnknownHostException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}
10.TCP传输练习
  • 要求客户端发送的内容由用户手动输入,使用BufferedReader

  • 要求服务器收到客户端的消息之后,想客户端回发消息"Receive!"

  • 要求服务器和客户端可以不断地进行通信,当客户端发送bye时结束通信

  • 要求服务能够同时支持多个客户端的连接,而且能够和多个客户端同事聊天,多线程

Server.java

public class Server {
	public static void main(String[] args) {
		try {
			//1.创建ServerSocket类型的对象,并且绑定端口
			ServerSocket serverSocket = new ServerSocket(8999);
			//等待客户端的连接请求
			Socket socket = serverSocket.accept();
			//用来接收客户端发来的内容
			BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
			//向客户端发送字符串内容,"I receive"
			PrintStream printStream = new PrintStream(socket.getOutputStream());
			
			while (true) {
				String string = bufferedReader.readLine();
				
				if ("bye".equalsIgnoreCase(string)) {
					System.out.println("客户端"+socket.getInetAddress()+"已经下线");
					break;
				}
				System.out.println("客户端发来的消息是:"+string);
				printStream.println("I Receive");
			}
			
			
			printStream.close();
			bufferedReader.close();
			socket.close();
			
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}

Client.java

public class Client {
	public static void main(String[] args) {
		try {
			
			//创建Socket类型的对象,并且提供IP地址和端口号
			Socket socket = new Socket("localhost",8999);
			//使用输入输出流进行通信
			BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
			//用来接收服务端发来的消息
			BufferedReader bufferedReader2 = new BufferedReader(new InputStreamReader(socket.getInputStream()));
			PrintStream printStream = new PrintStream(socket.getOutputStream());
			
			while (true) {
				
				System.out.println("请输入要发送的内容:");
				String string = bufferedReader.readLine();
				
				printStream.println(string);
				System.out.println("成功发送数据到服务器");
				
				if("bye".equalsIgnoreCase(string)) break;
				
				String string2 = bufferedReader2.readLine();
				System.out.println("服务器发来的消息是:"+string2);
				
			}
			
			bufferedReader2.close();
			bufferedReader.close();
			printStream.close();
			socket.close();
			
		} catch (UnknownHostException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
	}
}

上述代码实现了一对一通信,下面代码对上述代码进行更改,实现多对多进行通信

Server.java

public class Server {
	public static void main(String[] args) {
		try {
			//1.创建ServerSocket类型的对象,并且绑定端口
			ServerSocket serverSocket = new ServerSocket(8999);
			
			while (true) {
				System.out.println("等待客户端连接...");
				//等待客户端连接请求
				Socket socket = serverSocket.accept();
				System.out.println("客户端:"+socket.getInetAddress()+"连接成功");
				//只要有客户端连接成功,应该启动一个新线程为之服务,主线程始终接待
				new ServerThread(socket).start();
			}
			
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}

Client.java

public class Client {
	public static void main(String[] args) {
		try {
			
			//创建Socket类型的对象,并且提供IP地址和端口号
			Socket socket = new Socket("localhost",8999);
			//使用输入输出流进行通信
			BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
			//用来接收服务端发来的消息
			BufferedReader bufferedReader2 = new BufferedReader(new InputStreamReader(socket.getInputStream()));
			PrintStream printStream = new PrintStream(socket.getOutputStream());
			
			while (true) {
				
				System.out.println("请输入要发送的内容:");
				String string = bufferedReader.readLine();
				
				printStream.println(string);
				System.out.println("成功发送数据到服务器");
				
				if("bye".equalsIgnoreCase(string)) break;
				
				String string2 = bufferedReader2.readLine();
				System.out.println("服务器发来的消息是:"+string2);
				
			}
			
			bufferedReader2.close();
			bufferedReader.close();
			printStream.close();
			socket.close();
			
		} catch (UnknownHostException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch  
			e.printStackTrace();
		}
		
	}
}

ServerThread.java

public class ServerThread extends Thread{
	private Socket socket;
	public ServerThread(Socket socket) {
		this.socket=socket;
	}

	@Override
	public void run() {
		try {
			
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}
11.UDP编程
  • DatagramSocketDatagramPacket
  • 建立发送端,接收端
  • 调用Socket的发送接收方法
  • 关闭Socket
  • 发送端与接收端是两个独立的运行程序
UDP编程模型:
	主机A(接收方):
		1.创建DatagramSocket类型的对象,并且提供端口号
		2.创建DatagramPacket类型的对象,用于接收发送过来的数据
		3.使用上述的对象接收内容,使用receive()方法
		4.关闭相关资源
	主机B(发送方):
		1.创建DatagramSocket类型的对象
		2.创建DatagramPacket类型的对象,并且提供端口号和IP地址
		3.使用上述对象发送数据内容,使用send()方法
		4.关闭相关资源
DatagramSocket:是用于创建接收和发送数据报的套接字
	DatagramPacket():无参构造
	DatagramSocket(int port):创建套接字并且绑定端口号
	void receive(DatagramPacket p):用于接收数据并且放到参数指定的数据报之中
	void send():将参数指定的数据报发送出去
	void close():关闭套接字
	
DatagramPacket:用于描述数据报的内容
	DatagramPacket(byte[] buf,int length)
		构造DatagramPacket,用来接收长度为length的数据报
	DatagramPacket(byte[] buf,int length,InetAddress address,int port)
		构造数据报包,用来将长度为length的包发送到指定主机的指定端口号
		
		InetAddress getAddress() - 用于获取发送方/接收方IP地址信息
		int getPort() - 用于获取发送方/接收方的端口号信息
		int getLength() - 用于获取数据报中数据的长度

发送方代码

//1.创建DatagramSocket类型的对象 
DatagramSocket datagramSocket = new DatagramSocket();

//2.创建DatagramPacket类型的对象,提供接收方的端口号和IP地址
byte[] data = "hello".getBytes();
InetAddress inetAddress = InetAddress.getLocalHost();
DatagramPacket datagramPacket = new DatagramPacket(data, data.length, inetAddress, 8888);

//3.发送数据内容,使用send()方法
datagramSocket.send(datagramPacket);
System.out.println("成功发送数据内容");

//4.关闭套接字
datagramSocket.close();

接收方代码

//1.创建DatagramSocket类型的对象,并提供端口号
DatagramSocket datagramSocket = new DatagramSocket(8888);

//2.创建DatagramPacket类型的对象,提供接收数据内容
byte[] data = new byte[1024];
DatagramPacket datagramPacket = new DatagramPacket(data, data.length);

//3.接收数据并保存到数据报中,使用receive()方法
datagramSocket.receive(datagramPacket);

System.out.println("接收到的数据是:"+new String(data,0,data.length)+"!");

//4.关闭套接字
datagramSocket.close();
12.TCP协议和UDP协议的比较
TCP是一种面向连接协议,类似于打电话,是一种全双工字节流通信方式,服务器压力比较大,资源消耗高,发送数据效率低

UDP是一种全双工的数据报通信方式,服务器压力比较小,资源消耗小,发送数据效率相对高

//4.关闭套接字
datagramSocket.close();


**接收方代码**

```java
//1.创建DatagramSocket类型的对象,并提供端口号
DatagramSocket datagramSocket = new DatagramSocket(8888);

//2.创建DatagramPacket类型的对象,提供接收数据内容
byte[] data = new byte[1024];
DatagramPacket datagramPacket = new DatagramPacket(data, data.length);

//3.接收数据并保存到数据报中,使用receive()方法
datagramSocket.receive(datagramPacket);

System.out.println("接收到的数据是:"+new String(data,0,data.length)+"!");

//4.关闭套接字
datagramSocket.close();
12.TCP协议和UDP协议的比较
TCP是一种面向连接协议,类似于打电话,是一种全双工字节流通信方式,服务器压力比较大,资源消耗高,发送数据效率低

UDP是一种全双工的数据报通信方式,服务器压力比较小,资源消耗小,发送数据效率相对高
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值