准备知识
1、IP、协议、端口详解 点击这里
2、网络编程的三要素:
IP地址
:InetAddress: 网络中设备的标识,不易记忆,可用主机名;
端口号
:用于标识进程的逻辑地址,不同进程的标识 ;
传输协议
:通讯的规则常见协议:TCP,UDP
3、UDP协议与TCP协议的区别:
UDP——发短信
- 将数据源和目的封装成数据包中,不需要建立连接;
- 每个数据报的大小在限制在64k;
- 因无连接,是不可靠协议;
- 不需要建立连接,速度快
TCP——打电话、视频
- 建立连接,形成传输数据的通道;
- 在连接中进行大数据量传输;
- 需要连接所以是可靠协议;
- 必须建立连接,效率会稍低;
网络编程(Socket编程)
1、Socket是什么
- Socket是应用层与TCP/IP协议族通信的中间软件抽象层,是一组接口;
- 在设计模式中,Socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socket接口后面,对用户来说,一组简单的接口就是全部,让Socket去组织数据,以符合指定的协议;
- 应用程序可以通过套接字发送或接收数据,可对其进行像对文件一样的打开、读写和关闭等操作;套接字允许应用程序将I/O插入到网络中,并与网络中的其他应用程序进行通信;
2、Socket原理
- 网络上具有唯一标识的IP地址和端口号组合在一起才能构成唯一能识别的标识符套接字,即就是
Socket=IP+端口号
; - 通信的两端都有Socket、网络通信其实就是Socket间的通信、数据在两个Socket间通过IO传输;
3、Java如何进行网络编程
- 针对不同协议的Socket,Java给我们提供了相应的Socket;
- UDP协议使用的Socket,Java提供了
DatagramSocket类
来描述;TCP协议使用的Socket,Java使用了Socket类
来描述;
4、动态获取IP、主机名
- 在Java中,使用
InetAddress类
来描述IP,它的子类有:Inet4Address、Inet6Address
,我们可以直接使用父类; - 常用方法:
方法 | 作用 |
---|---|
static InetAddress getByName(String host) | 在给定主机名的情况下确定主机的 IP 地址 |
String getHostAddress() | 返回 IP 地址字符串 |
String getHostName() | 获取此 IP 地址的主机名 |
- 代码演示:
import java.net.InetAddress;
import java.net.UnknownHostException;
public class MyTest {
public static void main(String[] args) throws UnknownHostException {
//可以传递主机名或IP地址获取InetAddress对象
InetAddress ip1 = InetAddress.getByName("LAPTOP-4IO7TG8A");
InetAddress ip2 = InetAddress.getByName("192.168.0.103");
System.out.println(ip1==ip2);
System.out.println(ip1.getHostAddress());
//192.168.0.103---获取IP地址
System.out.println(ip1.getHostName());
//LAPTOP-4IO7TG8A----获取主机名
}
}
UDP协议传输数据
1、UDP协议发送数据
- DatagramSocket类:
此类表示用来发送和接收数据报包的套接字,启用 UDP 广播发送;
- 构造方法:
构造方法 | 方法解释 |
---|---|
DatagramSocket() | 构造数据报套接字并将其绑定到本地主机上任何可用的端口 |
DatagramSocket(int port) | 创建数据报套接字并将其绑定到本地主机上的指定端口 |
DatagramSocket(int port, InetAddress laddr) | 创建数据报套接字,将其绑定到指定的本地地址 |
- 常用方法:
public void send(DatagramPacket p) throws IOException
从此套接字发送数据报包;
public void receive(DatagramPacket p) throws IOException
从此套接字接收数据报包。
注意:这里的receive()是一个阻塞式方法,如果没有收到数据,他就会一直阻塞在那里,不往下执行;
- DatagramPacket类:
此类表示数据报包;
- 构造方法:
- 成员方法:
方法 | 作用 |
---|---|
public byte[] getData() | 返回数据缓冲区 |
public int getLength() | 返回将要发送或接收到的数据的长度 |
public int getPort() | 返回某台远程主机的端口号 |
public InetAddress getAddress() | 返回某台机器的 IP 地址 |
public int getLocalPort() | 返回此套接字绑定的本地主机上的端口号 |
最后一个方法,在你的程序启动之后,IDEA会分配给你一个端口号,代表的是当前启动的程序;
- 步骤:
- 创建UDP通讯客户端对象DatagramSocket
- 创建数据报包 DatagramPacket
- 发送数据
- 释放资源
代码实现:
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
public class UDPClient {
public static void main(String[] args) throws IOException {
//创建UDP的套接字
DatagramSocket ds = new DatagramSocket();
byte[] bytes = "你好,UDP,我来了!".getBytes();
DatagramPacket dp = new DatagramPacket(bytes, bytes.length, InetAddress.getByName("LAPTOP-4IO7TG8A"), 8888);
//发送数据报包
ds.send(dp);
//释放资源
ds.close();
}
}
- 代码里面,我们使用无参构造创建了Socket对象,然后将要发送的IP以及端口号都放在数据报包里面发送出去;
因为UDP协议是一个不可靠协议,就好比你发送信息,有可能你要发送的那个手机号已经为空或者停机了,但是你不需要关心这些,你只负责将信息发送出去;代码里面我们将数据发送出去,但是没有服务端接收,他就会被自动丢弃,而不会返回任何错误;
2、UDP协议接收数据
- 我们一般是客户端发送数据给服务端,服务端处理数据,那服务端怎么接受数据呢?
- 步骤:
- 创建UDP通讯协议服务器端对象(DatagramSocket) 注意要用有参数构造,指定端口号;
- 创建数据报包,作用用来接收数据;
- 接收数据 receive(数据包对象) ;
- 解析数据报包,拿出数据;
- 释放资源;
代码实现:
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
public class UDPServer {
public static void main(String[] args) throws IOException {
//创建Socket套接字
DatagramSocket ds = new DatagramSocket(8888);
byte[] bytes = new byte[1024];
DatagramPacket dp = new DatagramPacket(bytes, 0, bytes.length);
//接收数据报包
System.out.println("服务端已经开启,等待接收数据......");
ds.receive(dp);
//获取数据
byte[] data = dp.getData();
System.out.println(new String(data, 0, data.length));
//你好,UDP,我来了!
System.out.println(dp.getAddress());
System.out.println(dp.getAddress().getHostName());
System.out.println(dp.getAddress().getHostAddress());
// 192.168.0.103
// LAPTOP-4IO7TG8A
// 192.168.0.103
ds.close();
}
}
- 代码最开始,创建Socket的时候,服务端暴露端口号,等待客户端的连接;
首先开启服务端,然后开启客户端,服务端就会接收到来自客户端的信息;
服务端一般不关闭;
3、UDP协议发送端的数据来自于键盘录入
- 基于上面UDP协议下的客户端与服务端之间的通信,改进代码,实现:客户端键盘录入将数据传递给服务端;
代码实现:
----客户端:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
public class UDPClient {
public static void main(String[] args) throws IOException {
//客户端从键盘录入给服务端发送消息
//创建UDP的套接字
DatagramSocket ds = new DatagramSocket();
while(true){
System.out.println("请输入要发送的消息:");
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
String s = reader.readLine();
byte[] bytes = s.getBytes();
DatagramPacket dp = new DatagramPacket(bytes, bytes.length, InetAddress.getByName("LAPTOP-4IO7TG8A"), 8888);
ds.send(dp);
if(s.equals("886")){
break;
}
}
ds.close();
}
}
----服务端:
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;
public class UDPServer {
public static void main(String[] args) throws IOException {
DatagramSocket ds = new DatagramSocket(8888);
while (true){
byte[] bytes = new byte[1024];
DatagramPacket dp = new DatagramPacket(bytes,0,bytes.length);
System.out.println("服务端已经开启,等待接收数据......");
ds.receive(dp);
byte[] data = dp.getData();
String s = new String(data, 0, dp.getLength());
System.out.println(s);
if(s.equals("886")){
ds.close();
break;
}
}
}
}
客户端不断输入,并定义一个结束标记,服务端不断接收数据;
4、多线程实现聊天室程序
- 结合学习过的线程知识,将客户端与服务端都写在不同的线程里面,测试类创建线程并启动;
----客户端线程:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
public class ClientThread extends Thread {
//两个线程之间聊天
@Override
public void run() {
//创建UDP的套接字
DatagramSocket ds = null;
try {
ds = new DatagramSocket();
while (true) {
System.out.println("请输入要发送的消息:");
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
String s = reader.readLine();
byte[] bytes = s.getBytes();
DatagramPacket dp = new DatagramPacket(bytes, bytes.length, InetAddress.getByName("LAPTOP-4IO7TG8A"), 8888);
ds.send(dp);
if (s.equals("886")) {
break;
}
}
} catch (IOException e) {
e.printStackTrace();
} finally {
ds.close();
}
}
}
----服务端线程:
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
public class ServerThread extends Thread{
@Override
public void run() {
try {
DatagramSocket ds = new DatagramSocket(8888);
while (true){
byte[] bytes = new byte[1024];
DatagramPacket dp = new DatagramPacket(bytes,0,bytes.length);
System.out.println("服务端已经开启,等待接收数据......");
ds.receive(dp);
byte[] data = dp.getData();
String s = new String(data, 0, dp.getLength());
System.out.println(s);
if(s.equals("886")){
ds.close();
break;
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
----测试类:
public class MyTest {
public static void main(String[] args) {
ServerThread server = new ServerThread();
server.start();
ClientThread client = new ClientThread();
client.start();
}
}
5、两台电脑之间的通信
- 使用UDP通信原理,模拟两台电脑之间的通信,逻辑图如图所示:
代码实现:
-----A电脑:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
public class A {
public static void main(String[] args) throws IOException {
//两个电脑之间的通信
//我们将两台电脑的主线程作为客户端,分别给对方的子线程模拟的服务端发送消息
//是以开启线程的方式
new Thread() {
@Override
public void run() {
DatagramSocket ds = null;
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
try {
ds = new DatagramSocket();
//死循环用于发送数据
while (true) {
System.out.println("请输入给B电脑发送的消息:");
String s = br.readLine();
byte[] bytes = s.getBytes();
DatagramPacket dp = new DatagramPacket(bytes, 0, bytes.length, InetAddress.getByName("LAPTOP-4IO7TG8A"), 6666);
ds.send(dp);
if (s.equals("886")) {
break;
}
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
br.close();
} catch (IOException e) {
e.printStackTrace();
}
ds.close();
}
}
}.start();
sendMsg();
}
private static void sendMsg() throws IOException {
//子线程充当客户端,接受对方的消息
//暴露端口号
DatagramSocket ds = new DatagramSocket(8888);
while (true) {
System.out.println("A电脑的服务端已开启,等待连接........");
//接收B电脑的消息
byte[] bytes = new byte[1024 * 8];
DatagramPacket dp = new DatagramPacket(bytes, 0, bytes.length);
ds.receive(dp);
//获取B电脑的信息
byte[] data = dp.getData();
int length = dp.getLength();
String s = new String(data, 0, length);
System.out.println("IP为:" + dp.getAddress().getHostAddress() + "的电脑给你发来一条消息:" + s);
if ("886".equals(s)) {
break;
}
}
}
}
----B电脑:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
public class B {
public static void main(String[] args) throws IOException {
//两个电脑之间的通信
//我们将两台电脑的主线程作为客户端,分别给对方的子线程模拟的服务端发送消息
//是以开启线程的方式
new Thread() {
@Override
public void run() {
DatagramSocket ds = null;
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
try {
ds = new DatagramSocket();
//死循环用于发送数据
while (true) {
System.out.println("请输入给A电脑发送的消息:");
String s = br.readLine();
byte[] bytes = s.getBytes();
DatagramPacket dp = new DatagramPacket(bytes, 0, bytes.length, InetAddress.getByName("LAPTOP-4IO7TG8A"), 8888);
ds.send(dp);
if (s.equals("886")) {
break;
}
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
br.close();
} catch (IOException e) {
e.printStackTrace();
}
ds.close();
}
}
}.start();
sendMsg();
}
private static void sendMsg() throws IOException {
//子线程充当客户端,接受对方的消息
//暴露端口号
DatagramSocket ds = new DatagramSocket(6666);
while (true) {
System.out.println("B电脑的服务端已开启,等待连接........");
//接收B电脑的消息
byte[] bytes = new byte[1024 * 8];
DatagramPacket dp = new DatagramPacket(bytes, 0, bytes.length);
ds.receive(dp);
//获取B电脑的信息
byte[] data = dp.getData();
int length = dp.getLength();
String s = new String(data, 0, length);
System.out.println("IP为:" + dp.getAddress().getHostAddress() + "的电脑给你发来一条消息:" + s);
if("886".equals(s)){
break;
}
}
}
}
TCP协议传输数据
1、TCP协议发送数据
- 在Java中,针对TCP协议客户端,提供了一个类:Socket;服务端提供ServerSocket类;
Socket类:
此类实现客户端套接字(也可以就叫“套接字”),套接字是两台机器间通信的端点;
- 构造方法:
- 成员方法:
方法 | 作用 |
---|---|
public InputStream getInputStream() | 返回此套接字的输入流 |
public InputStream getInputStream() | 返回此套接字的输入流 |
public OutputStream getOutputStream() | 返回此套接字的输出流 |
public InetAddress getLocalAddress() | 获取套接字绑定的本地地址 |
public InetAddress getInetAddress() | 返回套接字连接的地址 |
public int getLocalPort() | 返回此套接字绑定到的本地端口 |
public int getPort() | 返回此套接字连接到的远程端口 |
public void shutdownInput() | 此套接字的输入流置于“流的末尾” |
public void shutdownOutput() | 禁用此套接字的输出流 |
-
步骤:
-
创建TCP通讯协议客户端对象(Socket) // public Socket(String host, int port)
-
获取输出流对象
-
写数据
-
释放资源
代码实现:
import java.io.IOException;
import java.io.OutputStream;
import java.net.Socket;
public class TCPClient {
public static void main(String[] args) throws IOException {
//创建套接字
Socket socket = new Socket("LAPTOP-4IO7TG8A",6666);
//获取输出流
OutputStream out = socket.getOutputStream();
out.write("你好,TCP,我来了!".getBytes());
out.flush();
socket.close();
}
}
2、TCP协议接收数据
ServerSocket类:
此类实现服务器套接字,服务器套接字等待请求通过网络传入;
- 构造方法:
- 成员方法:
public Socket accept() throws IOException
侦听并接受到此套接字的连接,此方法在连接传入之前一直阻塞;
-
步骤:
-
创建TCP通讯协议服务器端对象(ServerSocket)
-
监听客户端
-
获取输入流对象
-
读取数据
-
释放资源
代码实现:
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
public class TCPServer {
public static void main(String[] args) throws IOException {
ServerSocket ss = new ServerSocket(6666);
//侦听客户端
System.out.println("服务端已经开启,等待(客户端)连接.......");
Socket socket = ss.accept();
InputStream in = socket.getInputStream();
byte[] bytes = new byte[1024];
int len = in.read(bytes, 0, bytes.length);
System.out.println("IP为:" + socket.getInetAddress().getHostAddress() + "的客户端给你发来消息,内容是:" + new String(bytes, 0, len));
ss.close();
}
}
3、客户端键盘录入服务器控制台输出
----客户端:
import java.io.*;
import java.net.Socket;
public class TCPClient {
public static void main(String[] args) throws IOException {
//创建客户端TCP套接字
Socket socket = new Socket("192.168.10.5", 9999);
//获取通道输出流
OutputStream out = socket.getOutputStream();
//包装该通道输出流
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(out));
//创建键盘录入流
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
//写个循环,循环从键盘读取数据
while (true){
System.out.println("请输入你发给服务端的消息:");
String s = in.readLine();
//键盘读取一行,通道流写入一行
writer.write(s);
//换行
writer.newLine();
//字符流要刷新
writer.flush();
if(s.equals("886")){
break;
}
}
//禁用此套接字的输出流,服务端得知这个消息也会结束
socket.shutdownOutput();
socket.close();
in.close();
}
}
----服务端:
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 TCPServer {
public static void main(String[] args) throws IOException {
//客户端键盘录入服务器控制台输出
ServerSocket ss = new ServerSocket(9999);
//侦听客户端
System.out.println("服务端已开启,等待连接.......");
Socket socket = ss.accept();
InputStream in = socket.getInputStream();
//包装通道输入流
BufferedReader reader = new BufferedReader(new InputStreamReader(in));
//写个死循环,不断从客户端读取发送的消息
while (true) {
String s = reader.readLine();
System.out.println("IP为:" + socket.getInetAddress().getHostAddress() + "的主机给你发来消息:" + s);
if (s.equals("886")) {
break;
}
}
ss.close();
}
}
4、客户端键盘录入服务器写到文本文件
----客户端:
import java.io.*;
import java.net.Socket;
public class TCPClient {
public static void main(String[] args) throws IOException {
//客户端键盘录入,服务端写入文本文件中
//创建键盘录入流
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
//创建客户端TCP套接字
Socket socket = new Socket("192.168.10.5", 9999);
//获取通道输出流
OutputStream out = socket.getOutputStream();
//包装该通道输出流
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(out));
//写个循环,循环从键盘读取数据
while(true)
{
System.out.println("请输入你发给服务端的消息:");
String s = in.readLine();
if (s.equals("886")) {
break;
}
//键盘读取一行,通道流写入一行
writer.write(s);
//换行
writer.newLine();
//字符流要刷新
writer.flush();
}
socket.shutdownOutput();
in.close();
socket.close();
}
}
----服务端:
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
public class TCPServer {
public static void main(String[] args) throws IOException {
//暴露端口
ServerSocket ss = new ServerSocket(9999);
//侦听客户端
System.out.println("服务端已开启,等待连接...........");
Socket socket = ss.accept();
//创建输出流,将数据写入文本文件中
BufferedWriter writer = new BufferedWriter(new FileWriter("c.txt"));
//包装通道输入流
BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
String s = null;
//其实通道中是不会读取到null值的
// 因为TCP协议会将数据分成多个块进行发送,而后在另一端会从多个块进行接收,再组合在一起,它并不仅能确定read()和write()方法中所发送信息的界限。
/*read()方法何时返回-1,在一般的文件读取中,这代表流的结束,亦即读取到了文件的末尾,但是在 Socket 套接字中,这样的概念很模糊,因为套接字中数据的末尾并没有所谓的结束标记,无法通过其自身表示传输的数据已经结束,那么究竟什么时候 read()会返回 -1 呢?答案是:当 TCP 通信连接的一方关闭了套接字时。*/
while ((s = in.readLine()) != null) {
//获取通道输入
writer.write(s);
writer.newLine();
writer.flush();
}
writer.close();
ss.close();
}
}
注意:
1、客户端与服务器端在接收和发送数据时,read()和write()方法不一定要对应,比如,其中一方可以一次发送多个字节的数据,而另一方可以一个字节一个字节地接收,也可以一个字节一个字节地方送,而多个字节多个字节地接收。
2、因为TCP协议会将数据分成多个块进行发送,而后在另一端会从多个块进行接收,再组合在一起,它并不仅能确定read()和write()方法中所发送信息的界限;
3、read()方法何时返回-1,在一般的文件读取中,这代表流的结束,亦即读取到了文件的末尾,但是在 Socket 套接字中,这样的概念很模糊,因为套接字中数据的末尾并没有所谓的结束标记,无法通过其自身表示传输的数据已经结束,那么究竟什么时候 read()会返回 -1 呢?答案是:当 TCP 通信连接的一方关闭了套接字时;
4、read()方法会在没有数据可读时发生阻塞,直到有新的数据可读;
5、客户端读取文本文件服务器控制台输出
----客户端:
import java.io.*;
import java.net.InetAddress;
import java.net.Socket;
public class TCPClient {
public static void main(String[] args) throws IOException {
//客户端读取文本文件服务器控制台输出
//定义读取文本文件
BufferedReader reader = new BufferedReader(new FileReader("a.txt"));
Socket socket = new Socket(InetAddress.getByName("192.168.10.5"), 8888);
//包装通道流
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
String line = null;
while ((line = reader.readLine()) != null) {
writer.write(line);
writer.newLine();
writer.flush();
}
socket.shutdownOutput();
reader.close();
socket.close();
}
}
----服务端:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;
public class TCPServer {
public static void main(String[] args) throws IOException {
ServerSocket ss = new ServerSocket(8888);
//侦听客户端
System.out.println("服务端已开启,等待连接......");
Socket socket = ss.accept();
//包装通道输入流
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
String s = null;
while ((s = br.readLine()) != null) {
System.out.println(s);
}
ss.close();
}
}
6、TCP协议上传文本文件
----客户端:
import java.io.*;
import java.net.Socket;
public class MyClient {
public static void main(String[] args) throws IOException {
//客户端读取文本文件
//服务端写入文本文件
//模拟上传文件
BufferedReader in = new BufferedReader(new FileReader("a.txt"));
Socket socket = new Socket("192.168.10.5", 6666);
OutputStream out = socket.getOutputStream();
//包装通道字节流为字符流
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(out));
String line = null;
while ((line = in.readLine()) != null) {
writer.write(line);
writer.newLine();
writer.flush();
}
in.close();
socket.close();
}
}
-----服务端:
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
public class MyServer {
public static void main(String[] args) throws IOException {
BufferedWriter writer = new BufferedWriter(new FileWriter("b.txt"));
ServerSocket ss = new ServerSocket(6666);
//侦听客户端
System.out.println("服务端已开启,等待连接........");
Socket socket = ss.accept();
//包装通道流
InputStream inputStream = socket.getInputStream();
BufferedReader in = new BufferedReader(new InputStreamReader(inputStream));
String line = null;
while ((line = in.readLine()) != null) {
writer.write(line);
writer.newLine();
writer.flush();
}
System.out.println("上传文件成功!");
writer.close();
ss.close();
}
}
7、服务器给客户端一个反馈
----客户端:
import java.io.*;
import java.net.InetAddress;
import java.net.Socket;
public class TCPClient {
public static void main(String[] args) throws IOException {
//客户端读取文本文件服务器控制台输出,服务端需要给出一个反馈
//定义读取文本文件
BufferedReader reader = new BufferedReader(new FileReader("a.txt"));
Socket socket = new Socket(InetAddress.getByName("192.168.10.5"), 8888);
//包装通道流
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
String line = null;
while ((line = reader.readLine()) != null) {
writer.write(line);
writer.newLine();
writer.flush();
}
writer.write("over");
writer.newLine();
writer.flush();
InputStream in = socket.getInputStream();
//读取服务端的反馈
byte[] bytes = new byte[1024 * 8];
int len = in.read(bytes);
System.out.println("服务端告诉你:" + new String(bytes, 0, len));
//释放资源
reader.close();
socket.close();
}
}
---服务端:
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
public class TCPServer {
public static void main(String[] args) throws IOException {
BufferedWriter writer = new BufferedWriter(new FileWriter("b.txt"));
ServerSocket ss = new ServerSocket(8888);
//侦听客户端
System.out.println("服务端已开启,等待连接........");
Socket socket = ss.accept();
//包装通道流
InputStream inputStream = socket.getInputStream();
BufferedReader in = new BufferedReader(new InputStreamReader(inputStream));
String line = null;
while ((line = in.readLine()) != null) {
if ("over".equals(line)){
break;
}
writer.write(line);
writer.newLine();
writer.flush();
}
//System.out.println("上传文件成功!");----给客户端以反馈
OutputStream out = socket.getOutputStream();
out.write("客户端你好,你上传的文件我已经收到".getBytes());
//释放资源
writer.close();
ss.close();
/*
* 我们采用的方式是客户端手动写一个结束标记来结束,但是这样很明显是有漏洞的
* 如果上传的文本文件当中本身就存在一个和结束标记一样的一行文字,就会出现上传文件不完整的现象,
* 所以我们引入socket.shutdownOutput();
* */
}
}
8、TCP上传文本文件并给出反馈
---客户端:
import java.io.*;
import java.net.InetAddress;
import java.net.Socket;
public class TCPClient {
public static void main(String[] args) throws IOException {
//客户端读取文本文件服务器控制台输出,服务端需要给出一个反馈
//定义读取文本文件
BufferedReader reader = new BufferedReader(new FileReader("a.txt"));
Socket socket = new Socket(InetAddress.getByName("LAPTOP-4IO7TG8A"), 8888);
//包装通道流
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
String line = null;
while ((line = reader.readLine()) != null) {
writer.write(line);
writer.newLine();
writer.flush();
}
socket.shutdownOutput();
InputStream in = socket.getInputStream();
//读取服务端的反馈
byte[] bytes = new byte[1024 * 8];
int len = in.read(bytes);
System.out.println("服务端告诉你:" + new String(bytes, 0, len));
//释放资源
reader.close();
socket.close();
}
}
----服务端:
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
public class TCPServer {
public static void main(String[] args) throws IOException {
BufferedWriter writer = new BufferedWriter(new FileWriter("b.txt"));
ServerSocket ss = new ServerSocket(8888);
//侦听客户端
System.out.println("服务端已开启,等待连接........");
Socket socket = ss.accept();
//包装通道流
InputStream inputStream = socket.getInputStream();
BufferedReader in = new BufferedReader(new InputStreamReader(inputStream));
String line = null;
while ((line = in.readLine()) != null) {
writer.write(line);
writer.newLine();
writer.flush();
}
//System.out.println("上传文件成功!");----给客户端以反馈
OutputStream out = socket.getOutputStream();
out.write("客户端你好,你上传的文件我已经收到".getBytes());
//释放资源
writer.close();
ss.close();
}
}
多线程改进上传文本文件
-----客户端:
public class TCPClient {
public static void main(String[] args) throws IOException {
//上传任意类型的文件
Socket socket = new Socket("192.168.47.1", 5555);
//获取通道中的输入流,输出流
InputStream in = socket.getInputStream();
OutputStream out = socket.getOutputStream();
FileInputStream fin = new FileInputStream("xingye.jpg");
int len2=0;
byte[] bytes1 = new byte[1024];
while ((len2=fin.read(bytes1))!=-1){
out.write(bytes1,0,len2);
out.flush();
}
//禁用此套接字的输出流。
socket.shutdownOutput();
//读取服务端的反馈
byte[] bytes = new byte[1024];
int len = in.read(bytes); //读取服务端的反馈,读取不到,就阻塞在这里
System.out.println(new String(bytes,0,len));
//释放资源
socket.close();
fin.close();
}
}
----服务端:
public class TCPServer {
public static void main(String[] args) throws IOException {
//让一个服务端。连接多个客户端。
ServerSocket serverSocket = new ServerSocket(5555);
System.out.println("服务器已经开启等待连接。。。。。");
int i=1;
while (true){
//循环侦听客户端
Socket socket = serverSocket.accept();
System.out.println((i++)+"个客户端连接上来了");
//为每一个连接上来的客户端,单独开启一个线程来处理
//把每个客户端socket 传入到线程里面去
new UpLoadThread(socket).start();
}
}
}
----服务端线程:
public class UpLoadThread extends Thread {
private Socket socket;
public UpLoadThread(Socket socket) {
this.socket = socket;
}
@Override
public void run() {
try {
//获取通道中的输入输出流
InputStream in = socket.getInputStream();
OutputStream out = socket.getOutputStream();
int len=0;
byte[] bytes=new byte[1024];
FileOutputStream fout = new FileOutputStream(System.currentTimeMillis() + "tp.jpg");
while ((len=in.read(bytes)) !=-1) {
fout.write(bytes,0,len);
fout.flush();
}
//如果上传成功,服务端给客户端一个反馈
out.write("客户端你好,你上传的文件我已经收到".getBytes());
fout.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
与网络编程有关的DOS命令
DOS 命令 | 意义 |
---|---|
net view | 获取局域网中的所有主机名 |
ipconfig -all | 获取本地IP,主机名,MAC地址 |
arp -a | 获取本局域网中的所有IP地址和物理地址 |
ping -a x.x.x.x | 获取x.x.x.x的主机名 |
nbtstat -a 主机名 | 获取MAC地址 |