本帖最后由 鸭鸭鸭鸭 于 2019-3-8 15:42 编辑
网络编程
1. 网络协议
通过计算机网络可以使多台计算机实现连接,位于同一个网络中的计算机在进行连接和通信时需要遵守一定的规则,这就好比在道路中行驶的汽车一定要遵守交通规则一样。在计算机网络中,这些连接和通信的规则被称为网络通信协议,它对数据的传输格式、传输速率、传输步骤等做了统一规定,通信双方必须同时遵守才能完成数据交换。 网络通信协议有很多种,目前应用最广泛的是TCP/IP协议(Transmission Control Protocal/Internet Protoal传输控制协议/英特网互联协议),它是一个包括TCP协议和IP协议,UDP(User Datagram Protocol)协议和其它一些协议的协议组,在学习具体协议之前首先了解一下TCP/IP协议组的层次结构。
2. TCP/IP协议
Transmission Control Protocol/Internet Protocol的简写,中译名为传输控制协议/因特网互联协议,是Internet最基本的协议、Internet国际互联网络 的基础,由网络层的IP协议和传输层的TCP协议组成。TCP/IP 定义了电子设备如何连入因特网,以及数据如何在它们之间传输的标准。协议采用了4层的层级结构,每一层都呼叫它的下一层所提供的网络来完成自己的需求。 通俗而言:TCP负责发现传输的问题,一有问题就发出信号,要求重新传输,直到所有数据安全正确地传输到目的地。 而IP是给因特网的每一台电脑规定一个地址。 从协议分层模型方面来讲,TCP/IP由四个层次组成:网络接口层、网际层、运输层、应用层。
上图中,TCP/IP协议中的四层分别是应用层、传输层、网络层和链路层,每层分别负责不同的通信功能,接下来针对这四层进行详细地讲解。
链路层:
链路层是用于定义物理传输通道,通常是对某些网络连接设备的驱动协议,例如针对光纤、网线提供的驱动。
网络层:
网络层是整个TCP/IP协议的核心,它主要用于将传输的数据进行分组,将分组数据发送到目标计算机或者网络。
传输层:
主要使网络程序进行通信,在进行网络通信时,可以采用TCP协议,也可以采用UDP协议。
应用层:
主要负责应用程序的协议,例如HTTP协议、FTP协议等。
2.1. 应用层
主要负责应用程序的协议,例如HTTP协议、FTP协议等。 直接面向用户的的具体应用,负责处理特定的应用程序细节。
HTTP协议 HTTPS协议 FTP协议 TELNET协议 DNS协议 等
例如:
HTTP
超文本传输协定(HTTP,Hypertext Transfer Protocol)是互联网上应用最为广泛的一种网络协议。所有的WWW文件都必须遵守这个标准。(Hypertext Transfer Protocol,超文本传输协议),主要用于普通浏览。 默认端口80。
HTTPS
超文本传输安全协议(缩写:HTTPS,英语:Hypertext Transfer Protocol Secure)是超文本传输协议和SSL/TLS的组合,用以提供加密通讯及对网络服务器身份的鉴定。HTTPS连接经常用于万维网上的交易支付和企业信息系统中敏感信息的传输。HTTP协议的安全版本。与HTTP的URL由”http://” 起始且默认使用端口80不同,HTTPS的URL由“https://”起始且默认使用端口443。HTTP 是不安全的,且攻击者通过监听和中间人攻击等手段,可以获取网站帐户和敏感信息等。HTTPS被设计为可防止前述攻击,并(在没有使用旧版本的SSL时)被认为是安全的。HTTP协议的安全版本,默认使用端口443。
FTP
文件传输协议(英文:File Transfer Protocol,简称为FTP)是用于在网络上进行文件传输的一套标准协议。它属于网络传输协议的应用层。FTP服务一般运行在20和21两个端口。
2.2. 传输层
传输层是整个TCP/IP协议的控制部分,负责应用进程之间的端到端的通信。传输层要系统的管理数据信息的流动,还要提供可靠地传输协议,确保数据准确、有序到达目的地。 |
TCP/IP协议在这一层主要提供了两个协议: 传输控制协议TCP和用户数据报协议UDP。
2.2.1.TCP
Transmission control protocol (传输控制协议)
可靠、面向连接。
建立连接,形成传输数据的通道。
在连接中进行大数据量传输
通过三次握手完成连接,是可靠协议
必须建立连接,效率会稍低
用于:文件传输,接收邮件
三次握手:
第一次握手: 建立连接时,客户端发送数据包到服务器,并进入SYN_SEND状态,等待服务器确认。
第二次握手: 服务器收到数据包,必须确认客户的数据包,同时自己也发送一个数据包,此时服务器进入SYN_RECV状态;
第三次握手: 客户端收到的服务器的数据包,向服务器发送确认包,此包发送完毕,客户端和服务器进入ESTABLISHED状态,完成三次握手。
完成三次握手,客户端与服务器开始传送数据
2.2.2. UDP
User datagram protocol (用户数据报协议)
不可靠、无连接
将数据及源和目的封装成数据包中,不需要建立连接
每个数据报的大小在限制在64k内
不需要建立连接,速度快
视频通话,即时通信,IP电话 (VoIP)电话
2.3. 网络层
网络层是整个TCP/IP协议的核心,它主要用于将传输的数据进行分组,将分组数据发送到目标计算机或者网络。
2.4. 链路层
链路层是用于定义物理传输通道,通常是对某些网络连接设备的驱动协议,例如针对光纤、网线提供的驱动。
3. IP
当一台计算机要与另一台计算机通信时,需要知道另外一台计算机的地址。互联网协议(Internet Protocol IP)可以用来唯一的标识互联网上的计算机。
IP地址是网络中用于区分不同计算机的数字标识,由32个二进制位组成.并将其分成4组,每组8位.32位的IP地址由于用二进制表示不便于记忆,因而采用称为点分十进制的方法表示。
IP地址由4段用点隔开的0~255的十进制数组成例如,192.168.10.252 . 例如: 192.168.1.1 目前IP协议的版本号是4(IPv4)下一个版本是IPv6。IPv4采用32位(bit)地址长度,只有大约43亿个地址(2^32)。 IPv6具有更大的地址空间,IPv6中IP地址的长度为128位,即最大地址个数为2^128。 IP地址就好像电话号码:有了某人的电话号码,你就能与他通话了。同样,有了某台主机的IP地址,你就能与这台主机通信了。
4. 域名和端口
计算机端口(port) 是计算机与外界通信交流的出口。计算机通过端口区分Internet的各种服务. |
4.1.域名
由于ip地址是数字,不容易记忆.所以就将他们映射为域名(domain name) 例如: www.itcast.cn 在互联网中有叫做域名服务器(Domain Name Server DNS)的服务器,它可以把这个域名转换为Ip地址.然后根据这个Ip地址实现通信. 在命令行中使用: ipconfig可以查询自己的IP. ping 尝试连接某ip地址 特殊Ip地址 127.0.0.1 本地回路地址主机名:localhost,ping这个地址可以测试网卡是否可用
4.2. 端口号
计算机上的每一个程序都会对应一个端口号.例如使用计算机可以进行E-Mail(邮件) WWW(浏览器) FTP(文件传输)操作. 这些程序都需要通过互联网进行数据的传输.计算机是通过端口进行的判断数据的归属,不同的程序有不同的端口. 端口是有范围限制的,端口号只有整数0-65535。
(1)公认端口(wellkonwn ports) 范围是0-1023 系统保留端口。
(2)注册端口(registered ports) 范围1024-49151松散绑定一些服务,虽 然有一些服务绑定这些端口,这些端口还可以应用于其他服务。
(3)动态/私有端口(dynamic and/)范围49152-65535 动态分配是指当一个系统进程或应用程序进程需要网络通信时,它向主机申请一个端口,主机从可用的端口号中分配 一个供它使用。当这个进程关闭时,同时也就释放了所占用的端口号。
所以, 如果程序中需要指定端口,那么尽量使用1024以上的, 1024以下基本都被系统程序占用了。
5. Java程序设计的网络编程
网络编程是指编写运行在多个设备(计算机)的程序,这些设备都通过网络连接起来,实现网络间多设备通信。Java是通过Socket机制实现网络间的数据通信的。
那么什么是Socket呢?简单地说,Socket,就是两台主机之间逻辑连接的端点,Socket就是为网络服务提供的一种机制。通信的两端都有Socket。网络通信其实就是Socket间的通信。数据在两个Socket间通过IO传输。
java 是面向对象的编程语言,在java.net 包中的提供了一系列的类和接口来支持网络编程。你可以直接使用这些类和接口,来专注于解决问题,而不用关注通信细节。 java.net 包中提供了两种常见的网络协议的支持:
TCP:
TCP 是传输控制协议的缩写,它保障了两个应用程序之间的可靠通信。通常用于互联网协议,被称 TCP /IP。
UDP:
UDP 是用户数据报协议的缩写,一个无连接的协议。提供了应用程序之间要发送的数据的数据包。
5.1. InetAddress
既然要使用网络编程,那么Ip地址是需要经常使用的,Java提供了一个类来表示IP地址。java.net.InetAddress类是Java的IP地址封装类,它不需要用户了解如何实现IP地址的细节。 InetAddress类没有构造方法,要创建该类的实例对象,可以通过该类的静态方法获得该对象
方法:
public static InetAddress getLocalHost()
获得本机的InetAddress对象,当查找不到本地机器的地址 时,发生UnknownHostException异常。
public static InetAddress getByName (String host) 该方法获得由host指定的InetAddress对象,host是计算机的域名(例如 gz.itcast.cn),其作用跟IP地址一样,只不过域名标识计算机比IP标识计算机更易于记忆。如果找不到主机会发生UnknownHostException异常。
public static InetAddress[] getAllByName(String host) 使用getAllByName方法可以从DNS上得到域名对应的所有的IP.这个方法返回一个InetAddress类型的数组出错了同样会抛出UnknownException异常
5.1.1. 获取本机IP地址
private static void tsstLocalHost() throws UnknownHostException {
// 获取本机Ip地址
InetAddress localHost = InetAddress.getLocalHost();
// 通过InetAddress对象,方法实现操作
System.out.println(localHost);
// 获取Ip 十进制
String hostAddress = localHost.getHostAddress();
System.out.println(hostAddress);
// 获取主机名
String hostName = localHost.getHostName();
System.out.println(hostName);
// ip字节表示形式
byte[] address = localHost.getAddress(); System.out.println(Arrays.toString(address));
// Java 的字节数是有符号的,能存-128~ 127. System.out.println(Integer.toBinaryString(192)); System.out.println(Integer.toBinaryString(-64));
} |
5.1.2. 根据域名获取Ip
获取www.baidu.con 域名的Ip地址。
获取www.baidu.com 域名的所有IP地址。
// 获取baidu的ip地址
private static void testBaidu() throws UnknownHostException {
// 获取www.baidu.com Ip地址
String host = "www.baidu.com";
// InetAddress byName = InetAddress.getByName(host);
// String hostAddress = byName.getHostAddress();
// System.out.println(hostAddress); // 61.135.169.125
// 获取www.baidu.com
InetAddress[] allBynameBaidu = InetAddress.getAllByName(host);
for (InetAddress i : allBynameBaidu) { System.out.println(i.getHostAddress());
}
} |
5.2. Java中的UDP
需要学习使用的类:
DatagramSocket
DatagramPacket
需要建立发送端,接收端。
建立数据包。将数据存储在数据包中.
调用Socket的发送接收方法。
关闭Socket。
发送端与接收端是两个独立的运行程序。
5.2.1.UDP发送
第一步:创建Socket
//需要创建Socket, 发送端不需要指定ip地址和端口, 使用本机地址发送, 会自动找到未使用的端口。
//需要使用DatagramSocket此类表示用来发送和接收数据报包的套接字。
java.net.DatagramSocket
//可以通过构造函数创建该Socket对象 DatagramSocket socket = new DatagramSocket();
第二步:创建数据包
//发送时需要创建数据包如何创建数据包?使用DatagramPacket
java.net.DatagramPacket //此类表示数据报包。
//创建数据包时需要通过构造函数指定发送的数据(字节数组),数据长度(数组长度),接受方的IP地址
(InteAddress类),接受方端口号(port)。 构造函数: DatagramPacket(byte[] buf, int length, InetAddress
address, int port) //构造数据报包,用来将长度为 length 的包发送到指定主机上的指定端口号。
第三步:发送数据
//有了Socket 有了数据,如何发送数据包?使用Socket的send方法将数据包发送出去
void send(DatagramPacket p) //从此套接字发送数据报包。
第四步:关闭Socket
//使用Socket的close方法关闭。
void close() //关闭此数据报套接字。
注意: 在发送端,要在数据包对象中明确目的地IP及端口。
5.2.2. UDP接收
第一步:需要创建Socket,
接收时必须指定端口号.
DatagramSocket socket = new DatagramSocket(8888);
第二步:创建数据包, 接收时也需要创建数据包, 用来存储数据. 需要一个字节数组. DatagramPacket packet = newDatagramPacket(new byte[1024], 1024); 接收数据
第三步:接收数据 使用DatagramSocket 的receive方法接收数据.该方法需要指定数据包. socket.receive(packet);
第四步: 从数据包中获取数据
byte[] data = packet.getData();
第五步:获取数据长度 int len = packet.getLength();
第六步:获取发送端ip地址 packet.getInetAddress().getHostAddress();
第七步:获取发送端端口号packet.getPort(); 第八步:关闭socket socket.close(); 注意: 在接收端,要指定监听的端口。
5.2.3.案例一
发送数据:使用UDP将一串数据发送出去,并使用UDP接收.要求获取到发送者的Ip地址端口号.并将信息显示出来.
public class UDPSend {
public static void main(String[] args) throws SocketException,
UnknownHostException, IOException {
System.out.println("UDP发送端启动,准备发送数据");
// 创建Socket,
DatagramSocket socket = new DatagramSocket();
// 创建数据包
String data = "你好我是UDP";
InetAddress ip = InetAddress.getByName("192.168.10.252"); DatagramPacket packet = new DatagramPacket(data.getBytes(), data.getBytes().length, ip, 50000);
// 发送数据
socket.send(packet);
// 关闭socket
socket.close();
System.out.println("udp发送端数据发送完毕");
}
} |
接收数据
public class UdpReceive {
public static void main(String[] args) throws SocketException, IOException {
System.out.println("这是Udp接收端,已经启动,等待接收");
// 创建Socket 接收端必须指定端口号
DatagramSocket socket = new DatagramSocket(50000);
// 创建接受的数据包
byte[] byt = new byte[1024];
DatagramPacket packet = new DatagramPacket(byt, byt.length);
// 接收
socket.receive(packet);
// 获取发送方ip
InetAddress address = packet.getAddress();
String hostAddress = address.getHostAddress();
// 获取发送方端口号
int port = packet.getPort();
// 获取数据
byte[] data = packet.getData();
// 获取数据长度
int dataLen = packet.getLength();
System.out.println("IP:" + hostAddress + "端口号:" + port + "发送了: "
+ new String(data, 0, dataLen));
socket.close();
System.out.println("接收端接受完毕...");
}
}
5.2.4. 异常处理
发送端:
public class UDPSend {
public static void main(String[] args) {
System.out.println("UDP发送端启动,准备发送数据");
// 创建Socket,
DatagramSocket socket = null;
try {
socket = new DatagramSocket();
// 创建数据包
String data = "你好我是UDP";
InetAddress ip = InetAddress.getByName("255.255.255.255");
DatagramPacket packet = new DatagramPacket(data.getBytes(),
data.getBytes().length, ip, 50000);
// 发送数据
socket.send(packet);
} catch (IOException e) {
e.printStackTrace();
} finally {
// 关闭socket
socket.close();
}
System.out.println("udp发送端数据发送完毕");
}
} |
接收端:
public class UdpReceive {
public static void main(String[] args) {
System.out.println("这是Udp接收端,已经启动,等待接收");
// 创建Socket 接收端必须指定端口号
DatagramSocket socket = null;
try {
socket = new DatagramSocket(50000);
// 创建接受的数据包
byte[] byt = new byte[1024];
DatagramPacket packet = new DatagramPacket(byt, byt.length);
// 接收
socket.receive(packet);
// 获取发送方ip
InetAddress address = packet.getAddress();
String hostAddress = address.getHostAddress();
// 获取发送方端口号
int port = packet.getPort();
// 获取数据
byte[] data = packet.getData();
// 获取数据长度
int dataLen = packet.getLength();
System.out.println("IP:" + hostAddress + "端口号:" + port + "发送了: "
+ new String(data, 0, dataLen));
} catch (IOException e) {
e.printStackTrace();
} finally {
socket.close();
}
System.out.println("接收端接受完毕...");
}
} |
5.2.5. 案例二
循环发送接收 通过控制台录入用户信息,使用UDP 发送出去,另外一个UDP进行接收发送端可以持续发送信息,接收端可以持续接收信息.当发送端输入bye 结束。[/td][/tr]
发送方
public class TestUdpSend {
public static void main(String[] args) {
// 发送端
System.out.println("这是UDP发送端,即将发送数据");
DatagramSocket socket = null;
// 创建Socket
try {
socket = new DatagramSocket();
BufferedReader br = new BufferedReader(new InputStreamReader(
System.in));
while (true) {
System.out.println("请输入发送信息:");
String message = br.readLine();
if (!"bye".equals(message)) {
break;
}
// 准备数据包,封装数据
DatagramPacket packet = new DatagramPacket(message.getBytes(),
message.getBytes().length,
InetAddress.getByName("127.0.0.1"), 50000);
// 发送数据
socket.send(packet);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
// 关闭关闭socke
socket.close();
}
System.out.println("发送端数据数据发送完毕");
|
接收方
public class TestUdpReverse {
public static void main(String[] args) {
System.out.println("这是UDP接收端,准备接收数据");
// 创建Socket,指定端口
DatagramSocket socket = null;
try {
socket = new DatagramSocket(50000);
// 创建数组
byte[] byt = new byte[1024];
// 创建packet
DatagramPacket packet = new DatagramPacket(byt, byt.length);
while (true) {
// 接收数据
socket.receive(packet);
// 获取发送发ip
String ip = packet.getAddress().getHostAddress();
// 获取发送方端口
int port = packet.getPort();
// 获取数据长度
int dataLen = packet.getLength();
// 获取数据
byte[] data = packet.getData();
// 转为字符串
String mess = new String(data, 0, dataLen);
System.out
.println("发送方:" + ip + " 端口:" + port + " 发送了:" + mess);
if ("bye".equals(mess)) {
break;
}
}
} catch (IOException e) {
e.printStackTrace();
} finally {
socket.close();
}
} |
5.3. Java的TCP
面向连接, 数据安全, 区分服务器端和客户端.
TCP分为Socket(客户端)和ServerSocket(服务端)
需要分别建立客户端和服务器端
客户端和服务端建立连接后,通过Socket中的IO流进行数据的传输
需要关闭关闭socket
同样,客户端与服务器端是两个独立的应用程序。
5.3.1. TCP客户端
第一步:创建客户端Socket 需要指定连接到服务器的地址和端口号, 并尝试连接 客户端需要明确服务器的ip地址以及端口,这样才可以去试着建立连接,如果连接失败,会出现异常。
Socket socket = new Socket("192.168.1.220", 8888);
第二步:连接成功获取输入输出流
连接成功,说明客户端与服务端建立了通道,那么通过IO流就可以进行数据的传输,而Socket对象已经提供了输入流和输出流对象,通getInputStream(),getOutputStream()获取即可。
socket.getInputStream();
socket.getOuputStream();
第三步: 将数据写出到服务端
使用字节输出流的write() 方法
第四步:关闭socket
调用close方法
连接成功之后获取输入输出流
socket.getInputStream();
socket.getOuputStream();
获取流之后就可以通过输入输出流发送和读取数据了, 客户端的输入流连接服务端输出流, 客户端输出流连
接服务端输入流
客户端案例:
public class TcpClient {
public static void main(String[] args) throws IOException, IOException {
System.out.println("客户端启动...");
// 创建客户端
Socket socket = new Socket("127.0.0.1", 50000);
// 与服务端建立连接,获取输入输出流
InputStream in = socket.getInputStream();
OutputStream out = socket.getOutputStream();
// 将数据写出到服务端
System.out.println("客户端发送数据...");
out.write("Tcp,你好我是客户端...".getBytes());
// 关闭socket
out.close();
}
}
5.3.2. TCP服务端
第一步: 创建服务端 ServerSocket, 需要指定端口号. 客户端连接的就是这个端口.
java.net.ServerSocket
创建ServerSocket
ServerSocket serverSocket = new ServerSocket(8888);
第二步:监听客户端建立连接
通过accept方法,该方法会监听客户端连接,没有连接成功之前,处于阻塞状态。
Socket accept() //侦听并接受到此套接字的连接。
该方法会侦听是否有客户端连接,如果有建立连接,并获取客户端的Socket
也就是说服务端创建之后可以获取客户端连接, 返回一个Socket对象, 这个Socket就是和客户端连接的Socket
Socket socket = serverSocket.accept();
第三步: 接受客户端的数据,获取客户端的数据
Socket
//服务端获取这个socket的输入输出流, 就可以和客户端发送接收数据了
socket.getInputStream();
socket.getOutputStream();
第四步:获取客户端的ip地址和端口号
使用服务端获取的Socket 获取ip地址和端口.
InetAddress getInetAddress() // 返回套接字连接的地址。
int getPort() // 返回此套接字连接到的远程端口。
第五步: 关闭客户端和服务端
在服务端中分别调用close方法.
close();
5.3.3. 案例一
客户端向服务端发送数据,服务端获取并进信息打印在控制台
public class TcpServer {
public static void main(String[] args) throws IOException {
System.out.println("服务端启动:");
// 服务端,监听端口
ServerSocket server = new ServerSocket(50000);
// 使用acept进入侦听状态,获取客户端数据
Socket socket = server.accept();
[/table][table=50%,Wheat]
// 获取输入流输出流
InputStream in = socket.getInputStream();
OutputStream out = socket.getOutputStream();
// 获取客户端ip,端口
InetAddress inetAddress = socket.getInetAddress();
String ip = inetAddress.getHostAddress();
int port = socket.getPort();
// 获取客户端数据
byte[] byt = new byte[1024];
System.out.println("服务端接受数据:");
int len = in.read(byt);
String mess = new String(byt, 0, len);
System.out.println("该客户端:" + ip + ":" + port + "发送了:" + mess);
// 关闭客户端
socket.close();
// 关闭服务端
server.close();
}
}
5.3.4. 案例二
客户端给服务端发送信息,并接收服务端的回馈信息。
例如:
首先客户端向服务器通话
客户端:你好吗?服务器
服务器接到信息
服务器:收到客户端消息
客户端说:你好吗?服务器
服务器回馈信息
服务器:收到客户端消息
客户端说:你好吗?
服务器说:我很好
客户端接到服务器的信息
客户端:你好吗?服务器
客户端: 收到服务器消息
服务器说: 我很好
客户端:
public class TcpClient {
public static void main(String[] args) throws IOException, IOException {
System.out.println("客户端启动...");
// 创建客户端
Socket socket = new Socket("127.0.0.1", 50000);
// 与服务端建立连接,获取输入输出流
InputStream in = socket.getInputStream();
OutputStream out = socket.getOutputStream();
|
BufferedReader br = new BufferedReader(new InputStreamReader( System.in)); byte[] byt = new byte[1024];
while (true) {
// 将数据写出到服务端
System.out.println("客户端发送数据...");
System.out.println("你说:");
String mess = br.readLine();
System.out.println("你告诉了服务端:");
out.write(mess.getBytes());
// 获取服务端的回话
int len = in.read(byt);
System.out.print("服务端告诉我了:");
System.out.println(new String(byt, 0, len));
if ("bye".equals(mess)) {
break;
}
}
// 关闭socket
out.close();
}
} |
服务端
public class TcpServer {
public static void main(String[] args) throws IOException {
System.out.println("服务端启动:");
// 服务端,监听端口
ServerSocket server = new ServerSocket(50000);
// 使用acept进入侦听状态,获取客户端数据
Socket socket = server.accept();
// 获取输入流输出流
InputStream in = socket.getInputStream();
OutputStream out = socket.getOutputStream();
// 获取客户端ip,端口
InetAddress inetAddress = socket.getInetAddress();
String ip = inetAddress.getHostAddress();
int port = socket.getPort();
byte[] byt = new byte[1024];
BufferedReader br = new BufferedReader(new InputStreamReader(
System.in));
while (true) {
// 获取客户端数据
System.out.println("服务端接收数据:");
int len = in.read(byt);
String mess = new String(byt, 0, len);
System.out.println("客户端:" + ip + ":" + port + "告诉了服务端:" + mess);
// 向客户端回话.
System.out.println("你要告诉客户端:");
String str = br.readLine();
out.write(str.getBytes());
if ("bye".equals(mess)) {
break;
}
}
// 关闭客户端
socket.close();
// 关闭服务端
server.close();
}
} |
5.3.5. 案例三
服务器可以客户端的连接.客户端通过键盘录入数据,发送到服务端,服务端接收到数据后,转换成大写在返回给客户端。
需求: 服务器可以客户端的连接.客户端通过键盘录入数据,发送到服务端,服务端接收到数据后,转换成大写在返回给客户端。
客户端:
public class Cilent {
public static void main(String[] args) throws UnknownHostException,
IOException {
System.out.println("客户端启动...");
Socket socket = new Socket("127.0.0.1", 50000);
InputStream in = socket.getInputStream();
OutputStream out = socket.getOutputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
System.out.println("请录入:");
String mess = br.readLine();
out.write(mess.getBytes());
System.out.println("-----------获取服务器的回馈--------------");
byte[] byt = new byte[1024];
int len = in.read(byt);
System.out.println(new String(byt, 0, len));
socket.close();
}
} |
5.3.6. 多线程服务器
服务器一般是为多个客户端同时服务的, 当每个客户端连接到服务器时, 可以开一条单独的线程处理这个连接 服务器端
public class Server {
public static void main(String[] args) throws IOException {
System.out.println("服务器启动...");
ServerSocket server = new ServerSocket(50000);
while (true) {
Socket socket = server.accept();
ServerRunnable serverRunnable = new ServerRunnable(socket);
Thread t1 = new Thread(serverRunnable);
t1.start();
}
}
}
class ServerRunnable implements Runnable { Socket socket;
ServerRunnable(Socket socket) {
this.socket = socket;
}
@Override
public void run() {
try {
InputStream in = socket.getInputStream();
OutputStream out = socket.getOutputStream();
byte[] byt = new byte[1024];
int len = in.read(byt);
String str = new String(byt, 0, len);
System.out.println(str);
System.out.println("------------服务器转大写--------------");
String upperCase = str.toUpperCase();
System.out.println(upperCase);
// 转大写,写回给客户端
out.write(upperCase.getBytes());
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
5.3.7. 上传文件
分析:
先启动服务器,再启动客户端.客户端和服务端需要建立连接,服务端向客户端反馈信息.告知客户端”连接成功,请上传
文件路径:
客户端:
一: 客户端需要检测文件是否存在,是否是文件.
二: 客户端将文件名,和文件的长度发给服务器,服务器以此判断文件是否存在,文件大小用以验证文件是否上传完毕.
三: 客户端根据服务器端的反馈信息,如果服务器存在该文件,上传结束,不存在开始上传.
四: 客户端开始上传, 使用字节输入流,将文件加载到输入流中,读取输入流,通过socket的输出流写到服务器.
五: 服务器:
根据客户传送的信息,判断文件是否存在如果存在不再上传,需要将结果反馈给客户端.
如果不存在,新建字节输出流,将客户端传送的数据,写到服务器中.判断服务器文件的长度和客户端文件的长度是否一
致.一致上传完毕
客户端
public class Client {
public static void main(String[] args) throws IOException {
System.out.println("客户端启动");
Socket socket = new Socket("127.0.0.1", 5000);
InputStream ips = socket.getInputStream();
OutputStream ops = socket.getOutputStream();
ops.write("客户端请求上传文件".getBytes());
byte[] byt = new byte[1024];
int len = 0;
len = ips.read(byt);
String mess = new String(byt, 0, len);
System.out.println(mess);
// 获取上传文件
File file = getFile();
// 获取上传文件名
mess = file.getName();
System.out.println("文件名:" + mess);
//
ops.write(mess.getBytes());
// 上传文件大小
ops.write(String.valueOf(file.length()).getBytes());
len = ips.read(byt);
mess = new String(byt, 0, len);
if ("文件存在".equals(mess)) {
System.out.println("文件存在,无需上传");
return;
}
// 上传
FileInputStream fis = new FileInputStream(file);
while ((len = fis.read(byt)) != -1) {
ops.write(byt, 0, len);
}
System.out.println("文件上传完毕");
len = ips.read(byt);
System.out.println("服务器说:" + new String(byt, 0, len));
ops.close();
socket.close();
}
private static File getFile() {
// 用户输入文件路径
Scanner sc = new Scanner(System.in);
while (true) {
System.out.println("请输入文件的路径名:");
String nextLine = sc.nextLine();
// 创建File
File file = new File(nextLine);
if (!file.exists()) {
System.out.println("文件不存在");
continue;
}
if (file.isDirectory()) {
System.out.println("不支持目录");
continue;
}
return file;
}
} |
服务端
public class Server {
public static void main(String[] args) throws IOException {
System.out.println("服务端启动");
ServerSocket server = new ServerSocket(5000);
Socket client = server.accept();
InputStream ips = client.getInputStream();
OutputStream ops = client.getOutputStream();
// 2
byte[] byt = new byte[1024];
int len = -1;
len = ips.read(byt);
System.out.println(new String(byt, 0, len));
// 3
ops.write("接收请求,请上传".getBytes());
// 5
len = ips.read(byt);
String fileName = new String(byt, 0, len);
System.out.println("文件名:" + fileName);
// 根据文件名创建File对象,
File file = new File("d:\\", fileName);
// 8获取文件长度
len = ips.read(byt);
long length = Long.parseLong(new String(byt, 0, len));
// 9判断文件是否存在
if (file.exists() && file.length() == length) {
System.out.println("文件已经存在");
ops.write("文件存在".getBytes());
return;
} else {
ops.write("文件不存在".getBytes());
}
// 12接收
FileOutputStream fos = new FileOutputStream(file);
while ((len = ips.read(byt)) != -1) {
fos.write(byt, 0, len);
fos.flush();
//服务器中的写出文件时,如果客户端socket没有关闭,那么读不到文件末尾,需要强制结束(判断文件长度是否一致)
if (file.length() == length) {
System.out.println("文件上传完毕...");
ops.write("文件接收完毕".getBytes());
break;
}
}
fos.close();
client.close();
server.close();
}
} |
6.总结:
想要了解网络编程,必须对于网络的基础只是要有基本的了解.
例如:ip地址,端口号,互联网协议等网络三要素,这些常识性的知识了解后才能真正的理解网络编程,以后同学们如果要
做这方面的工作,那么必然也是要对计算机网络的基础知识进行的了解的.给同学们推荐两本书籍,作为课后的拓展:
图书1: java网络编程
图书2: 计算机网络(谢希仁)
希望能对同学们有所帮助.
然后同学们需要重点掌握和tcp有关的实现代码,这对后续的学习也是有比较大的帮助的.
|
|