一、概述
-
计算机网络:
把分布在不同地理区域的计算机与专门的外部设备用通信线路互连成一个规模大、功能强的网络系统,从而使众多的计算机可以方便地互相传递信息共享硬件、软件、数据信息等资源。 -
网络编程的目的: 直接或间接地通过网络协议与其它计算机实现数据交换,进行通讯
-
实现网络通信需要解决的两个问题:
- 如何准确地定位网络上一台或多台主机;定位主机上的特定的应用
- 找到主机后如何可靠高效地进行数据传输
二、通讯要素一:IP和端口号
1、IP的理解
- IP:唯一的标识 Internet 上的计算机(通信实体)
- 在Java中使用InetAddress类代表IP
- IP分类:IPv4 和 IPv6 ; 万维网 和 局域网
- 域名: 通过域名解析服务器将域名解析为IP地址 www.baidu.com
- 域名解析:域名容易记忆,当在连接网络时输入一个主机的域名后,域名服务器(DNS)负责将域名转化成IP地址,这样才能和主机建立连接。
- 本地IP地址:127.0.0.1 对应着:localhost
2、端口号
用于标识正在计算机上运行的进程。
- 要求:不同的进程不同的端口号
- 范围:被规定为一个 16 位的整数 0~65535。
- 分类:
- 公认端口:0~1024.被预先定义的服务通信占用(如:HTTP占用端口80,FTP占用端口21,TeInet占用端口23)。
- 注册端口:1024~49151.分配给用户进程或应用程序。(如:Tomcat占用端口8080,MSQL占用端口3306,Oracle占用端口1521等)。
- 动态私有端口:49152~65535。
3、InetAddress类
此类的一个对象就代表着一个具体的IP地址
public class InetAddressTest {
public static void main(String[] args) {
try {
//File file = new File("hello.txt");
InetAddress inet1 = InetAddress.getByName("192.168.10.14");
System.out.println(inet1);
InetAddress inet2 = InetAddress.getByName("www.baidu.com");
System.out.println(inet2);
InetAddress inet3 = InetAddress.getByName("127.0.0.1");
System.out.println(inet3);
//获取本地ip
InetAddress inet4 = InetAddress.getLocalHost();
System.out.println(inet4);
//getHostName()
System.out.println(inet2.getHostName());
//getHostAddress()
System.out.println(inet2.getHostAddress());
} catch (UnknownHostException e) {
e.printStackTrace();
}
}
三、通信要素二:网络通信协议
1、TCP和UDP的区别
TCP协议:
-
使用TCP协议前,须先建立TCP连接,形成传输数据通道
-
传输前,采用“三次握手”方式,点对点通信,是可靠的
-
TCP协议进行通信的两个应用进程:客户端、服务端。
-
在连接中可进行大数据量的传输
-
传输完毕,需释放已建立的连接,效率低
UDP协议:
-
将数据、源、目的封装成数据包,不需要建立连接
-
每个数据报的大小限制在64K内
-
发送不管对方是否准备好,接收方收到也不确认,故是不可靠的
-
可以广播发送
-
发送数据结束时无需释放资源,开销小,速度快
2、TCP三次握手和四次挥手
四、套接字Socket
端口号与IP地址的组合得出一个网络套接字:Socket
-
利用套接字(Socket)开发网络应用程序早已被广泛的采用,以至于成为事实上的标准。
-
网络上具有唯一标识的P地址和端口号组合在一起才能构成唯一能识别的标识符套接字。
-
通信的两端都要有 Socket,是两台机器间通信的端点。
-
网络通信其实就是 Socket间的通信
-
Socket允许程序把网络连接当成一个流,数据在两个Socket间通过IO传输。
-
一般主动发起通信的应用程序属客户端,等待通信请求的为服务端
-
Socket分类
- 流套接字(stream socket):使用TCP提供可依赖的字节流服务
- 数据报套接字(datagram socket):使用UDP提供“尽力而为”的数据报服务
创建一个套接字, 并连接指定ip和端口号的 服务器.
参数1. 服务器的ip地址
参数2. 服务器软件的端口号..
常用方法:
- OutputStream getOutputStream();
返回的是 , 指向通信的另一端点的输出流
- InputStream getInputStream();
返回的是 , 指向通信的另一端点的输入流
- void close();
关闭套接字
五、基于Socket的TCP编程
1、客户端Socket的工作过程
-
创建Socket:根据指定服务端的P地址或端口号构造Sσcket类对象。若服务器端响应,则建立客户端到服务器的通信线路。若连接失败,会出现异常。
-
打开连接到 Socket的输入出流:使用getInputstream()方法获得输入流,使用getOutputStream()方法获得输出流,进行数据传输
-
按照一定的协议对Socket进行读/写操作:通过输入流读取服务器放入线路的信息(但不能读取自己放入线路的信息),通过输出流将信息写入线程
-
关闭 Socket:断开客户端到服务器的连接,释放线路
说明:
-
客户端程序可以使用Socket类创建对象,创建的同时会自动向服务器方发起连接。
-
Socket的构造器是:
- Socket(String host,int port) throws UnknownHostException,EXCeption:向服务器(域名是host,端口号为port)发起TCP连接,若成功,则创建Socket对象,否则抛出异常。
- Socket(InetAddress address,int port)throws IOException:根据InetAddress对象所表示的IP地址以及端口号port发起连接
-
客户端建立 socketAtClient对象的过程就是向服务器发出套接字连接请求
2、服务器端Socket的工作过程
-
调用ServerSocket(int port):创建一个服务器端套接字,并绑定到指定端口上。用于监听客户端的请求。
-
调用accept0():监听连接请求,如果客户端请求连接,则接受连接,返回通信套接字对象。
-
调用该Socket类对象的getOutputStream()和getInputStream():获取输出流和输入流,开始网络数据的发送和接收。
-
关闭 ServerSocket和Socket对象:客户端访问结束,关闭通信套接字。
说明:
-
ServerSocket对象负责等待客户端请求建立套接字连接,类似邮局某个窗口中的业务员。也就是说,服务器必须事先建立一个等待客户请求建立套接字连接的 Server Socket对象。
-
所谓“接收”客户的套接字请求,就是 accept()方法会返回一个Socket对象
在网络编程时, 获取输入输出流的操作 ,对于客户端与服务器来说是相对的
客户端的输入流, 输入的是服务器的输出流 输出的内容
客户端与服务器获取流的顺序必须是相反的:
3、代码示例
public class Demo {
/**
* 服务器
*/
public static void main(String[] args) throws IOException {
//搭建服务器
ServerSocket server = new ServerSocket(55565);
System.out.println("服务器启动完成");
//等待客户端的连接
Socket socket = server.accept();
System.out.println("一个客户端连接了");
//发消息用输出流
OutputStream os = socket.getOutputStream();
PrintStream ps = new PrintStream(os);
ps.println("欢迎连接到服务器");
InputStream is = socket.getInputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(is));
System.out.println("服务器接收到客户端的消息:"+br.readLine());
System.out.println("服务器程序执行结束");
}
}
public class ClientDemo {
/**
* 客户端
*/
public static void main(String[] args) throws IOException {
//连接服务器
Socket socket = new Socket("localhost", 55565);
//收消息用输入流
InputStream is = socket.getInputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(is));
System.out.println("客户端接收到服务端的消息:"+br.readLine());
OutputStream os = socket.getOutputStream();
PrintStream ps = new PrintStream(os);
ps.println("服务器你好");
}
}
在服务器中加入多线程
public class Demo {
/**
* 服务器
*/
public static void main(String[] args) throws IOException {
//搭建服务器
ServerSocket server = new ServerSocket(55565);
System.out.println("服务器启动完成");
while (true){
//等待客户端的连接
Socket socket = server.accept();
System.out.println("一个用户连接了");
//如果不加入多线程的话,必须等这次交互执行完了,才能有其他用户再连接
new Thread(){
@Override
public void run() {
//和客户端进行交互,假设花10分钟
try {
Thread.sleep(60000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}.start();
}
}
}