JAVA网络编程入门

软件结构

  1. C/S结构

  2. B/S结构



无论哪一种结构,都离不开网络的支持。网络编程,就是在网络的条件下实现机器间的通信的过程

网络通信协议

网络通信协议:通信双方必须同时遵守才能完成数据交换









UDP:无连接性,数据被限制在64kb,适用于丢包问题不太大的情况,效率高



TCP:面向连接,可靠无差错,三次握手

网络编程三要素:协议,IP地址和端口号

  1. ip地址



    查看本机Ip地址:控制台输入ipconfig

    查看是否可以和某一台计算机进行网络交换:ping ip地址

  2. 端口号



用协议+IP地址+端口号可以唯一的表示网络中的进程并进行进程间的通信了

软件在启动时可以和操作系统要指定的端口号,或者由操作系统分配随机的端口号

端口号由两个字节组成,范围为0-65535

TCP通信程序

TCP通信可以实现两台计算机之间的数据交互,通信的两端要严格区分客户端和服务器端



客户端和服务器端进行一个数据交流,一共需要4个IO流对象:





? Socket s1=server.accept();//获取客户端的流对象



java.net

套接字(Socket):包含了ip地址和端口号的网络单位(可以借此识别设备和进程)

  1. 客户端



这里使用Socket的输入流来读,输出流来写,不要混了

  1. 服务器端



服务器端必须明确知道是哪个客户端请求的服务器,所以要使用accept方法

一个TCP通信的小例子:

TCPServer.java

import java.io.IOException;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket; import org.omg.CORBA.portable.InputStream; /*
*@author JiaDing
*服务器端
*/
public class TCPServer {
public static void main(String[]args)throws IOException{
//创建使用特定端口的对象
ServerSocket server=new ServerSocket(8888);
Socket socket=server.accept();
java.io.InputStream is=socket.getInputStream();
byte[]bytes=new byte[1024];
int len=is.read(bytes);
System.out.println(new String(bytes,0,len));//利用字符数组创建字符串
OutputStream os=socket.getOutputStream();
os.write("收到谢谢".getBytes());//要将字符串转换为字符数组才行
socket.close();//销毁socket对象
server.close();//销毁server对象
; }
}

TCPClient.java

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket; /*
*@author JiaDing
*TCP通信的客户端
*使用host(服务器主机的名称/IP地址)和port(端口号)的构造方法
*getOutputStream 返回套接字的输出流,使用其write方法给服务器发送数据
*getInputStream 返回套接字的输入流,使用其read方法读取服务器回写数据
*close关闭套接字
*/ public class TCPClient {
public static void main (String[] args) throws IOException {
Socket socket=new Socket("127.0.0.1",8888);
OutputStream os=socket.getOutputStream();
os.write("你好服务器".getBytes());
InputStream is=socket.getInputStream();
byte[]bytes=new byte[1024];
int len=is.read(bytes);
System.out.println(new String(bytes,0,len));
socket.close();
} }

TCP通信往往是按字节处理而不是字符处理,因为不是所有类型都是字符

127.0.0.1是什么地址?*

127.0.0.1/8整个都是环回地址,用来测试本机的TCP/IP协议栈,发往这段A类地址数据包不会出网卡,网络设备不会对其做路由。

实例:文件上传



Client.java

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.Socket; /*
*@author JiaDing
*文件上传案例的客户端
*/
public class Client {
public static void main(String[]args)throws IOException {
FileInputStream fis=new FileInputStream("E:\\out.txt");
Socket socket=new Socket("127.0.0.1",8888);
java.io.OutputStream os=socket.getOutputStream();
byte[] re=new byte[1024];//准备了1kb的大小
int len=0;
while((len=fis.read(re))!=-1) {//这里一定要给len=fis.read(re)加一个括号,否则两个等号无法区分优先级
os.write(re,0,len);
}
/*
* 注意!这里有一个很重要的问题!
* 由于我们的判断条件是len!=-1,所以我们无法将文件的结束标记上传过去,这样服务器会一直处于等待状态,造成阻塞
* 而对应的,由于服务器阻塞,所以无法执行回显,is.read()也就无法读入数据,同样陷入阻塞
* 解决办法就是在客户端利用套接字的方法人工结束输出流
*/
socket.shutdownOutput();
InputStream is=socket.getInputStream();
while((len=is.read(re))!=-1) {//这里一定要给len=fis.read(re)加一个括号,否则两个等号无法区分优先级
System.out.println(new String(re,0,len));
}
fis.close();
socket.close();
} }

Server.java

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket; /*
*@author JiaDing
*文件上传案例的服务器端
*/
public class Server {
public static void main(String[]args) throws IOException{
ServerSocket ss=new ServerSocket(8888);
Socket socket=ss.accept();
File file=new File("E:\\update");
if(!file.exists())
file.mkdir();
byte[] by=new byte[1024];
InputStream is=socket.getInputStream();
FileOutputStream out=new FileOutputStream("E:\\update\\t1.txt");
int len=0;
while(len!=-1) {
out.write(by, 0, len);
len=is.read(by);
}
OutputStream os=socket.getOutputStream();
os.write("Update is finished!".getBytes());
socket.close();
out.close();
ss.close(); }
}

优化:

  1. 名称写死了:自定义服务器端命名规则,防止同名文件被覆盖
//自定义文件名规则:域名+毫秒值+随机数
//毫秒值广泛应用在服务器端文件名保存上
String name="jd"+System.currentTimeMillis()+new Random().nextInt(999999)+".txt";
FileOutputStream out=new FileOutputStream("E:\\update\\"+name);
  1. 服务器上传后就停止:让服务器一直处于监听状态:使用死循环,服务器不关闭

B\S服务器的模拟

服务器代码-返回请求版:

import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket; /*
*@author JiaDing
*BS版本的TCP服务器
*/
public class Server {
public static void main(String[]args)throws IOException{
ServerSocket server=new ServerSocket(8080);
Socket socket=server.accept();
InputStream is=socket.getInputStream();
byte[]by=new byte[1024];
int len=0;
while((len=is.read(by))!=-1) {
System.out.println(new String(by,0,len));
}
} }

访问时要指明文件夹名\要访问的资源名,如果端口不是80还要在IP后面加上端口号

服务器端结果(其实就是客户端的请求信息):

GET /src/Test1.html HTTP/1.1

Host: 127.0.0.1:8080

Connection: keep-alive

DNT: 1

Upgrade-Insecure-Requests: 1

User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.75 Safari/537.36

Sec-Fetch-Mode: navigate

Sec-Fetch-User: ?1

Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,/;q=0.8,application/signed-exchange;v=b3

Sec-Fetch-Site: none

Accept-Encoding: gzip, deflate, br

Accept-Language: zh-CN,zh;q=0.9,en;q=0.8

服务器应该回写应该网页(文件),我们需要读取文件地址,地址已经包含在了请求第一行

可以使用BufferedReader中的方法readList读取第一行



服务器代码—返回网页版:

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket; /*
*@author JiaDing
*BS版本的TCP服务器
*/
public class Server {
public static void main(String[]args)throws IOException{
ServerSocket server=new ServerSocket(8080);
Socket socket=server.accept();
InputStream is=socket.getInputStream();
/*byte[]by=new byte[1024];
int len=0;
while((len=is.read(by))!=-1) {
System.out.println(new String(by,0,len));
}*/
//把is网络字节输入流对象转换为字符缓存输入流
BufferedReader br=new BufferedReader(new InputStreamReader(is));
String line=br.readLine();
String htmlpath=line.split(" ")[1].substring(1);//取第一行中间的地址,去掉最前面的、
//System.out.println(htmlpath);
FileInputStream fis=new FileInputStream(htmlpath);
OutputStream os=socket.getOutputStream();
//写入HTTP协议响应头,固定写法
os.write("HTTP/1.1 200 OK\r\n".getBytes());
os.write("Content-Type:text/html\r\n".getBytes());
//必须写入空行,否则浏览器不解析
os.write("\r\n".getBytes());
int len=0;
byte[]by=new byte[1024];
while((len=fis.read(by))!=-1) {
os.write(by,0,len);
}
fis.close();
socket.close();
server.close();
} }

  • 为什么服务器要是多线程呢?

    如果不采用多线程,那么服务器只能线型地处理浏览器的请求,浏览器上的资源只能一个一个打开,体验会很差

采样头多线程之后的代码:

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket; /*
*@author JiaDing
*BS版本的TCP服务器
*/
public class Server {
public static void main(String[]args)throws IOException{
ServerSocket server=new ServerSocket(8080);
while(true) {
Socket socket=server.accept();
new Thread(new Runnable() {
@Override
public void run() {
try {
InputStream is=socket.getInputStream();
//把is网络字节输入流对象转换为字符缓存输入流
BufferedReader br=new BufferedReader(new InputStreamReader(is));
String line=br.readLine();
String htmlpath=line.split(" ")[1].substring(1);//取第一行中间的地址,去掉最前面的、
//System.out.println(htmlpath);
FileInputStream fis=new FileInputStream(htmlpath);
OutputStream os=socket.getOutputStream();
//写入HTTP协议响应头,固定写法
os.write("HTTP/1.1 200 OK\r\n".getBytes());
os.write("Content-Type:text/html\r\n".getBytes());
//必须写入空行,否则浏览器不解析
os.write("\r\n".getBytes());
int len=0;
byte[]by=new byte[1024];
while((len=fis.read(by))!=-1) {
os.write(by,0,len);
}
fis.close();
socket.close();
}catch(IOException e) {
e.printStackTrace();
}
}
}).start(); }
} }

注意这里会报一个错误:java.io.FileNotFoundException: favicon.ico。这是因为浏览器会自动访问网站的图标而我们的网站没有的原因,这个图标必须放在根目录,例如如果访问的地址是“http://127.0.0.1/src/Test1.html”,那么就要放在src文件夹的外面

另外,实验了一下,将服务器的端口改成了80好像并没有什么问题,因为服务器监听的是本机的80端口,而如果我们访问外网的话,应该是访问外网网站服务器的80端口,两者并不冲突。部署在本机上的服务器只能监听到访问本机且本端口的请求。所以,网页中如果有外链,那么打开这个外链的过程和我们的服务器是没有丝毫关系的,我们自建的服务器的好坏不会对其造成影响

JAVA网络编程入门的更多相关文章

  1. 【Java】Java网络编程菜鸟进阶:TCP和套接字入门

    Java网络编程菜鸟进阶:TCP和套接字入门 JDK 提供了对 TCP(Transmission Control Protocol,传输控制协议)和 UDP(User Datagram Protoco ...

  2. 20145225《Java程序设计》 实验五 Java网络编程及安全

    20145225<Java程序设计> 实验五 Java网络编程及安全 实验报告 一.实验内容 基于Java Socket实现安全传输. 基于TCP实现客户端和服务器,结对编程一人负责客户端 ...

  3. 20145215实验五 Java网络编程及安全

    20145215实验五 Java网络编程及安全 实验内容 掌握Socket程序的编写: 掌握密码技术的使用: 设计安全传输系统. 实验步骤 本次实验我的结对编程对象是20145208蔡野,我负责编写客 ...

  4. JAVA网络编程【转】出处不详

    网络编程 网络编程对于很多的初学者来说,都是很向往的一种编程技能,但是很多的初学者却因为很长一段时间无法进入网络编程的大门而放弃了对于该部分技术的学习. 在 学习网络编程以前,很多初学者可能觉得网络编 ...

  5. 【转】JAVA 网络编程

    网络编程 网络编程对于很多的初学者来说,都是很向往的一种编程技能,但是很多的初学者却因为很长一段时间无法进入网络编程的大门而放弃了对于该部分技术的学习. 在 学习网络编程以前,很多初学者可能觉得网络编 ...

  6. 脑残式网络编程入门(六):什么是公网IP和内网IP?NAT转换又是什么鬼?

    本文引用了“帅地”发表于公众号苦逼的码农的技术分享. 1.引言 搞网络通信应用开发的程序员,可能会经常听到外网IP(即互联网IP地址)和内网IP(即局域网IP地址),但他们的区别是什么?又有什么关系呢 ...

  7. 脑残式网络编程入门(三):HTTP协议必知必会的一些知识

    本文原作者:“竹千代”,原文由“玉刚说”写作平台提供写作赞助,原文版权归“玉刚说”微信公众号所有,即时通讯网收录时有改动. 1.前言 无论是即时通讯应用还是传统的信息系统,Http协议都是我们最常打交 ...

  8. 脑残式网络编程入门(二):我们在读写Socket时,究竟在读写什么?

    1.引言 本文接上篇<脑残式网络编程入门(一):跟着动画来学TCP三次握手和四次挥手>,继续脑残式的网络编程知识学习 ^_^. 套接字socket是大多数程序员都非常熟悉的概念,它是计算机 ...

  9. 脑残式网络编程入门(一):跟着动画来学TCP三次握手和四次挥手

    .引言 网络编程中TCP协议的三次握手和四次挥手的问题,在面试中是最为常见的知识点之一.很多读者都知道“三次”和“四次”,但是如果问深入一点,他们往往都无法作出准确回答. 本篇文章尝试使用动画图片的方 ...

随机推荐

  1. 通过ID获取元素

    网页由标签将信息组织起来,而标签的id属性值是唯一的,就像是每人有一个身份证号一样,只要通过身份证号就可以找到相对应的人.那么在网页中,我们通过id先找到标签,然后进行操作. 语法: document ...

  2. LC 932. Beautiful Array

    For some fixed N, an array A is beautiful if it is a permutation of the integers 1, 2, ..., N, such ...

  3. gateway 整合 websocket demo

    背景: 这个websocket  因为使用的地方不多,并没有独立出一个项目,是集成在已有的服务中. 1: gateway 配置 - id: service-test   uri: lb:ws://se ...

  4. flutter vscode创建objc工程

    通过vscode创建flutter工程时,默认创建的是swift项目工程,如果想要修改,可以通过vscode设置默认创建语言 1. Open vscode settings. Under User S ...

  5. Python中sort和sorted函数代码解析

    Python中sort和sorted函数代码解析 本文研究的主要是Python中sort和sorted函数的相关内容,具体如下. 一.sort函数 sort函数是序列的内部函数 函数原型: L.sor ...

  6. 基于durid的JDBCUtils工具类

    1.JDBCUtils类 package com.alphajuns.utils; import com.alibaba.druid.pool.DruidDataSourceFactory; impo ...

  7. C# Timespan Tostring 时分秒格式

    timeSpan.ToString(@"hh\:mm\:ss");

  8. JS apply 、call和bind

    JS当中的call .apply.和bind 这三个方法都是js function当中自带的方法,用来改变this的指向. call()方法 语法格式: fun.call(thisArg[,arg1[ ...

  9. flask 框架快速入门

    flask 框架快速入门 搭建一个简易flask项目 首先使用 Pycharm创建flask项目 运行flask项目 1.使用Pycharm搭建flask项目 (如果Pycharm新建项目中未出现该图 ...

  10. python pandas dataframe 读取和写入Oracle

    1.代码:主要写入时表要为小写,否则报错 Could not reflect: requested table(s) not available in Engine from sqlalchemy i ...