java网络编程以及通信的项目研究
网络编程
什么是网络编程
就是使用编程语言进行多态计算机之间的通信
网络编程的三大要素
- IP地址:网络内的每一台计算机的唯一标识,通过IP找到指定的计算机
- 端口:用于标识进程的逻辑地址,通过端口找到指定的进程
- 协议:定义通信规则,符合协议则可以通信,不符合不可以通信
通俗点来说,就是,我要和小王说话,首先我要找到小王的地址(相当于通过IP找到指定的计算机);之后我要和小王说话,小王用耳朵听我说(相当于使用端口接收);而我们对化需要使用双方都可以听懂的普通话(这就是协议的作用)
我们在命令提示符内,输入 cmd 进入,输入 ipconfig找到自己的ip地址
IPv4地址对应的就是网络ID
127.0.0.1 用于本机测试吗,是本机回环地址
255.255.255.0 是当前子网,一般用于当前子网广播信息
Socket
套接字,是计算机之间通信的一种约定或者一种方式,通过Socket这种约定,一台计算机可以接收其他计算机的数据,也可以向其他计算机发送数据。
通俗来说,如果你想给远方的朋友写一封信,如何写信,将信打包,属于应用层。信怎么写,怎么打包完全由我们自己做主
当我们将信放入邮筒时,邮筒的投信口就是套接字
TCP协议的数据传递细节
TCP通信原理
服务器创建ServerSocket,在指定的端口监听并处理请求,客户端创建Socket,向服务器发送请求。
流程示意图:
Socket是客户端 ServerSocket是服务器端
UDP协议数据传递细节
- UDP协议不需要利用IO流实现数据的传输
- 每个数据发送单元被统一封装为数据包的方式,发送方将数据包发送到网络内,数据包在网络内去寻找他的目的地。
- DatagramSocket:用于发送或者接收数据包
- DatagramPacket:数据包
TCP的接收方和发送方是固定的,而我们从上面这个图可以看出,UDP接收方和发送方并不固定,所以UDP统一使用DatagramSocket去表示双方内的某一方
流程示意图:
对InetAddress类和InetSocketAddress类的实践
InetAddress类
这种类用于IP套接字地址(IP地址+ 端口号),用于socket通信
InetAddress类常用的方法
实例代码:
package net;
import java.net.InetAddress;
public class TestInetAddress {
public static void main(String[] args) throws Exception {
//1. 获取InetAddress的方式
InetAddress ia = InetAddress.getLocalHost();
System.out.println("获取主机的ip地址"+ia.getHostAddress());
System.out.println("获取主机名称:"+ia.getHostName());
//2.根据域名去获取InetAddress对象
InetAddress ia2 = InetAddress.getByName("www.baidu.com");
System.out.println("百度服务器的IP地址:"+ia2.getHostAddress());
System.out.println("主机名称:"+ia2.getHostName());
//3.根据IP地址获取一个InetAddress对象
//这里返回主机名称时,返回的是主机的IP而不是主机的域名
//这是因为IP地址不存在或者主机服务器不允许进行一个主机IP地址的映射,这就会返回一个IP地址
InetAddress ia3 = InetAddress.getByName("61.135.253.15");
System.out.println("服务器主机IP:"+ia3.getHostAddress());
System.out.println("主机名称:"+ia3.getHostName());
}
}
package net;
import java.net.InetAddress;
import java.net.InetSocketAddress;
public class TestInetSocketAddress {
public static void main(String[] args) throws Exception {
//创建对象
InetSocketAddress tsa = new InetSocketAddress("localhost",9999);
InetSocketAddress tsa2 = new InetSocketAddress("127.0.0.1",9999);
InetSocketAddress tsa3 = new InetSocketAddress("192.168.86.100",9999);
InetAddress ia = InetAddress.getByName("localhost");
InetSocketAddress tsa4 = new InetSocketAddress(ia,9999);
System.out.println("主机名称:"+tsa4.getHostName());
System.out.println("主机IP地址:"+tsa4.getAddress());
}
}
URL类
URL是统一资源定位符,由四部分组成:协议,存放资源的主机域名,端口号,资源文件名
如:http://www.baidu.com:80/index.html#aa?name=bb
#aa是一个锚点
URL 内 ?后的是传递的参数
URL类实践:
package net;
import java.net.MalformedURLException;
import java.net.URL;
public class TestURL {
public static void main(String[] args) throws MalformedURLException {
URL url = new URL("http://www.baidu.com:80/index.html#aa?username=bjsxt&pwd=bjsxt");
System.out.println("协议名称:"+url.getProtocol());
System.out.println("主进程:"+url.getHost());
System.out.println("端口号:"+url.getPort());
System.out.println("获取资源路径:"+url.getFile());
System.out.println("获取默认端口号:"+url.getDefaultPort());
System.out.println("获中国URL的路径部分:"+url.getPath());
}
}
package net;
import java.io.*;
import java.net.URL;
public class TestURL2 {
public static void main(String[] args) throws IOException {
/**
* 网络爬虫
* 1. 从网络内获取资源
* 2. 存储到本地
*/
//1.创建URL对象
URL url = new URL("http://www.baidu.com");
//2.获取字节输入流
InputStream is = url.openStream();
//3.缓冲流
BufferedReader br = new BufferedReader(new InputStreamReader(is,"utf-8"));
//4. 存储到本地
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream("index.html")));
//5.边读边写
String line = null;
while ((line=br.readLine())!=null){
bw.write(line);
bw.newLine();
bw.flush();
}
//6. 关闭流
bw.close();
br.close();
}
}
在运行之后,会增加一个从百度网址爬取到的内容,存放在了这个index.html页面内。
实现基于TCp协议的Socket编程
单向通信实现
字节流
客户端向服务器端发送的是字节,第二步发送的是字符串
需要知道,服务器端和客户端分别在两个项目的不同包下,以模拟两个电脑之间进行数据传递
先编写服务器端
package net.server;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
/**
* 这是服务器端端的应用程序代码
*/
public class Test {
public static void main(String[] args) throws IOException {
System.out.println("---------服务器端启动----------");
//1.创建ServerSocket对象
ServerSocket server = new ServerSocket(9999);
//2.监听是否有客户端来请求
Socket client = server.accept();
//3.获取输入流
InputStream is = client.getInputStream();
System.out.println((char)is.read());
//4.获取输出流
//我们想向客户端发送“收到了” 需要获取输出流
OutputStream os = client.getOutputStream();
os.write("服务器端收到了".getBytes());
//5.一次通信结束,关闭流 关闭Socket
if (os!=null){
os.close();
}
if (is!=null){
is.close();
}
if (client!=null){
client.close();
}
}
}
再编写客户端
package client;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
/**
* 客户端
*/
public class Test {
public static void main(String[] args) throws IOException {
System.out.println("------客户端启动---------");
//1.创建我们的Socket对象
Socket client =new Socket("192.168.86.100",9999);
//2.获取输出流
OutputStream os = client.getOutputStream();
//服务器端是接收一个字节,那我们在客户端就应该去发送一个字节
os.write('a');
//3.获取输入流
InputStream is = client.getInputStream();
byte[] buf = new byte[1024];//中转站
int len = 0; //读到的字节的个数
while ((len=is.read(buf))!=-1){
System.out.println(new String(buf,0,len));
}
//4.关闭流
if (is!=null){
is.close();
}
if (os!=null){
os.close();
}
if (client!=null){
client.close();
}
}
}
我们先启动服务器端
再启动客户端
这样,我们就实现了客户端向服务器端发送一个字节
数据流(可以发送多个字节)
服务器端:
package net.server;
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
/**
* 这是服务器端的应用程序代码
*/
public class Test2 {
public static void main(String[] args) throws IOException {
System.out.println("---------数据流版的服务器端启动----------");
//1.创建ServerSocket对象
ServerSocket server = new ServerSocket(9999);
//2.监听是否有客户端来请求
Socket client = server.accept();
//3.获取输入流
DataInputStream dis = new DataInputStream(client.getInputStream());
System.out.println(dis.readUTF());
//4.获取输出流
//我们想向客户端发送“收到了” 需要获取输出流
DataOutputStream dos = new DataOutputStream(client.getOutputStream());
dos.writeUTF("服务器端收到了");
//5.一次通信结束,关闭流 关闭Socket
if (dos!=null){
dos.close();
}
if (dis!=null){
dis.close();
}
if (client!=null){
client.close();
}
}
}
客户端:
package client;
import java.io.*;
import java.net.Socket;
/**
* 客户端
*/
public class Test2 {
public static void main(String[] args) throws IOException {
System.out.println("------数据流版的客户端启动---------");
//1.创建我们的Socket对象
Socket client =new Socket("127.0.0.1",9999);
//2.获取输出流
DataOutputStream dos = new DataOutputStream(client.getOutputStream());
dos.writeUTF("helloWorld");
//3.获取输入流
DataInputStream dis = new DataInputStream(client.getInputStream());
System.out.println(dis.readUTF());
//4.关闭流
if (dis!=null){
dis.close();
}
if (dos!=null){
dos.close();
}
if (client!=null){
client.close();
}
}
}
我们先启动服务器端,然后启动客户端,结果如下:
双向通信实现
服务器端和客户端不可以在同一个项目下,且,双向通信的实现前提,两个类所在的包结构和包名必须一致
服务器端:
package net.entity;
import net.thread.ServerThread;
import java.io.DataOutputStream;
import java.io.ObjectInputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;
public class Server {
public static void main(String[] args) throws Exception {
System.out.println("服务器端启动");
//1. 创建ServerSocket对象
ServerSocket server = new ServerSocket(9999);
while (true){
Socket socket = server.accept();
//创建线程类的对象,并启动线程
ServerThread st = new ServerThread(socket);
//启动线程
new Thread(st).start();
}
}
}
客户端:
package net.entity;
import java.io.DataInputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.net.Socket;
import java.util.Scanner;
public class Client {
public static void main(String[] args) throws IOException {
System.out.println("客户端启动");
//1. 创建Socket对象,用于连接服务器
Socket client = new Socket("127.0.0.1",9999);
//2. 创建输出流(也就是对象流)
ObjectOutputStream oos = new ObjectOutputStream(client.getOutputStream());
//3. 创建User对象
//调用获取用户信息的getUser()
User u = getUser();//new User("aaa","aaa");
//4. User对象发送到服务器
oos.writeObject(u);
//5. 获取输入流(使用数据流激进行获取)
DataInputStream dis = new DataInputStream(client.getInputStream());
System.out.println(dis.readUTF());
//6. 关闭流
if (oos!=null){
oos.close();
}
if (dis!=null){
dis.close();
}
if (client!=null){
client.close();
}
}
//获取用户对象的方法
public static User getUser(){
Scanner input = new Scanner(System.in);
System.out.println("请输入用户名:");
String userName = input.next();
System.out.println("请输入密码:");
String passWord = input.next();
//封装为User对象
return new User(userName,passWord);
}
}
运行结果:
以上,为双向通信的实验
在实现过程内,有什么疑问,原理上的难点,请留言,我会尽快回复
本文地址:https://blog.csdn.net/qq_43545801/article/details/109552092