网络编程Socket

Posted 猪八戒1.0

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了网络编程Socket相关的知识,希望对你有一定的参考价值。

一.基本介绍

网络三要素:ip,端口,协议

TCP保证接收,UDP不管是否接收到,是不可靠协议。

 

 二.UDP协议的Socket 

         由于UDP是不可靠协议,不管你是否能够接收到,所以当只写了客户端,没写服务端时,运行并不会报错。而实际一般都是先写服务端再写客户端

客户端

package test;

import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.nio.charset.StandardCharsets;

public class Send 
    public static void main(String[] args) 
        /*
        * DatagramSocket
        * 1.创建对象
        * 2.把想发送的数据打包(并且不超过64K)
        * 3.发送
        * */
        DatagramSocket ds=null;
        try 
            ds=new DatagramSocket();
            //ip和端口号都是指服务器的ip和端口号
            byte[] buf="发送了UDP协议的数据".getBytes(StandardCharsets.UTF_8);
            InetAddress adress=InetAddress.getByName("此处填自己电脑ip地址");
            DatagramPacket dp=new DatagramPacket(buf,buf.length,adress,10086);

            ds.send(dp);
         catch (Exception e) 
            throw new RuntimeException(e);
        finally 
            if(ds!=null)
                ds.close();
            
        
    

服务端

package test;

import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;

public class Receive 
    public static void main(String[] args) 
        /*
        * 1.创建接收方对象 new DatagramSocket(10086);需要指定接收的端口
        * 2.创建一个数据包
        * 3.从数据包DatagramPacket里面接收数据
        * */
        DatagramSocket ds=null;
        try 
            //指定哪个端口接收
            ds=new DatagramSocket(10086);

            byte[] buf=new byte[1024];
            DatagramPacket dp=new DatagramPacket(buf,buf.length);

            ds.receive(dp);//阻塞方法 也就是等着客服端发送

            System.out.println(dp.getPort());//得端口号
            System.out.println(dp.getAddress());//得ip地址

            //输出客户端发送的信息
            byte[] b=dp.getData();
            //如果是b.length会出现很多空余
            //System.out.println(new String(b,0,b.length));
            System.out.println(new String(b,0,dp.getLength()));

         catch (Exception e) 
            throw new RuntimeException(e);
        
    

运行结果

看服务端最后 b.length过多出现如下效果

改为 dp.getLength()

 实际需要保证服务端一直运行,所以使用while(true)

服务端

package test;

import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;

public class Receive 
    public static void main(String[] args) 
        /*
        * 1.创建接收方对象 new DatagramSocket(10086);需要指定接收的端口
        * 2.创建一个数据包
        * 3.从数据包DatagramPacket里面接收数据
        * */
        DatagramSocket ds=null;
        try 
            //指定哪个端口接收
            ds=new DatagramSocket(10086);
            while (true)

                byte[] buf=new byte[1024];
                DatagramPacket dp=new DatagramPacket(buf,buf.length);

                ds.receive(dp);//阻塞方法 也就是等着客服端发送 走完这一步之后就已经获取到数据包了

                //输出客户端发送的信息
                byte[] b=dp.getData();
                System.out.println(new String(b,0,dp.getLength()));
            


         catch (Exception e) 
            throw new RuntimeException(e);
        
    

客户端

package test;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.nio.charset.StandardCharsets;

public class Send 
    public static void main(String[] args) 
        /*
        * DatagramSocket
        * 1.创建对象
        * 2.把想发送的数据打包(并且不超过64K)
        * 3.发送
        * */
        DatagramSocket ds=null;
        BufferedReader br;
        try 
            ds=new DatagramSocket();
            br=new BufferedReader(new InputStreamReader(System.in));
            String line="";
            while ((line=br.readLine())!=null)
                if(line.equals("886"))
                    break;
                
                InetAddress adress=InetAddress.getByName("此处写自己电脑ip地址");
                DatagramPacket dp=new DatagramPacket(line.getBytes(StandardCharsets.UTF_8)
                        , line.length(),adress,10086);
                ds.send(dp);
            

         catch (Exception e) 
            throw new RuntimeException(e);
        finally 
            if(ds!=null)
                ds.close();
            
        
    

程序没有问题,运行可能出现乱码情况,这是代码编辑器的问题

三.TCP协议

可靠协议,必须建立连接通道,在连接通道内进行数据传输,通过三次握手建立连接

 socket双向通信

 服务端

package test;

import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;

public class Service 
    public static void main(String[] args) 

        ServerSocket server=null;
        try 
            server=new ServerSocket(10086);

            Socket socket = server.accept();
            InputStream is = socket.getInputStream();
            byte[] b=new byte[1024];
            int len=0;
            while ((len=is.read(b))!=-1)
                System.out.println(new String(b,0,len));
            
         catch (IOException e) 
            throw new RuntimeException(e);
        

    

客户端

package test;

import java.io.IOException;
import java.io.OutputStream;
import java.net.Socket;
import java.nio.charset.StandardCharsets;

public class Client 
    public static void main(String[] args) 
        /*
        * 1.创建一个客户端的socket对象
        * 2.建立连接(很简单,但是不好理解)
        * 3.通过io流在管道里面传输数据
        * 4.关闭socket
        *
        * 当我们new对象的时候就建立连接了
        * */
        Socket client=null;
        try 
            client=new Socket("192.168.65.216",10086) ;
            //得到输出流
            OutputStream os=client.getOutputStream();
            os.write("tcp".getBytes(StandardCharsets.UTF_8));
         catch (Exception e) 
            throw new RuntimeException(e);
        finally 
            try 
                if(client!=null)
                    client.close();
                
             catch (IOException e) 
                throw new RuntimeException(e);
            
        

    

上传图片

使用flush刷新下比较好,不用可能图片少一点

package test1;

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;

public class Server 
    public static void main(String[] args) 
        ServerSocket server=null;
        try 
            server=new ServerSocket(10000);
            Socket client = server.accept();
            //client.getInputStream()得到客户端数据
            BufferedInputStream bis=new BufferedInputStream(client.getInputStream());

            BufferedOutputStream bos=new BufferedOutputStream(new FileOutputStream("new.jpg")) ;
            byte[] b=new byte[1024];
            int len=0;
            while ((len=bis.read(b))!=-1)
                bos.write(b,0,len);
                bos.flush();
            

            bos.close();
            client.close();
            server.close();
         catch (Exception e) 
            throw new RuntimeException(e);
        
    

package test1;

import java.io.*;
import java.net.Socket;

public class Client 
    public static void main(String[] args) 
        Socket client = null;

        try 
            client = new Socket("电脑ip", 10000);
            BufferedOutputStream bos =new BufferedOutputStream(client.getOutputStream()) ;
            BufferedInputStream bis = new BufferedInputStream(new FileInputStream("C:\\\\Users\\\\27955\\\\Desktop\\\\1.jpg"));
            byte[] b = new byte[1024];
            int len = 0;
            while ((len = bis.read(b)) != -1) 
                bos.write(b, 0, len);
                bos.flush();
            

            bis.close();
            client.close();
         catch (IOException e) 
            throw new RuntimeException(e);
        

    

服务器向客户端发送信息

package test1;

import java.io.IOException;
import java.io.InputStream;
import java.net.Socket;

public class Client1 
    public static void main(String[] args) throws IOException 
        Socket client = new Socket("电脑ip",10001);
        InputStream is = client.getInputStream();
        byte[] b=new byte[1024];
        int len=0;
        while ((len=is.read(b))!=-1)
            System.out.println(new String(b,0,len));
        
    

package test1;

import java.io.IOException;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.charset.StandardCharsets;

public class Server1 
    public static void main(String[] args) throws IOException 
        ServerSocket server= new ServerSocket(10001);
        Socket client = server.accept();
        OutputStream os = client.getOutputStream();
        os.write("hahaha".getBytes(StandardCharsets.UTF_8));
        client.close();
    

四.综合练习

编写服务端用于接收上传的图片,连接用的端口号为 10203,接收到的图片存储于 /home/project/pic 目录中,并将其命名为 mn.jpg,接收完成后向客户端传输一个 “接收完成” 。
编写客户端用于上传图片。接收到服务端“接收完成”的信息后,向控制台输出 “上传完成” 。

package service;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileOutputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.charset.StandardCharsets;

public class UploadService 
    public static void main(String[] args) 
        ServerSocket server=null;
        try 
            server=new ServerSocket(10203);
            Socket client = server.accept();
            //client.getInputStream()得到客户端数据
            BufferedInputStream bis=new BufferedInputStream(client.getInputStream());

            OutputStream os = client.getOutputStream();
            os.write("接收完成".getBytes(StandardCharsets.UTF_8));

            BufferedOutputStream bos=new BufferedOutputStream(new FileOutputStream("/home/project/pic/mn.jpg")) ;
            byte[] b=new byte[1024];
            int len=0;
            while ((len=bis.read(b))!=-1)
                bos.write(b,0,len);
                bos.flush();
            



            bos.close();
            client.close();
            server.close();
         catch (Exception e) 
            throw new RuntimeException(e);
        
    

package client;

import java.io.*;
import java.net.Socket;

public class UploadClient 
    public static void main(String[] args) 
        Socket client = null;

        try 
            client = new Socket("localhost", 10203);
            BufferedOutputStream bos =new BufferedOutputStream(client.getOutputStream()) ;
            BufferedInputStream bis = new BufferedInputStream(new FileInputStream("/home/project/pic/timg.jpg"));
            byte[] b = new byte[1024];
            int len = 0;
            while ((len = bis.read(b)) != -1) 
                bos.write(b, 0, len);
                bos.flush();
            

            InputStream is = client.getInputStream();
            byte[] b2=new byte[1024];
            int len2=0;
            while ((len2=is.read(b2))!=-1)
                if((new String(b2,0,len2)).equals("接收完成"))
                    System.out.println("上传完成");
            

            bis.close();
            client.close();
         catch (IOException e) 
            throw new RuntimeException(e);
        

    

五.使用底层技术实现爬虫操作

爬虫可以将网络上的资源作为己用,但是那些资源的拥有者是否允许开发者爬取他们的数据呢? 🙄

因此在使用爬虫时,稍有不慎,就可能面临法律纠纷,大家在使用爬虫时务必要注意法律问题。

以下列举的一些禁止爬取的场景,但由于非法律专业人员,以下列举场景仅供大家参考,爬虫的禁止场景并不限于此。

在实际工作中,如果有涉及爬虫开发的地方,建议大家务必咨询一下公司的法务人员或者相关法律人士。

不要大规模的爬取网络数据,或对对方的服务器造成较大影响。
不要将爬虫用于灰色产业、敏感行业,或使用爬虫非法获利。
不要使用爬虫获取网站或用户的隐私数据。
不要违背 robots 协议或经营者意志。
不要使用爬虫进行任何法律、法规或道德禁止的行为。

从技术体系而言,爬虫可以分为三个部分:获取海量数据、解析数据和存储数据。

接下来,我们模拟爬取蓝桥云课首页的数据。

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URL;
import java.net.URLConnection;
/**
 * 爬虫获取海量数据
 */
public class TestCrawler 
    //获取蓝桥云课首页的html源码
    public static String getResource() 
        BufferedReader reader = null;
        // 创建字符串缓冲区,存放爬取的数据
        StringBuffer html = new StringBuffer();
        try 
            // 创建 URL 对象
            URL url = new URL("https://www.lanqiao.cn/");
            // 通过 URL 对象获取 URLConnection 对象
            URLConnection urlConnection = url.openConnection();
            // 建立连接
            urlConnection.connect();
            // 通过 IO 流提取数据信息
            reader = new BufferedReader(new InputStreamReader(urlConnection.getInputStream()));
            String line = null;
            while ((line = reader.readLine()) != null) 
                html.append(line);
            
         catch (Exception e) 
            e.printStackTrace();
         finally 
            // 流资源释放
            try 
                if (reader != null) 
                    reader.close();
                
             catch (IOException e) 
                e.printStackTrace();
            
        
        return html.toString();
    

    public static void main(String[] args) 
        String html = getResource();
        System.out.println("蓝桥云课首页的源码如下所示:\\n"+html);
    

作为简单的模拟爬虫,我们实现从源码中解析出网站标题,即上述 <meta> 标签中的 content 值。

🤔 那么如何解析呢?

我们知道,源码其实就是字符串,因此完全可以用字符串解析的相关方法进行解析。

但纯字符串解析有些繁琐,本次会采用 “正则表达式+字符串解析” 结合的方式来获取 content 值。

正则表达式描述了一种字符串匹配的模式(pattern),可以用来检查一个串是否含有某种子串,可替换匹配的子串,或者提取匹配的子串。

👉 这里我们回顾一下之前学过的正则表达式的常用方法,如下所示:

Pattern.compile(String regex):根据参数 regex 创建一个正则表达式实例;
Matcher matcher(CharSequence input):对整个输入字符串 input 进行匹配。
假设现在要在 A 字符串中,查找是否含有 B 内容,就可以通过以下形式进行匹配查询:

Pattern pattern = Pattern.compile(A);
Matcher matcher = pattern.matcher(B);

之后再结合 matcher.find() 方法进行正则匹配,如果 matcher.find() 的返回值是 true,就说明 A 中存在符合条件的 B,否则说明不存在。

修改已存在的 TestCrawler.java 源文件,添加一个 parseResource() 方法,用来完成从蓝桥云课首页的源码中解析出 <meta> 标签中的 content 属性值,源代码如下:

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URL;
import java.net.URLConnection;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
 * 爬虫获取海量数据
 */
public class TestCrawler 
    //获取蓝桥云课首页的html源码
    public static String getResource() 
        BufferedReader reader = null;
        // 创建字符串缓冲区,存放爬取的数据
        StringBuffer html = new StringBuffer();
        try 
            // 创建 URL 对象
            URL url = new URL("https://www.lanqiao.cn/");
            // 通过 URL 对象获取 URLConnection 对象
            URLConnection urlConnection = url.openConnection();
            // 建立连接
            urlConnection.connect();
            // 通过 IO 流提取数据信息
            reader = new BufferedReader(new InputStreamReader(urlConnection.getInputStream()));
            String line = null;
            while ((line = reader.readLine()) != null) 
                html.append(line);
            
         catch (Exception e) 
            e.printStackTrace();
         finally 
            // 流资源释放
            try 
                if (reader != null) 
                    reader.close();
                
             catch (IOException e) 
                e.printStackTrace();
            
        
        return html.toString();
    

    // 从蓝桥云课首页的源码中解析特定的数据
    public static String parseResource(String html) 
        /*
            (.+?) :表示匹配一次符合条件的值,即从蓝桥网首页的源码中,寻找以下内容:
            <meta data-n-head="ssr" data-hid="description" name="description" content=
        */
        Pattern pattern = Pattern.compile("meta data-n-head=\\"ssr\\" data-hid=\\"description\\" name=\\"description\\" content=\\"(.+?)\\"");
        //从蓝桥云课首页的源码中,提取符合pattern约束的字符串
        Matcher matcher = pattern.matcher(html);
        String result = null;
        //判断是否存在 符合约束的字符串
        if (matcher.find()) 
            //提取出全部符合条件的值,即提取出<meta>标签中的content属性值
            result = matcher.group(0);
            result = result.substring(result.indexOf("content=") + "content=".length());
        
        return result;
    

    public static void main(String[] args) 
        String html = getResource();
        // System.out.println("蓝桥云课首页的源码如下所示:\\n"+html);
        String result = parseResource(html);
        System.out.println(result == null ? "爬取失败" : "description 的 content 值是:" + result);
    

总结

本实验主要讲解的是爬虫获取海量数据和解析数据。

通过 URLConnection 类完成数据爬取操作。
数据的获取也是离不开 IO 流的,因此流是无处不在的。
将之前所学的字符串缓冲区、IO 流和网络编程结合进行操作,同时也可以巩固所学的知识。
通过 Java 自带的正则表达式类完成数据解析操作。
通过 Pattern 和 Matcher 类进行正则匹配操作。
字符串中的 substring()、indexOf() 和 length() 常用方法进行处理。
模拟了爬取网页的数据信息,这虽然在搜集海量数据的时候非常有用,但是不能随意使用,因为随意爬取有些网站的数据是可以构成违法行为的,一点要谨慎使用

以上是关于网络编程Socket的主要内容,如果未能解决你的问题,请参考以下文章

网络新媒体专业的所学课程都有哪些?

socket 编程

socket网络编程

网络编程-Socket介绍

C++ socket编程 和 MFC socket编程 有啥区别??

Python Socket编程基础篇