赞
踩
IP地址
端口
协议
通信:TCP、UDP协议。(传输层)
是设备上应用程序的唯一标识。
端口号:用两个字节表示整数,它的取值范围是0~65535。其中,周知端口: 0~1023之间的端口用于一些知名的网络服务和应用。
注册端口: 普通的应用程序需要使用1024~49151的端口号。如果端口号被另外一个服务或应用所占用,会导致当前程序启动失败。
**动态端口:**49152~65535,之所以称为动态端口,是因为它一般不固定分配某种进程,而是动态分配。
- IP地址可以唯一定位一台网络上的计算机
- 127.0.0.1表示的是本机localhost
IPv4 / IPv6
IPv4
如 127.0.0.1 ,四个字节组成。每个字节 0~255,一共有42亿个IPv4地址,早已用尽。
IPv6
如 ABCD:EF01:2345:6789:ABCD:EF01:2345:6789,128位,由八个无符号整数组成,号称可以为全世界的每一粒沙子编上一个地址。
此类表示Internet协议(IP)地址。
通过一些静态方法可以返回一个InetAddress对象代表IP。
// 方法很多,不用记忆,查文档就可以。这个是最常用的
// 获得InetAddress对象
InetAddress ip = InetAddress.getByName("www.baidu.com"); // 虽然是填名字,但是也可以填ip地址比如"127.0.0.1"
计算机与外界通讯交流的出口
有以下几个特点:
可以通过一些命令来查看端口占用情况:
cmd命令:
netstat -ano #查看所有端口
netstat -ano | findstr "14420" #查看指定端口
tasklist | findstr "11420" #查看指定端口的进程
知道了IP地址,我们可以定位一台主机,
知道了端口号,我们可以定位一个进程,
现在知道了IP地址和端口号,我们可以定位某台计算机上的某个进程。
于是,之前InetAddress的代码就可以做一些改进
InetSocketAddress ip = new InetSocketAddress("www.baidu.com", 443);
//第一个参数可以填hostname 也可以填 InetAddress对象,第二个参数填端口号(int类型)。
协议就是一些规则,通信协议就是关于通信的规则,如果要通信,那么就必须遵守这些规则
最重要的有TCP协议和UDP协议。
是指能够在多个不同网络间实现信息传输的协议簇。TCP/IP协议不仅仅指的是TCP和IP两个协议,而是指一个由FTP、SMTP、TCP、UDP、IP等协议构成的协议簇, 只是因为在TCP/IP协议中TCP协议和IP协议最具代表性,所以被称为TCP/IP协议。
传输控制协议
因为是C/S架构,所以要有一个客户端和一个服务端。
public static void main(String[] args) { // 首先要有一个目标,向哪一个服务端发送消息 InetSocketAddress severIP = new InetSocketAddress("127.0.0.1", 8888); // 有了目标主机后,就可以建立连接 Socket socket = new Socket(); socket.connect(serverIP); // 已经连接上了,就可以发送数据,利用IO流 OutPutStream os = socket.getOutPutSteam(); PrintStream ps = new PrintStream(os); // 发文字消息用打印流更方便 Scanner in = new Scanner(System.in); while (true) { System.out.println("请输入消息:"); ps.println(in.nextLine()); ps.flush(); } }
public static void main(String[] args) {
// 要能呗客户端所连接,端口号要一致
ServerSocket server = new ServerSocket(8888);
// 建立连接
Socket socket = server.accept();
// 建立好连接后,就可以接受数据了
InputStream is = socket.getInPutStream();
BufferedReader rd = new BufferedRead(new InputStreamReader(is)); // 字符缓冲流读文字消息更方便
String s;
while ((s = rd.readLine()) != null) {
System.out.println(new Date + "\n" + s);
}
}
文件上传依然是IO流传输数据
消息是一种数据,图片也是一种数据,万物都是数据,所以步骤都是一样的
public static void main(String[] args) { // 1. 建立连接 Socket socket = new Socket(); socket.connect(new InetSocketAddress("127.0.0.1", 9957)); // 2. 传文件 要有文件流 FileInputStream fos = new FileInputStream("地址"); // 3. 要有字节流 传输数据给服务端 BufferedOutputStream bos = new BufferedOutputStream(socket.getOutputStream()); byte[] buffer = new byte[1024]; int len; // 4. 发送文件 while ((len = fos.read(buffer)) != -1) { bos.write(buffer, 0, len); } // 5. 获取服务端响应 socket.shutdownOutput(); // 关闭输出管道 BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream())); System.out.println(bufferedReader.readLine()); // 6. 关闭通道 fis.close(); bos.close(); socket.close(); }
public static void main(String[] args) { // 1. 设置端口,让客户端找到 ServerSocket server = new ServerSocket(9957); // 2. 监听客户端的连接 Socket socket = server.accept(); // 3. 文件输出 要文件流 接受数据 要字节流 FileOutputStream fos = new FileOutputStream("地址"); BufferedInputStream bis = new BufferedInputStream(socket.getInputStream()); // 4. 文件输出 byte[] buffer = new byte[1024]; int len; while ((len = bis.read(buffer)) != -1) { fos.write(buffer, 0, len); } // 5. 给予客户端回应 PrintStream ps = new PrintStream(socket.getOutputStream()); ps.println("接收完毕!"); //关闭通道 fos.close(); bis.close(); socket.close(); }
UDP不需要连接,知道了目标的地址就可以发消息。
public class Client {
public static void main(String[] args) throws IOException {
// 1. 建立socket连接
DatagramSocket datagramSocket = new DatagramSocket();
// 2. 创建数据
String s = "hello udp";
DatagramPacket packet = new DatagramPacket(s.getBytes(), 0, s.getBytes().length, InetAddress.getByName("127.0.0.1"), 9957);
// 3. 调用方法发送
datagramSocket.send(packet);
//4. 关闭资源
datagramSocket.close();
}
}
public static void main(String[] args) throws IOException {
// 1. 建立socket连接
DatagramSocket datagramSocket = new DatagramSocket(9957);
// 2. 创建数据包
byte[] buffered = new byte[1024];
DatagramPacket packet = new DatagramPacket(buffered, 0, buffered.length);
// 3. 调用方法接受数据
datagramSocket.receive(packet);
// 4. 解析数据包
System.out.println(packet.getAddress());
System.out.println(new String(packet.getData(),0, packet.getLength()));
// 5. 关闭资源
datagramSocket.close();
}
只需要把上面的代码稍微改一下即可。
循环输入,以及持久监听。
public static void main(String[] args) throws IOException {
DatagramSocket socket = new DatagramSocket();
DatagramPacket packet;
Scanner in = new Scanner(System.in);
String s;
do {
System.out.print("Say:");
s = in.nextLine();
packet = new DatagramPacket(s.getBytes(), 0, s.getBytes().length, new InetSocketAddress("127.0.0.1", 9957));
socket.send(packet);
} while (s.compareTo("exit") != 0);
socket.close();
}
public static void main(String[] args) throws IOException {
DatagramSocket socket = new DatagramSocket(9957);
DatagramPacket packet;
byte[] bufferd = new byte[1024];
while (true) {
packet = new DatagramPacket(bufferd, 0, bufferd.length);
socket.receive(packet);
System.out.println(new String(packet.getData(), 0, packet.getLength()));
}
}
发数据需要一个发送端,收数据需要一个接收端
于是先把接收端和发送端的类。
// 因为需要多线程,所以需要实现Runnable接口 public class TalkSend implements Runnable { // UDP发送数据必要的socket连接以及packet和一个字符缓冲流读取消息 private DatagramSocket socket; private DatagramPacket packet; private BufferedReader reader; // 设置目标 IP,port以及自己的用户名 private String toIP; private int toPort; private String name; // 初始化 public TalkSend(String toIP, int toPort, String name) { this.toIP = toIP; this.toPort = toPort; this.name = name; try { reader = new BufferedReader(new InputStreamReader(System.in)); socket = new DatagramSocket(); socket.connect(new InetSocketAddress(toIP, toPort)); } catch (Exception e) { e.printStackTrace(); } } // 多线程run方法 @Override public void run() { // 死循环持续读取键盘输入流,持续发送 String s; while (true) { try { s = name + ": " + reader.readLine(); packet = new DatagramPacket(s.getBytes(), 0, s.getBytes().length); socket.send(packet); if (s.endsWith(": bye")) { break; } } catch (Exception e) { e.printStackTrace(); } } socket.close(); } }
public class TalkReceive implements Runnable { // UDP接收数据需要socket连接和packet private DatagramSocket socket; private DatagramPacket packet; // 接收的端口 private int myPort; //初始化 public TalkReceive(int myPort) { this.myPort = myPort; try { socket = new DatagramSocket(myPort); } catch (Exception e){ e.printStackTrace(); } } // 多线程run方法 @Override public void run() { // 死循环持续监听消息 String s; byte[] buffered = new byte[1024]; while (true) { try { packet = new DatagramPacket(buffered, 0, buffered.length); socket.receive(packet); } catch(Exception e) { e.printStackTrace(); } s = new String(packet.getData(), 0, packet.getLength()); System.out.println(s); if (s.endsWith(": bye")) { break; } } } }
有了发送端和接收端,我们就可以把他们都丢进线程池中,这样我们就可以和另一台主机通信了。
public class Demo { public static void main(String[] args) { ExecutorService pool = new ThreadPoolExecutor(4, 8, 2, TimeUnit.MINUTES, new ArrayBlockingQueue<>(4), Executors.defaultThreadFactory(), new ThreadPoolExecutor.CallerRunsPolicy()); Scanner in = new Scanner(System.in); System.out.println("设置接收端口:"); int port = in.nextInt(); System.out.println("目标IP:"); String toIP = in.next(); System.out.println("目标端口:"); int toPort = in.nextInt(); System.out.println("您的昵称:"); String name = in.next(); pool.execute(new TalkReceive(port)); pool.execute(new TalkSend(toIP, toPort, name)); } }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。