JAVA SE 实战篇 C2 网络编程基础

P1 关于网络编程的几个知识点

1 TCP/IP协议

TCP/IP协议并非一个协议,由两个协议族组成,IP协议族和TCP协议族,其中IP协议工作在网络层,TCP工作在传输层

TCP协议是端对端的通信协议,通过IP地址和IP协议可以找到通信的对象,即某设备,但还需要知道是哪个进程在进行本次通信,为了解决这个问题,TCP提出了“端”的概念,即端口号,端口号是一个2B无符号编号,从0-65535,不同的通信双方需要先指定一个端口号,以便将信息传输给指定的应用程序

2 TCP和UDP

TCP协议族有两个重要的协议:TCP协议和UDP协议,前者称为面向连接后者称为面向无连接

面向连接是保证质量的数据传输面向无连接是不保证质量的数据传输

对于速度要求更高的应用场景,可以选择面向无连接的数据传输方式(UDP),对于每个字节都不能出错的应用场景,使用面向连接的数据传输方式(TCP)

3 MAC地址和IP地址

连入网络的计算机或其它设备,都必须要有至少一块联网电路板,这块电路板叫做MAC,俗称网卡,现在的计算机网卡一般被集成到了主板上,每块网卡都有全世界唯一的6字节编号,这个编号即MAC地址,也称局域网地址,以太网地址,物理地址

但是MAC地址很难记,如果网卡可以拆卸下来,那么MAC地址也会变化,这对网络通信是很棘手的问题,为此出现了IP地址,操作系统会将IP地址和MAC地址绑定,由此避免了MAC地址变化的麻烦

127.0.0.1在网络编程中代替本地地址

P2 简单网络通信形式

1 P2P模式

P2P(对等网络模式):对端到对端,这是一种对等网络通信形式,在P2P网络中,各个联网的节点的身份都是一样的,没有高低主从之分,没有权限差异

实现P2P通信,通信双方必须知道对方的IP,并且事先确定好端口号,因为这种方式不存在管理者和被管理者,通信方式比较自由,其编程逻辑也比较简单

但这种通信形式,不利于集群管理,如对于聊天室一类的应用程序,需要一个“管理员”,能够统一管理各个接入聊天室的终端,以便完成类似群发消息,屏蔽消息,强制下线等操作

2 C/S模式

C/S(服务器/客户机模式):与P2P工作模式不同,C/S对接入网络的终端将分为服务器客户机两种,它们的身份不同,权限不同,有主有从

服务器和客户机指的都是软件,运行着服务器软件的机器就是服务器,运行客户机软件的机器就是客户机,并不是由硬件决定,也有机器可以同时作为服务器和客户机

(1) 服务器

服务器是C/S网络里的核心,如果服务器没有运行,或者服务器出现故障,那么整个网络就会瘫痪

服务器的几个特点:

1,服务器是必须众所周知的,服务器的IP地址和端口号必须公之于众,否则客户机找不到服务器
2,服务器需要响应客户机的联网请求,服务器需要不断地侦听来自客户机的联网请求,并将客户机加入到网络中,才能形成网络
3,服务器与多个客户机连接并通信,对于每一个客户机,服务器要用一个独立的线程实现与该客户机的通信
4,服务器负责处理来自客户机的信息,包括信息转发,资源请求等等
5,服务器也可以主动和客户机通信,实现对客户机的主动管理,如强制下线某客户机,通知一个客户机其它客户机的状态等等
6,服务器一旦终止工作,整个C/S网络服务崩溃

(2) 客户机

客户机的几个特点:
1,客户机必须知道服务器的IP地址和端口号,以便向服务器发出连接请求
2,客户机必须先连接到服务器,才能获得服务
3,客户机只能从服务器那里得知其它客户机的状态
4,客户机之间的通信,必须由服务器进行转发
5,客户机可以随时断开与服务器的连接,下线的客户机可以再次与服务器连接,只要服务器仍在正常工作

P3 简单的一对一C/S连接

对于C/S模式,一定有两套软件:Server和Client,无论是Server还是Client,都是基于网络通信,网络通信基于通信信道,通信信道基于端对端的TCP工作协议

配置信息接口:

package com.mec.net.one2one.core;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;

public interface INetConfig {
    
    
	
	//默认ip地址
	String ip = "127.0.0.1";
	//默认端口号,要避免使用已经被使用过的端口号
	int port = 54188;
	
	//关闭输入输出流,关闭连接
	default void close
		(InputStream is, OutputStream os, Socket socket) throws IOException {
    
    
		
		if(is != null) {
    
    
			is.close();
		}
		is = null;
		
		if(os != null) {
    
    
			os.close();
		}
		os = null;
		
		if(socket != null && !socket.isClosed()) {
    
    
			socket.close();
		}
		socket = null;
		
	}

}

1 Server类

package com.mec.net.one2one.core;

import java.io.DataInputStream;
import java.io.DataOutput;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;

public class Server implements INetConfig{
    
    
	
	//服务器端口
	private ServerSocket server;
	//服务器端口号,建议不要使用10000号前的端口号
	private int port;
	
	//连接需要用到的输入,输出流
	private DataInputStream dis;
	private DataOutputStream dos;
	
	
	public Server() {
    
    
		this.port = INetConfig.port;
	}

	public void setPort(int port) {
    
    
		this.port = port;
	}
	
	public void startServer() throws IOException {
    
    
		
		//初始化服务器连接
		server = new ServerSocket(port);
		
		//侦听连接请求,建立连接
		//这里在没有等到连接请求时,37行后的代码会被阻塞
		Socket socket = server.accept();
		
		//从建立好的连接中获取输入,输出流,类似于电话必须需要麦克风和耳机
		//接收者的接收流接收的是发送者发送流传来的消息
		//即你的耳机输出的是和你通信的人的麦克风的输入
		this.dis = new DataInputStream(socket.getInputStream());
		this.dos = new DataOutputStream(socket.getOutputStream());
		
		//连接成功后,且建立输入,输出流后就可以通信
		String mess = this.dis.readUTF();
		System.out.println("来自客户端的消息:" + mess);
		
		this.dos.writeUTF("[" + "成功建立连接!" + "]");
	
		//关闭输入输出流,断开连接
		close(this.dis, this.dos, socket);
		
	}
		

}

2 Client类

package com.mec.net.one2one.core;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;
import java.net.UnknownHostException;

public class Client implements INetConfig{
    
    
	
	//连接端口
	private Socket socket;
	//确定服务器的ip地址
	private String ip;
	//客户端连接端口号
	private int port;
	
	//连接需要用到的输入,输出流
	private DataInputStream dis;
	private DataOutputStream dos;
	
	public Client() {
    
    
		this.ip = INetConfig.ip;
		this.port = INetConfig.port;
	}
	
	public void setIp(String ip) {
    
    
		this.ip = ip;
	}

	public void setPort(int port) {
    
    
		this.port = port;
	}

	public void startConnect() 
			throws UnknownHostException, IOException {
    
    
		
		//发出连接到服务器的请求,建立连接
		socket = new Socket(ip, port);
		
		//从连接中获取输入,输出流
		this.dis = new DataInputStream(socket.getInputStream());
		this.dos = new DataOutputStream(socket.getOutputStream());
		
		//连接成功后,且建立输入,输出流后就可以通信
		this.dos.writeUTF("hello,server!");
		
		String mess = this.dis.readUTF();
		System.out.println("来自服务器的消息" + mess);
		
		//关闭输入输出流,断开连接
		close(this.dis, this.dos, socket);
		
	}
	
}

3 测试,小结

(1) 连接测试

对于Server和Client类,各自都有一个测试类:

TestServer:
在这里插入图片描述
TestClient:
在这里插入图片描述
启动时,需要先启动服务器,在启动客户端,如果先启动客户机,那么将会抛出找不到服务器IP地址的异常,拒绝连接,测试结果:

先启动服务器:
在这里插入图片描述
此时的服务器在侦听来自客户机的连接

启动客户机,服务器的控制台:
在这里插入图片描述
成功建立连接,服务器收到了来自客户机的消息

客户机的控制台:
在这里插入图片描述
服务器收到客户机的消息后,向客户机发送一条消息

(2) 小结

单对单的C/S连接,很像两个人打电话时的情况,A向B拨打电话,A是客户机,B是服务器,A发起连接,B侦听到连接后与A通信 ,这里的“连接 socket”可以理解为电话线,二者间存在一个连接,这个连接socket内包含了双方各自的输入,输出流,A发送消息需要使用输出流,B接收消息需要使用输入流,通信结束后,需要关闭输入输出流,即断开连接

但是上述的程序非常简单,没有实用意义,以聊天室为例,通信中的会话是由用户来决定的,且一个服务器将要面对多个客户端,它需要为每个客户端创建一个线程专门用来解决与该客户端的通信

猜你喜欢

转载自blog.csdn.net/weixin_43541094/article/details/110561035