一、网络编程
1、网络编程: 网络编程主要用于解决计算机与计算机(手机、平板..)之间的数据传输问题。
2、区分网络编程和网页编程
① 网络编程不需要基于html页面就可以达到数据之间的传输。 比如: feiQ , QQ , 微信….
② 网页编程就是要基于html页面的基础上进行数据的交互的。 比如: 珍爱网、 oa(办公自动化)、 高考的报告系统…
3、计算机网络: 分布在不同地域的计算机通过外部设备链接起来达到了消息互通、资源共享的效果就称作为一个计算机网络。
4、网络通讯的三要素:IP、端口号、协议
二、IP地址
1、IP地址:IP地址的本质就是一个由32位的二进制数据组成的数据。后来别人为了方便我们记忆IP地址,就把IP地址切成了4份,每份8bit。2^8 = 0~255
00000000-00000000-00000000-00000000
2、IP地址 = 网络号+ 主机号。
3、IP地址的分类
① A类地址 = 一个网络号 + 三个主机号 2^24 政府单位
② B类地址 = 两个网络号 + 两个主机号 2^16 事业单位(学校、银行..)
③ C类地址 = 三个网络号 + 一个主机号 2^8 私人使用
4、常用的方法
方法 | 解释 |
---|---|
getLocalHost() | 获取本机的IP地址 |
getByName(“IP或者主机名”) | 根据一个IP地址的字符串形式或者是一个主机名生成一个IP地址对象。 (用于获取别人的IP地址对象) |
getHostAddress() | 返回一个IP地址的字符串表示形式。 |
getHostName() | 返回计算机的主机名 |
public class Demo30.1{
public static void main(String[] args) throws UnknownHostException {
//getLocalHost 获取本机的IP地址对象
InetAddress address = InetAddress.getLocalHost();
System.out.println("IP地址:"+address.getHostAddress());
System.out.println("主机名:"+address.getHostName());
//获取别人机器的IP地址对象
//可以根据一个IP地址的字符串形式或者是一个主机名生成一个IP地址对象。
InetAddress address = InetAddress.getByName("Jolly-pc140116");
System.out.println("IP地址:"+address.getHostAddress());
System.out.println("主机名:"+address.getHostName());
InetAddress[] arr = InetAddress.getAllByName("www.baidu.com");//域名
}
}
三、端口号
1、端口号是没有类描述的。
2、端口号的范围: 0~65535
① 从0到1023:系统紧密绑定于一些服务
② 1024~65535:我们可以使用
四、UDP协议
1、网络通讯的协议:
① udp通讯协议
② tcp通讯协议。
2、UDP的特点
① 将数据极其源和目的封装为数据包,不需要建立连接。
② 每个数据包大小限制在64K中
③ 因为无连接,所以不可靠
④ 因为不需要建立连接,所以速度快
⑤ udp 通讯是不分服务端与客户端的,只分发送端与接收端。
如:对讲机、飞秋、凌波、CS等
3、在java中网络通讯也称作为Socket(插座)通讯,要求通讯的两台机器都必须要安装Socket,不同的协议就有不同的插座(Socket)。
4、udp协议下的Socket
① DatagramSocket:udp插座服务
② DatagramPacket:数据包类
③ DatagramPacket(buf, length, address, port):{buf:发送的数据内容、length:发送数据内容的大小、address:发送的目的IP地址对象、port:端口号}
5、发送端的使用步骤
① 建立udp的服务
② 准备数据,把数据封装到数据包中发送。 发送端的数据包要带上ip地址与端口号
③ 调用udp的服务,发送数据
④ 关闭资源
//发送端
public class Sender {
public static void main(String[] args) throws IOException {
//建立udp的服务
DatagramSocket datagramSocket = new DatagramSocket();
//准备数据,把数据封装到数据包中。
String data = getData("这个是我第一个udp的例子..");
//创建了一个数据包
//每个网络程序都有自己所处理的特定格式数据,如果接收到的数据不符合指定的格式,那么就会被当成垃圾数据丢弃(加密)
DatagramPacket packet = new DatagramPacket(data.getBytes(), data.getBytes().length,InetAddress.getLocalHost(), 9090);
//在udp协议中有一个IP地址称作为广播地址,广播地址就是主机号为255地址。给广播IP地址发送消息的时候,在同一个网络段的机器都可以接收到信息(192.168.15.255)
//DatagramPacket packet = new DatagramPacket(data.getBytes(), data.getBytes().length,InetAddress.getName("192.168.15.255"), 2425);
//调用udp的服务发送数据包
datagramSocket.send(packet);
//关闭资源:实际上就是释放占用的端口号
datagramSocket.close();
}
// 把数据拼接成指定格式的数据
//version:time :sender : ip: flag:content ;
//版本号 时间 发送人 :IP:发送的标识符(32): 真正的内容;
public static String getData(String content) {
StringBuilder sb = new StringBuilder();
sb.append("1.0:");
sb.append(System.currentTimeMillis() + ":");
sb.append("狗蛋:");
sb.append("192.168.10.1:");
sb.append("32:");
sb.append(content);
return sb.toString();
}
}
6、接收端的使用步骤
① 建立udp的服务
② 准备空的数据包接收数据。
③ 调用udp的服务接收数据。
④ 关闭资源
public class Receive {
public static void main(String[] args) throws IOException {
//建立udp的服务,并且要监听一个端口。
DatagramSocket socket = new DatagramSocket(9090);
//准备空的数据包用于存放数据
byte[] buf = new byte[1024];
DatagramPacket datagramPacket = new DatagramPacket(buf, buf.length); // 1024
//调用udp的服务接收数据
socket.receive(datagramPacket); //receive是一个阻塞型的方法,没有接收到数据包之前会一直等待,数据实际上就是存储到了byte的字节数组中了。
System.out.println("接收端接收到的数据:"+ new String(buf,0,datagramPacket.getLength())); // getLength()获取数据包存储了几个字节
//关闭资源
socket.close();
}
}
7、群聊
//群聊发送端
public class ChatSender extends Thread {
@Override
public void run() {
try {
//建立udp的服务
DatagramSocket socket = new DatagramSocket();
//准备数据,把数据封装到数据包中发送
BufferedReader keyReader = new BufferedReader(new InputStreamReader(System.in));
String line = null;
DatagramPacket packet = null;
while((line = keyReader.readLine())!=null){
//把数据封装到数据数据包中,然后发送数据。
packet = new DatagramPacket(line.getBytes(), line.getBytes().length, InetAddress.getByName("192.168.15.255"), 9090);
//把数据发送出去
socket.send(packet);
}
//关闭资源
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
//群聊接收端
public class ChatReceive extends Thread {
@Override
public void run() {
try {
//建立udp的服务,要监听一个端口
DatagramSocket socket = new DatagramSocket(9090);
//准备空的数据包存储数据
byte[] buf = new byte[1024];
DatagramPacket packet = new DatagramPacket(buf, buf.length);
boolean flag = true;
while(flag){
socket.receive(packet);
// packet.getAddress() 获取对方数据 包的IP地址对象。 System.out.println(packet.getAddress().getHostAddress()+"说:"+new String(buf,0,packet.getLength()));
}
//关闭资源
socket.close();
}catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
public class ChatMain {
public static void main(String[] args) {
ChatReceive chatReceive = new ChatReceive();
chatReceive.start();
ChatSender chatSender = new ChatSender();
chatSender.start();
}
}
8、udp是一个不可靠(数据包可能会丢失)的协议
什么情况下数据包会出现丢失呢?带宽不足、cpu的处理能力不足
public class SafeSender{
public static void main(String[] args) throws Exception {
//建立udp的服务
DatagramSocket socket = new DatagramSocket();
//准备数据,数据封装到数据中发送
DatagramPacket packet = null;
for(int i = 0 ; i< 10; i++){ //连续发送10个数据包
String data =i +"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
packet = new DatagramPacket(data.getBytes(), data.getBytes().length, InetAddress.getLocalHost(), 9090);
//发送数据包
socket.send(packet);
}
//关闭资源
socket.close();
}
}
public class SafeReceive {
public static void main(String[] args) throws IOException, Exception {
//建立udp的服务
DatagramSocket socket = new DatagramSocket(9090);
//建立空的数据包存储数据
byte[] buf = new byte[1024];
DatagramPacket packet = new DatagramPacket(buf, buf.length);
//不断接收数据包
while(true){
socket.receive(packet);
System.out.println(new String(buf,0,packet.getLength()));
Thread.sleep(10);
}
}
}
五、TCP协议
1、TCP的特点
① tcp是基于IO流进行数据的传输的,面向连接
② tcp进行数据传输的时候是没有大小限制的
③ 通过三次握手机制连接,可靠协议
④ 通信前必须建立连接,效率稍低
⑤ tcp是区分客户端与服务端的。
如:打电话、文件的传送
2、tcp协议下的Socket
Socket(客户端) ,tcp的客户端一旦启动马上要与服务端进行连接。
3、tcp的客户端使用步骤:
① 建立tcp的客户端服务
② 获取到对应的流对象
③ 写出或读取数据
④ 关闭资源
//tcp客户端
public class Clinet {
public static void main(String[] args) throws IOException{
//建立tcp的服务
Socket socket = new Socket(InetAddress.getLocalHost(),9090);
//获取到Socket的输出流对象
OutputStream outputStream = socket.getOutputStream();
//利用输出流对象把数据写出即可。
outputStream.write("服务端你好".getBytes());
//获取到输入流对象,读取服务端回送的数据。
InputStream inputStream = socket.getInputStream();
byte[] buf = new byte[1024];
int length = inputStream.read(buf);
System.out.println("客户端接收到的数据:"+ new String(buf,0,length));
//关闭资源
socket.close();
}
}
//服务端
public class Server {
public static void main(String[] args) throws Exception {
//建立Tcp的服务端,并且监听一个端口。
ServerSocket serverSocket = new ServerSocket(9090);
//接受客户端的连接
Socket socket = serverSocket.accept(); //accept()接受客户端的连接 该方法也是一个阻塞型的方法,没有客户端与其连接时,会一直等待下去。
//获取输入流对象,读取客户端发送的内容。
InputStream inputStream = socket.getInputStream();
byte[] buf = new byte[1024];
int length = 0;
length = inputStream.read(buf);
System.out.println("服务端接收:"+ new String(buf,0,length));
//获取socket输出流对象,想客户端发送数据
OutputStream outputStream = socket.getOutputStream();
outputStream.write("客户端你好啊!".getBytes());
//关闭资源
serverSocket.close();
}
}
4、 需求: 客户端与服务端一问一答聊天。
① 如果使用BuffrerdReader的readline方法一定要加上\r\n才把数据写出。
② 使用字符流一定要调用flush方法数据才会写出。
//聊天的客户端
public class ChatClient {
public static void main(String[] args) throws IOException {
//建立tcp的客户端服务
Socket socket = new Socket(InetAddress.getLocalHost(),9090);
//获取socket的输出流对象。
OutputStreamWriter socketOut = new OutputStreamWriter(socket.getOutputStream());
//获取socket的输入流对象
BufferedReader socketReader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
//获取键盘的输入流对象,读取数据
BufferedReader keyReader = new BufferedReader(new InputStreamReader(System.in));
String line = null;
//不断的读取键盘录入的数据,然后把数据写出
while((line = keyReader.readLine())!=null){
socketOut.write(line+"\r\n");
//刷新
socketOut.flush();
//读取服务端回送的数据
line = socketReader.readLine();
System.out.println("服务端回送的数据是:"+line);
}
//关闭资源
socket.close();
}
}
//聊天的服务端
public class ChatServer {
public static void main(String[] args) throws IOException {
//建立tcp的服务端
ServerSocket serverSocket = new ServerSocket(9090);
//接受客户端的连接,产生一个SOcket
Socket socket = serverSocket.accept();
//获取到Socket的输入流对象
BufferedReader socketReader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
//获取到Socket输出流对象
OutputStreamWriter socketOut = new OutputStreamWriter(socket.getOutputStream());
//获取键盘的输入流对象
BufferedReader keyReader = new BufferedReader(new InputStreamReader(System.in));
//读取客户端的数据
String line = null;
while((line = socketReader.readLine())!=null){
System.out.println("服务端接收到的数据:"+ line);
System.out.println("请输入回送给客户端的数据:");
line = keyReader.readLine();
socketOut.write(line+"\r\n");
socketOut.flush();
}
//关闭资源
serverSocket.close();
}
}
5、编写一个服务端可以给多个客户端发送图片(多线程)
public class ImageServer extends Thread {
Socket socket ;
//使用该集合是用于存储ip地址的。
static HashSet<String> ips = new HashSet<String>();
public ImageServer(Socket socket) {
this.socket = socket;
}
@Override
public void run() {
try {
//获取到socket输出流对象
OutputStream outputStream = socket.getOutputStream();
//获取图片的输入流对象
FileInputStream fileInputStream = new FileInputStream("F:\\美女\\3.jpg");
//读取图片数据,把数据写出
byte[] buf = new byte[1024];
int length = 0 ;
while((length = fileInputStream.read(buf))!=-1){
outputStream.write(buf,0,length);
}
String ip = socket.getInetAddress().getHostAddress();
// socket.getInetAddress() 获取对方的IP地址
if(ips.add(ip)){
System.out.println("恭喜"+ip+"同学成功下载,当前下载的人数是:"+ ips.size());
}
//关闭资源
fileInputStream.close();
socket.close();
}catch (IOException e) {
}
}
public static void main(String[] args) throws IOException {
//建立tcp的服务 ,并且要监听一个端口
ServerSocket serverSocket = new ServerSocket(9090);
while(true){
//接受用户的链接。
Socket socket = serverSocket.accept();
new ImageServer(socket).start();
}
}
}
//下载图片的客户端
public class ImageClient {
public static void main(String[] args) throws Exception{
//建立tcp的服务
Socket socket = new Socket(InetAddress.getLocalHost(),9090);
//获取socket的输入流对象
InputStream inputStream = socket.getInputStream();
//获取文件的输出流对象
FileOutputStream fileOutputStream = new FileOutputStream("F:\\3.jpg");
//边读边写
byte[] buf = new byte[1024];
int length = 0 ;
while((length = inputStream.read(buf))!=-1){
fileOutputStream.write(buf,0,length);
}
//关闭资源
fileOutputStream.close();
socket.close();
}
}
6、实现登陆与注册功能。
客户端与服务端连接的时候,就要提示客户端请选择功能。客户端注册的时候,用户名与密码都是发送给服务端 的,服务端需要把数据保存到服务端的文件上。登陆: 登陆的时候客户端输入用户名与密码发送给服务端,服务端需要校验,返回结果给客户端。
public class LoginClinet {
public static void main(String[] args) throws IOException {
Socket socket = new Socket(InetAddress.getLocalHost(),9090);
//获取socket的输出流对象
OutputStreamWriter socketOut = new OutputStreamWriter(socket.getOutputStream());
//获取到socket的输入流对象
BufferedReader socketReader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
//获取到键盘的输入流对象
BufferedReader keyReader = new BufferedReader(new InputStreamReader(System.in));
while(true){
System.out.println("请选择功能: A(登陆) B(注册)");
String option = keyReader.readLine();
if("a".equalsIgnoreCase(option)){
getInfo(socketOut, keyReader, option);
//读取服务器反馈的信息
String line = socketReader.readLine();
System.out.println(line);
}else if("b".equalsIgnoreCase(option)){
getInfo(socketOut, keyReader, option);
//读取服务器反馈的信息
String line = socketReader.readLine();
System.out.println(line);
}
}
}
public static void getInfo(OutputStreamWriter socketOut,BufferedReader keyReader, String option)
throws IOException {
System.out.println("请输入用户名:");
String userName = keyReader.readLine();
System.out.println("请输入密码:");
String password = keyReader.readLine();
String info = option +" "+userName+" "+password+"\r\n";
socketOut.write(info);
socketOut.flush();
}
}
public class LoginServer extends Thread {
Socket socket;
static File file = new File("F:\\users.properties");
public LoginServer(Socket socket) {
this.socket = socket;
}
static {
try {
if (!file.exists()) {
file.createNewFile();
}
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void run() {
while(true){
try {
// 获取socket的输入流对象
BufferedReader bufferedReader = new BufferedReader(
new InputStreamReader(socket.getInputStream()));
// 获取socket的输出流对象
OutputStreamWriter socketOut = new OutputStreamWriter(
socket.getOutputStream());
// 读取客户端输入的信息
String info = bufferedReader.readLine();
String[] datas = info.split(" ");
// 获取到用户 的选择功能
String option = datas[0];
// 注册
String userName = datas[1];
String password = datas[2];
if ("a".equalsIgnoreCase(option)) {
// 登陆
Properties properties = new Properties();
// 加载配置文件
properties.load(new FileReader(file));
if (properties.containsKey(userName)) {
String tempPass = properties.getProperty(userName);
if (password.equals(tempPass)) {
socketOut.write("欢迎" + userName + "登陆成功\r\n");
} else {
socketOut.write("密码错误\r\n");
}
} else {
socketOut.write("用户名不存在,请重新输入...\r\n");
}
socketOut.flush();
} else if ("b".equalsIgnoreCase(option)) {
// 创建一个配置文件类
Properties properties = new Properties();
//加载原来的配置文件
properties.load(new FileReader(file));
if (!properties.containsKey(userName)) {
// 不存在该用户名
properties.setProperty(userName, password);
// 生成一个配置文件
properties.store(new FileWriter(file), "users");
socketOut.write("注册成功..\r\n");
} else {
// 存在用户名
socketOut.write("用户名已经被注册,请重新输入\r\n");
}
socketOut.flush();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) throws IOException {
ServerSocket serverSocket = new ServerSocket(9090);
while (true) {
Socket socket = serverSocket.accept();
new LoginServer(socket).start();
}
}
}