java回顾之Socket网络编程
一、软件结构
C/S结构 :全称为Client/Server结构,是指客户端和服务器结构。常见程序有QQ、迅雷等软件
特点:
优点:
对网络要求不是很高
缺点:
开发维护周期长
占用空间
B/S结构 :全称为Browser/Server结构,是指浏览器和服务器结构。常见浏览器有谷歌、火狐等
特点
优点:
开发维护周期短
不占用空间
缺点:
对网络网速要求高
二、网络编程三要素
ip地址:
ip地址是计算机的唯一标识。互联网上面的每个设备都有唯一的ip地址
查ip的命令:ipconfig
ip的分类
IPV4:有4个255的段落,192.168.31.56。IPV4已经被全世界的设备用光了
IPV6: ABCD:EF01:2345:6789:ABCD:EF01:2345:6789 ,号称能给世界的每一粒沙子分配一个不重复的IP
端口号:
端口号是软件的唯一标识。每个软件都有一个唯一的端口号,范围是0~65535,其中0~1023是留给系统使用的要 避开。
协议:
协议是传输数据的方式
TCP:面向连接,安全,效率低
三次握手
UDP:面向无连接,不安全,效率高,对数据包有大小限制不能超过64K
三、InetAddress类
InetAddress类成员方法
* static InetAddress getLocalHost()
获得本地主机IP地址对象
* static InetAddress getByName(String host)
根据IP地址字符串或主机名获得对应的IP地址对象
* String getHostName() 获得主机名
* String getHostAddress() 获得IP地址字符串
四、TCP通信程序
代码演示:
客户端发送数据
public class Demo客户端 { public static void main(String[] args) throws IOException { //创建套接字对象 //客户端要指定给谁发送数据 //第一个参数是对方的ip 第二个参数是对方的端口 Socket s = new Socket("192.168.31.87",8888); //Socket里面包含输出流和输入流,我们网络编程需要获取底层流完成数据发送 //获取输出流 OutputStream os = s.getOutputStream(); //键盘输入内容发送 Scanner sc = new Scanner(System.in); String str = sc.nextLine(); //输出(把字符串转成字节数组) os.write(str.getBytes()); //关闭资源 s.close(); } }
服务端接收数据
public class Demo服务器端 { public static void main(String[] args) throws IOException { //创建服务器对象 //参数代表的是自己的端口 ServerSocket ss = new ServerSocket(8888); //接受客户单的连接 //阻塞(如果连接不到客户端就会一直等待) Socket s = ss.accept(); //获取输入流 InputStream is = s.getInputStream(); //接受数据 byte[] arr = new byte[1024]; //默认发送的时候就只发了一句话,不需要使用循环读取 int len = is.read(arr); //打印 System.out.println("客户端对我说:" + new String(arr,0,len)); //关闭资源 s.close(); //ss.close();服务器打开之后一般不会关闭 } }
4.3、TCP模拟聊天
客户端代码 public class Demo客户端 { public static void main(String[] args) throws IOException { //创建套接字对象 //客户端要指定给谁发送数据 //第一个参数是对方的ip 第二个参数是对方的端口 Socket s = new Socket("192.168.31.87",9999); //Socket里面包含输出流和输入流,我们网络编程需要获取底层流完成数据发送 while(true) { //获取输出流 OutputStream os = s.getOutputStream(); //键盘输入内容发送 Scanner sc = new Scanner(System.in); String str = sc.nextLine(); //输出(把字符串转成字节数组) os.write(str.getBytes()); //接受数据 //获取输入流 InputStream is = s.getInputStream(); byte[] arr = new byte[1024]; //读取内容 int len = is.read(arr); //打印 System.out.println("服务器对我说: " + new String(arr, 0, len)); } //关闭资源 //s.close(); } }
服务端代码:
public class Demo服务器端 { public static void main(String[] args) throws IOException { //创建服务器对象 //参数代表的是自己的端口 ServerSocket ss = new ServerSocket(9999); //接受客户单的连接 Socket s = ss.accept(); //阻塞(如果连接不到客户端就会一直等待) while(true) { //获取输入流 InputStream is = s.getInputStream(); //接受数据 byte[] arr = new byte[1024]; //默认发送的时候就只发了一句话,不需要使用循环读取 int len = is.read(arr); //打印 System.out.println("客户端对我说:" + new String(arr, 0, len)); //发送数据 //获取输出流 OutputStream os = s.getOutputStream(); //发送固定语句 os.write("滚".getBytes()); } //关闭资源 //s.close(); //ss.close();服务器打开之后一般不会关闭 } }
4.4、文件上传案例
客户端发送文件 public class Demo文件上传的客户端 { public static void main(String[] args) throws IOException { //创建对象 //Socket s = new Socket("127.0.0.1",5678); //Socket s = new Socket("localhost",5678); Socket s = new Socket("192.168.31.87",5678); //创建输入流 FileInputStream fis = new FileInputStream("C:\资料\小资料\微信图片_20190108110420.jpg"); //获取输出流 OutputStream os = s.getOutputStream(); //每次读取一个字节发送给服务器 int i; while((i = fis.read()) != -1){ //输出 os.write(i); } //关闭资源 fis.close(); s.close(); } } 服务器接收文件 public class Demo文件上传的服务器端 { public static void main(String[] args) throws IOException { //创建对象 ServerSocket ss = new ServerSocket(5678); //获取客户端连接 Socket s = ss.accept(); //获取输入流 InputStream is = s.getInputStream(); //创建输出流 FileOutputStream fos = new FileOutputStream("day18\上传图片.jpg"); //每次读取一个字节输出一个字节 int i; while ((i=is.read()) != -1){ fos.write(i); } //关流 fos.close(); s.close(); } }
有个循环发送的问题。
解决办法:
-
客户端停止发送数据之后,需要通知服务器:
s.shutdownOutput();
4.6、
B/S模式是web的内容,以下是服务器需要给浏览器发送的响应信息,在正式传输数据前,要先写这个。 - ``` 发送图片: "HTTP/1.1 200 OK Content-Type:image/jpeg " 发送文字时: "HTTP/1.1 200 OK Content-Type:text/html;charset=UTF-8 " ``` - 服务器端 public class Demo服务器 { public static void main(String[] args) { try { //创建服务器对象 ServerSocket ss = new ServerSocket(7890); while (true) { //接受浏览器的请求 Socket s = ss.accept(); //获取输入流 InputStream is = s.getInputStream(); //调用读取一行字符串的方法 //readLine() BufferedReader br = new BufferedReader(new InputStreamReader(is)); String str = br.readLine(); //从浏览器发过来的数据 //System.out.println(str); // GET /1.jpg HTTP/1.1 //1.截取用户要访问的资源名称 String[] arr = str.split(" "); String name = arr[1]; // /1.jpg //2.查找文件是否存在 File f = new File("day18/发车" + name); //路径: day18/发车/4.jpg //3.获取输出流 BufferedOutputStream os = new BufferedOutputStream(s.getOutputStream()); if (f.exists()) { //4.创建输入流 BufferedInputStream fis = new BufferedInputStream(new FileInputStream(f)); //要访问的资源存在 //如果存在把图片发送给浏览器 os.write("HTTP/1.1 200 OK Content-Type:image/jpeg ".getBytes()); //发送数据 int i; while ((i = fis.read()) != -1) { os.write(i); } //关流 fis.close(); } else { //要访问的资源不存在 //如果不存在给浏览器"你要访问的资源被外星人劫持了404~" os.write("HTTP/1.1 200 OK Content-Type:text/html;charset=UTF-8 ".getBytes()); os.write("你要访问的资源被外星人劫持了404~".getBytes()); } //关流 os.close(); //关闭 s.close(); } }catch (Exception e){ //如果你想看报错信息 //e.printStackTrace(); } } } - 上面的代码是单线程代码,当多个浏览器都访问同一个服务器时。服务器需要一个一个处理。 - 接下来我们写一个多线程代码。为每一次访问都开启一个线程 public class Demo服务器多线程 { public static void main(String[] args) throws Exception{ //创建服务器对象 ServerSocket ss = new ServerSocket(7890); //可以接受多个浏览器的访问 while(true){ //接受浏览器的请求 Socket s = ss.accept(); //每次接收到浏览器请求就开启新线程 //使用Lambda表达式开启多线程(Runnable是一个函数式接口) Thread t = new Thread(()->{ try { //获取输入流 InputStream is = s.getInputStream(); //调用读取一行字符串的方法 //readLine() BufferedReader br = new BufferedReader(new InputStreamReader(is)); String str = br.readLine(); //从浏览器发过来的数据 //System.out.println(str); // GET /1.jpg HTTP/1.1 //1.截取用户要访问的资源名称 String[] arr = str.split(" "); String name = arr[1]; // /1.jpg //2.查找文件是否存在 File f = new File("day18/发车" + name); //路径: day18/发车/4.jpg //假设每次执行都需要3秒钟,这样为了看到多线程的效果 Thread.sleep(3000); //3.获取输出流 BufferedOutputStream os = new BufferedOutputStream(s.getOutputStream()); if (f.exists()) { //4.创建输入流 BufferedInputStream fis = new BufferedInputStream(new FileInputStream(f)); //要访问的资源存在 //如果存在把图片发送给浏览器 os.write("HTTP/1.1 200 OK Content-Type:image/jpeg ".getBytes()); //发送数据 int i; while ((i = fis.read()) != -1) { os.write(i); } //关流 fis.close(); } else { //要访问的资源不存在 //如果不存在给浏览器"你要访问的资源被外星人劫持了404~" os.write("HTTP/1.1 200 OK Content-Type:text/html;charset=UTF-8 ".getBytes()); os.write("你要访问的资源被外星人劫持了404~".getBytes()); } //关流 os.close(); //关闭 s.close(); }catch (Exception e){ //异常处理 } }); t.start(); } }