Socket网络编程

web开发基础

C/S架构

服务器-客户机,即Client-Server(C/S)结构。C/S结构通常采取两层结构。服务器负责数据的管理,客户机负责完成与用户的交互任务。
例如我们需要下载QQ、微信、电脑版吃鸡,如果该客户端软件需要升级,用户需要重新下载最新版本的客户端下载安装。
C(客户端Client)/S(Server)架构 
C(客户端Client)/S(Server)架构  桌面应用程序 
java swing 、 c# 
 
下载对应的安装包
安装成功之后
才可以使用

B/S架构

什么是web开发
Web:全球广域网,也称为万维网(www),也就是能够通过浏览器访问的网站
例如 通过浏览器访问 www.mayikt.com 、www.baidu.com、www.taobao.com。
JavaWeb开发:是使用java技术栈开发Web项目。


什么是B/S架构
B/S架构的全称为Browser/Server,即浏览器/服务器结构,Browser指的是Web浏览器
它的特点是 客户端只需要安装浏览器,应用程序的逻辑和数据都存放在服务器端共享访问。
优点:易于维护升级:服务器端升级后,客户端(浏览器端无需升级)就获取最新的版本
静态web资源(如html 页面、js、css、images):指web页面中供人们浏览的数据始终是不变。
动态web资源:指web页面中供人们浏览的数据是由程序产生的,不同时间点访问web页面看到的内容各不相同。
  静态web资源开发技术:Html js、css、images
  常用动态web资源开发技术:JSP/Servlet、ASP、PHP等
  在Java中,动态web资源开发技术统称为Javaweb。
 
缺点
B/S体系架构的软件 版本升级的时候 客户端是无需升级的 只需要重新刷新网页即可。
缺陷:会占用服务器端带宽资源。


Http协议原理

Http请求格式

请求数据格式:
1.请求行 请求方法(get、post)、url  (/首页) http协议版本1.1版本 请求第一行
GET /front/showcoulist HTTP/1.1
2.请求头 (键值对的形式)
Host: www.mayikt.com Connection: keep-alive Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.159 Safari/537.36 
Accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9 Referer: http://www.mayikt.com/ Accept-Encoding: gzip, deflate Accept-Language: zh-CN,zh;q=0.9 Cookie: __yjs_duid=1_92c8557b9791d34466e53ea75410e2c01649305342102; JSESSIONID=B303350E58BA0F1230E0B66A9ADCD35F; Hm_lvt_eaa952b8610db6f155ab0febd442e89a=1649305345,1649313679; Hm_lpvt_eaa952b8610db6f155ab0febd442e89a=1649316541
3.请求体
 
1.请求行:请求数据第一行 
1.1由3部分组成,分别为:请求方法、URL 以及协议版本,之间由空格分隔
1.2请求方法包括GET、HEAD、PUT、POST、TRACE、OPTIONS、DELETE以及扩展方法,当然并不是所有的服务器都实现了所有的方法,部分方法即便支持,处于安全性的考虑也是不可用的
1.3协议版本的格式为:HTTP/主版本号.次版本号,常用的有HTTP/1.0和HTTP/1.1
2.请求头:
第二行开始,格式 key:value形式 
Accept-Ranges: bytes
Access-Control-Allow-Origin: *
Access-Control-Expose-Headers: X-Log, X-Reqid
Access-Control-Max-Age: 2592000
常见http协议请求头
Host:接受请求的服务器地址,可以是IP:端口号,也可以是域名
User-Agent:发送请求的应用程序名称
Connection:指定与连接相关的属性,如Connection:Keep-Alive
Accept-Charset:通知服务端可以发送的编码格式
Accept-Encoding:通知服务端可以发送的数据压缩格式
Accept-Language:通知服务端可以发送的语言
3.请求体:
post请求的最后一部分,存放发送请求的参数
userName=mayikt&age=26
 
get与post请求区别
1.get请求请求的参数在请求行中,没有请求体;
2.post请求请求参数在请求体中;
3.get请求请求参数有大小限制,post请求没有

Http响应格式

响应格式分为3个部分
1.响应行:响应数据第一行   http协议版本1.1版本  HTTP/1.1 200
200表示响应状态码 ok为 成功状态
2.响应头:第二行开始 格式 key value
Location: http://www.baidu.com(服务端需要客户端访问的页面路径)
Server:apache tomcat(服务端的Web服务端名)
Content-Encoding: gzip(服务端能够发送压缩编码类型)
Content-Length: 80(服务端发送的压缩数据的长度)
Content-Language: zh-cn(服务端发送的语言类型)
Content-Type: text/html; charset=GB2312(服务端发送的类型及采用的编码方式)
Last-Modified: Tue, 11 Jul 2000 18:23:51 GMT(服务端对该资源最后修改的时间)
Refresh: 1;url=http://www.mayikt.com(服务端要求客户端1秒钟后,刷新,然后访问指定的页面路径)
Content-Disposition: attachment; filename=aaa.zip(服务端要求客户端以下载文件的方式打开该文件)
Transfer-Encoding: chunked(分块传递数据到客户端)
Set-Cookie:SS=Q0=5Lb_nQ; path=/search(服务端发送到客户端的暂存数据)
Expires: date(Cache-Control过期时间)
Cache-Control: no-cache(服务端禁止客户端缓存页面数据) max-age=xx(通知浏览器:xx秒之内别来烦我,自己从缓冲区中刷新)
Pragma: no-***(服务端禁止客户端缓存页面数据)
Connection: close(1.0)/(1.1)Keep-Alive(维护客户端和服务端的连接关系)
Date: Tue, 11 Jul 2000 18:23:51 GMT(服务端响应客户端的时间)   
   服务器端响应结果给客户端类型:(Content-Type)
   1.text/html;charset=UTF-8
   2.image类型
3.响应体  存放服务器响应给客户端的内容


socket

概述

1、计算机网络是通过传输介质、通信设施和网络通信协议,把分散在不同地点的计算机设备互连起来,实现资源共享和数据传输的系统。网络编程就就是编写程序使联网的两个(或多个)设备(例如计算机)之间进行数据传输。Java语言对网络编程提供了良好的支持,通过其提供的接口我们可以很方便地进行网络编程。例如我们的QQ聊天
2、Java是 Internet 上的语言,它从语言级上提供了对网络应用程 序的支持,程序员能够很容易开发常见的网络应用程序。
3、Java提供的网络类库,可以实现无痛的网络连接,联网的底层细节被隐藏在 Java 的本机安装系统里,由 JVM 进行控制。并 且 Java 实现了一个跨平台的网络库,程序员面对的是一个统一的网络编程环境。

网络通讯三要素

IP地址
端口号
协议
1、我们需要知道的是主机间通过网络进行通信是需要遵循网络通信协议,是通过IP地址准确定位主机,通过端口号准确定位主机上的应用,例如IP地址和端口号  192.168.110.1:80
2、如何实现网络中的主机互相通信?
① 通信双方地址:IP和端口号
② 一定的规则协议。 tcp或者udp

IP地址准确定位主机

1.IP 地址:InetAddress(在Java中使用InetAddress类代表IP)
一的标识 Internet 上的计算机(通信实体)
2.本地回环地址(hostAddress):127.0.0.1 主机名(hostName):localhost
3.IP地址分类方式1:IPV4 和 IPV6
 3.1 IPV4:4个字节组成,4个0-255。大概42亿,30亿都在北美,亚洲4亿。2011年初已 经用尽。以点分十进制表示,如192.168.0.1
 3.2 IPV6:128位(16个字节),写成8个无符号整数,每个整数用四个十六进制位表示, 数之间用冒号(:)分开,如:3ffe:3201:1401:1280:c8ff:fe4d:db39:1984
4.IP地址分类方式2:公网地址(万维网使用)和私有地址(局域网使用)。192.168. 开头的就是私有址址,范围即为192.168.0.0–192.168.255.255,专门为组织机 构内部使用
特点:不易记忆 
 
端口号就是标识正在计算机上运行的进程(程序)
不同的进程有不同的端口号
被规定为一个 16 位的整数 0~65535。

InetAddress

在JDK中提供了一个与IP地址相关的InetAddress类,该类用于封装一个IP地址,并提供了一系列与IP地址相关的方法。
InetAddress类的常用方法

Socket网络编程_第1张图片
ipconfig 获取本机的ip地址
1.127.0.0.1 (电脑自己访问自己)
2.localhost (电脑自己访问自己)
3.192.168.0.106 直接获取到局域网的ip   (让别人访问电脑)

public class Test {
    public static void main(String[] args) throws UnknownHostException {
        //获取给定主机名的的IP地址,host参数表示指定主机
        //InetAddress inetAddress = InetAddress.getByName("127.0.0.1");
        //InetAddress inetAddress = InetAddress.getByName("activate.navicat.com");
        InetAddress inetAddress = InetAddress.getByName("192.168.1.1");
        //获取本地主机地址
        InetAddress localHost = InetAddress.getLocalHost();
        System.out.println(localHost.getHostAddress());
        //获取本地IP地址的主机名
        String hostName = inetAddress.getHostName();
        System.out.println("hostName:"+hostName);
        //获取字符串格式的原始IP地址
        String hostAddress = inetAddress.getHostAddress();
        System.out.println("hostAddress:"+hostAddress);
    }
}

dns域名解析

hostName 就是我们的域名
hostName 主机名称 其实就是 域名
mayikt.com---域名
taobao.com---域名
localhost--域名 dns解析ip地址
为了方便记忆 我们会使用域名在通过dns解析成我们的ip地址。
C:\Windows\System32\drivers\etc 配置我们本地dns域名解析。
www.mayikt.com 配置公网ip地址 刷新到电信运营

案例

修改主机的 hosts 文件   会先访问 host 文件里面的 ip 地址

public class Test {
    public static void main(String[] args) throws UnknownHostException {
        //获取给定主机名的的IP地址,host参数表示指定主机
        //InetAddress inetAddress = InetAddress.getByName("127.0.0.1");
        //InetAddress inetAddress = InetAddress.getByName("activate.navicat.com");
        //InetAddress inetAddress = InetAddress.getByName("192.168.1.1");
        //localhost 主机的名称 127.0.0.1
        InetAddress inetAddress = InetAddress.getByName("www.baidu.com");
        //获取本地主机地址
        InetAddress localHost = InetAddress.getLocalHost();
        System.out.println(localHost.getHostAddress());
        //获取本地IP地址的主机名
        String hostName = inetAddress.getHostName();
        System.out.println("hostName:"+hostName);
        //获取字符串格式的原始IP地址
        String hostAddress = inetAddress.getHostAddress();
        System.out.println("hostAddress:"+hostAddress);
    }
}
 

hosts 文件 里面没有对应的 ip 时,会去电信运营商获取 ip

public class Test {
    public static void main(String[] args) throws UnknownHostException {
        //获取给定主机名的的IP地址,host参数表示指定主机
        //InetAddress inetAddress = InetAddress.getByName("127.0.0.1");
        //InetAddress inetAddress = InetAddress.getByName("activate.navicat.com");
        //InetAddress inetAddress = InetAddress.getByName("192.168.1.1");
        //localhost 主机的名称 127.0.0.1
        InetAddress inetAddress = InetAddress.getByName("www.baidu.com");
        //获取本地主机地址
        InetAddress localHost = InetAddress.getLocalHost();
        System.out.println(localHost.getHostAddress());
        //获取本地IP地址的主机名
        String hostName = inetAddress.getHostName();
        System.out.println("hostName:"+hostName);
        //获取字符串格式的原始IP地址
        String hostAddress = inetAddress.getHostAddress();
        System.out.println("hostAddress:"+hostAddress);
    }
}

 

此时的 ip 就是公网的 ip 可以通过互联网访问 

UDP协议

概念

什么是UDP
UDP协议 为应用程序提供了一种无需建立连接就可以发送封装的 IP 数据报的方法,俗称面向无连接。通俗易懂讲解 UDP协议会把数据打包发送给目标地址, 这个数据包能不能发送给目标地址就不管了,所以我们的udp协议 它是不可靠协议、安全性低,容易丢包 但是速度非常快 无需类似于 tcp协议三次握手。
核心特点:面向无连接、不可靠的协议 、安全系数很低 容易丢包  但是传输速度是非常快 不需要类似于tcp协议三次握手。 
聊天工具 会用到 UDP 协议  

发送数据

发送数据
1.创建发送端socket对象;
2.提供数据,并将数据封装到数据包中;
3.通过socket服务的发送功能,将数据包发出去;
4.释放资源;
在windows 操作系统中,C:\Windows\System32\drivers\etc\HOSTS 文件中 新增 127.0.0.1 test.mayikt.com
 

 
相关代码:
/**
 * @author peiyu
 * @version 1.0
 */
public class UdpClient {
    public static void main(String[] args) throws IOException {
//        1.创建发送端socket对象;
        DatagramSocket datagramSocket = new DatagramSocket();
//        2.提供数据,并将数据封装到数据包中;
        /**
         * 参数1:发送数据 类型 byte 数组
         * 参数2:发送数组的长度
         * 参数3:发送到服务器端 IP地址
         * 参数4:发送到服务器端 端口号码
         */
        byte[] msg = "mayikt".getBytes();
        // 走 DNS 解析获取 ip 地址 127.0.0.1
        InetAddress inetAddress = InetAddress.getByName("test.mayikt.com");
        int port = 8808;
        DatagramPacket datagramPacket = new DatagramPacket(msg, msg.length, inetAddress, port);
//        3.通过socket服务的发送功能,将数据包发出去;
        datagramSocket.send(datagramPacket);
        System.out.println("发送成功");
//        4.释放资源;
        datagramSocket.close();
    }
}
 
接受数据
1.创建接收端socket对象;
2.接收数据;
3.解析数据;
4.输出数据;
5.释放资源;
 
/**
 * @author peiyu
 * @version 1.0
 */
public class UdpServer {
    public static void main(String[] args) throws IOException {
        /**
         * 1.创建接收端socket对象;
         * 2.接收数据;
         * 3.解析数据;
         * 4.输出数据;
         * 5.释放资源;
         */
        //1.创建接收端socket对象
        int port = 8808;
        DatagramSocket datagramSocket = new DatagramSocket(port);
        //2.接收数据;
        byte[] bytes = new byte[1024];
        //数据包的接收形式
        DatagramPacket datagramPacket = new DatagramPacket(bytes,bytes.length);
        //开始监听客户端发送给服务端数据,如果没有监听到数据则一直等待
        System.out.println("开始接收客户端发来的数据");
        datagramSocket.receive(datagramPacket);
        System.out.println("接受到数据");
        //3.解析数据
        byte[] data = datagramPacket.getData();
        String msg = new String(data);
        //4.输出数据
        System.out.println(msg);
        //5.释放资源
        datagramSocket.close();
    }
}

练习题

使用udp协议 客户端可以一直发送数据给服务器端,服务器端可以一直接受到客户端发送的数据。
如果客户端输入 666 就会直接退出程序。
 
客户端
public class UdpClient {
    public static void main(String[] args) throws IOException {
        //1.创建发送端socket对象
        DatagramSocket datagramSocket = new DatagramSocket();
        //2.提供数据,并将数据封装到数据包中;
        /**
         * 参数1:发送数据 类型 byte 数组
         * 参数2:发送数组的长度
         * 参数3:发送到服务器端 IP地址
         * 参数4:发送到服务器端 端口号码
         */
        while (true) {
            System.out.println("客户端:请输入发送数据:");
            Scanner sc = new Scanner(System.in);
            String next = sc.next();

            if ("666".equals(next)){
                System.out.println("发送者退出");
                break;
            }

            byte[] msg = next.getBytes();
            InetAddress inetAddress = InetAddress.getByName("test.mayikt.com");
            int prot = 1234;
            DatagramPacket datagramPacket = new DatagramPacket(msg, msg.length, inetAddress, prot);
            //3.通过socket服务的发送功能,将数据包发出去;
            datagramSocket.send(datagramPacket);
            System.out.println("数据发送成功");
        }
    }
}
 
服务端
public class UdpServer {
    public static void main(String[] args) throws IOException {
        /**
         * 1.创建接收端socket对象;
         * 2.接收数据;
         * 3.解析数据;
         * 4.输出数据;
         * 5.释放资源;
         */
        int cout = 0;
        int port = 1234;
        DatagramSocket datagramSocket = new DatagramSocket(port);
        while (true) {
            if (cout == 1000) {
                break;
            }
            byte[] bytes = new byte[1024];
            DatagramPacket datagramPacket = new DatagramPacket(bytes,bytes.length);

            datagramSocket.receive(datagramPacket);

            byte[] data = datagramPacket.getData();
            String msg = new String(data);

            System.out.println("服务器端接收到客户端发送的数据是::"+msg);

            cout++;
        }
        datagramSocket.close();
    }
}

TCP协议 

概述

TCP是面向连接的可靠协议、通过三次握手建立连接,通讯完成时拆除连接
UDP是面向无连接通讯协议,udp通讯时不需要接受方确定,属于不可靠传输,可能会存在丢包的现象。
 
tcp协议 需要先经历三次握手成功之后 在将数据发送给服务器端 确保服务器端是在 在将数据
发送给服务器端。三次握手、四次挥手。
udp协议直接将数据发送给服务器端-----传输效率高 缺陷 没有验证服务器端
是否存在 如果不在的情况下 直接传输数据 可能丢失数据
数据连接池 目的tcp协议连接复用
jdbc
 
首先我们要知道在tcp建立连接中,有一些名词表示:
比如:syn就是建立连接、ack就是确认标志、fin终止标志
 
第一次握手:客户端会向服务器端发送码为syn=1,随机产生一个seq_number=x的数据包到服务器端 (syn)
第二次握手:服务端接受到客户端请求之后,确认ack=x+1, 于是就向客户端发送syn(服务端独立生成 随机生成数字Y)+ack
第三次握手:客户端接受syn+ack,向服务器端发送ack=y+1,此包发送完毕即可 建立tcp连接。
 
白话文翻译:
第一次握手:客户端向服务器端发送 问服务器你在不在?
第二次握手:服务器端回应客户端说:我在的。
第三次握手:客户端发送给服务器端:ok,那我开始建立连接的
 
 
关闭连接:
第一次挥手: 客户端向服务器端发送释放的报文,停止发送数据fin=1、生成一个序列号seq=u;
第二次挥手: 服务器端接受到释放的报文后,发送ack=u+1;随机生成的seq=v给客户端;当前状态为关闭等待状态
 
客户端收到了服务器确认通知之后,此时客户端就会进入到终止状态,等待服务器端发送释放报文。
第三次挥手:服务器端最后数据发送完毕之后,就向客户端发送连接释放报文,FIN=1,ack=u+1 当前为半关闭状态,随机生成一个随机树w
 
第四次挥手,客户端必须发出确认,ACK=1,ack=w+1,而自己的序列号是seq=u+1,此时,客户端就进入了TIME-WAIT(时间等待)状态。注意此时TCP连接还没有释放,必须经过2∗∗MSL(最长报文段寿命)的时间后,当客户端撤销相应的TCB后,才进入CLOSED状态。
 
服务器只要收到了客户端发出的确认,立即进入CLOSED状态。同样,撤销TCB后,就结束了这次的TCP连接。可以看到,服务器结束TCP连接的时间要比客户端早一些。
 
白话文翻译四次挥手:
 
第一次挥手  客户端向服务端发送一个释放连接通知;
第二次挥手   服务端接受到释放通知之后,告诉给客户端说等待一下,因为可能存在有其他的数据没有发送完毕,等待数据全部传输完毕之后就开始 关闭连接;
第三次挥手   服务器端所有的数据发送完毕之后,就告诉客户端说现在可以释放连接了。
第四次挥手: 客户端确认是最终释放连接通知,ok 就开始 就向服务区端发送我们可以开始关闭连接啦;

发送数据

客户端
1.创建发送端Socket对象(创建连接)
2.获取输出流对象;
3.发送数据;
4.释放资源;
public class TcpClient {
    public static void main(String[] args) throws IOException {
        /**
         * 1.创建发送端Socket对象(创建连接) 进行三次握手 判断服务器是否存在
         * 2.获取输出流对象;
         * 3.发送数据;
         * 4.释放资源;
         */
        //1.创建发送端Socket对象(创建连接)
        Socket socket = new Socket(InetAddress.getByName("test.mayikt.com"),8080);
        //2.获取输出流对象;
        OutputStream outputStream = socket.getOutputStream();
        //3.发送数据;
        outputStream.write("mayikt.tcp".getBytes());
        //4.释放资源;
        outputStream.close();
        socket.close();
    }
}

服务器端
public class TcpServer {
    public static void main(String[] args) throws IOException {
        /**
         * 1.创建接收端Socket对象;
         * 2.监听(阻塞:如果建立连接失败,程序会一直阻塞,不往下执行);
         * 3.获取输入流对象;
         * 4.获取数据;
         * 5.输出数据;
         * 6.释放资源;
         */
        //1.创建接收端Socket对象;
        ServerSocket serverSocket = new ServerSocket(8080);
        //2.监听(阻塞:如果建立连接失败,程序会一直阻塞,不往下执行);
        System.out.println("服务端开始接收数据");
        Socket socket = serverSocket.accept();
        //3.获取输入流对象;
        InputStream inputStream = socket.getInputStream();
        //4.获取数据;
        byte[] msg = new byte[1024];
        int len = inputStream.read(msg);  //read(数组):len就是读取的长度 数组中会有内容     read():获取的就是读取的内容      等于 -1 读取接收
        //5.输出数据;
        System.out.println("客户端发送的数据:"+new String(msg,0,len));
        //6.释放资源;
        serverSocket.close();
        socket.close();
        inputStream.close();
    }
}

练习1

Socket网络编程_第2张图片

使用tcp协议 客户端可以一直发送数据给服务器端,服务器端可以一直接受到客户端发送的数据。
如果客户端输入 666 就会直接退出程序。


客户端
public class TcpClient {
    public static void main(String[] args) throws IOException {
        while (true) {
            //创建  socket
            Socket socket = new Socket("127.0.0.1",8080);
            System.out.println("客户端:输入要发送的数据内容");
            Scanner sc = new Scanner(System.in);
            String line = sc.nextLine();
            if ("666".equals(line)){
                System.out.println("退出程序");
                return; //退出程序
            }
            //获取 outputStream  写入数据
            OutputStream outputStream = socket.getOutputStream();
            //写入数据给服务端
            outputStream.write(line.getBytes());
            //接收服务端发来的数据
            InputStream inputStream = socket.getInputStream();
            //接收数据
            byte[] bytes = new byte[1024];
            int len = inputStream.read(bytes);
            System.out.println("服务端响应客户端的数据:"+new String(bytes,0,len));
            //释放资源
            outputStream.close();
            inputStream.close();
        }
    }
}
 
 
服务端 
public class TcpServer {
    public static void main(String[] args) throws IOException {
        // 创建 serverSocket
        ServerSocket serverSocket = new ServerSocket(8080);
        System.out.println("服务器启动");
        while (true){
            // 接收客户端数据  当没有接收到数据时阻塞  直到有数据发过来
            Socket socket = serverSocket.accept();
            //接收数据
            InputStream inputStream = socket.getInputStream();
            byte[] bytes = new byte[1024];
            int len = inputStream.read(bytes);
            System.out.println("服务器端接受客户端:"+new String(bytes,0,len));
            //服务器端给客户端传输数据
            OutputStream outputStream = socket.getOutputStream();
            String uuid = "我收到了:"+UUID.randomUUID().toString();
            outputStream.write(uuid.getBytes());
            //释放资源
            inputStream.close();
            outputStream.close();
            socket.close();
        }
    }
}
 
 
服务端改造(改造成多线程方式) 
public class TcpServer {
    public static void main(String[] args) throws IOException {
        // 创建 serverSocket
        ServerSocket serverSocket = new ServerSocket(8080);
        System.out.println("服务器启动");
        while (true){
            // 接收客户端数据  当没有接收到数据时阻塞  直到有数据发过来
            Socket socket = serverSocket.accept();

            //创建一个线程  当多个客户端并发时 不会出现不能执行的现象
            new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        //接收数据
                        InputStream inputStream = socket.getInputStream();
                        byte[] bytes = new byte[1024];
                        int len = inputStream.read(bytes);
                        System.out.println("服务器端接受客户端:"+new String(bytes,0,len));
                        //服务器端给客户端传输数据
                        OutputStream outputStream = socket.getOutputStream();
                        String uuid = "我收到了:"+UUID.randomUUID().toString();
                        outputStream.write(uuid.getBytes());
                        //释放资源
                        inputStream.close();
                        outputStream.close();
                        socket.close();
                    }catch (Exception e){

                    }
                }
            }).start();
        }
    }
}

练习2实现登录

Socket网络编程_第3张图片

客户端
public class TcpClient {
    public static void main(String[] args) throws IOException {
        while (true) {
            Scanner sc = new Scanner(System.in);
            System.out.println("请输入用户名:");
            String userName = sc.nextLine();
            System.out.println("请输入密码:");
            String userPwd = sc.nextLine();
            Socket socket = new Socket("127.0.0.1", 8080);

            String text = "userName=" + userName+ "&userPwd="  +userPwd;
            OutputStream outputStream = socket.getOutputStream();
            outputStream.write(text.getBytes());

            InputStream inputStream = socket.getInputStream();
            byte[] bytes = new byte[1024];
            int len = inputStream.read(bytes);
            String resp = new String(bytes, 0, len);
            if ("ok".equals(resp)) {
                System.out.println("登陆成功");
            } else {
                System.out.println("登陆失败");
            }
            //关闭资源
            inputStream.close();
            outputStream.close();
            socket.close();
        }
    }
}
 
服务端
public class TcpServer {
    public static void main(String[] args) throws IOException {
        ServerSocket serverSocket = new ServerSocket(8080);
        System.out.println("服务器端启动成功...");
        while (true) {
            Socket socket = serverSocket.accept();
            
            InputStream inputStream = socket.getInputStream();
            byte[] bytes = new byte[1024];
            int len = inputStream.read(bytes);
            String text = new String(bytes, 0, len);
            String userName = text.split("&")[0].split("=")[1];
            String userPwd = text.split("&")[1].split("=")[1];

            OutputStream outputStream = socket.getOutputStream();
            if ("mayikt".equals(userName) && "123456".equals(userPwd)){
                outputStream.write("ok".getBytes());
            } else {
                outputStream.write("fails".getBytes());
            }

            //关闭流
            socket.close();
            inputStream.close();
            outputStream.close();
        }
    }
}

练习3

手写http
 
服务端
public class HttpTcpServer {
    public static void main(String[] args) throws IOException {
        // 服务器端可以一直接受 客户端数据
        // 创建监听端口号码 ServerSocket
        //监听端口是 80 htt协议底层基于 tcp 封装  默认端口是 80 在浏览器那不用写
        ServerSocket serverSocket = new ServerSocket(80);
        System.out.println("服务器启动");
        while (true) {
            // 监听客户端发送过来的数据注意  我们的客户端没有发送数据给服务器端 该方法就会在这里一直阻塞
            Socket socket = serverSocket.accept();
            //不允许单独new线程 线程池来维护线程----java进阶
            new Thread(new Runnable() {
                OutputStream outputStream = null;
                FileInputStream fileInputStream = null;
                @Override
                public void run() {
                    try {
                        //获取域名上的值
                        InputStream inputStream = socket.getInputStream();
                        byte[] bytess = new byte[1024];
                        int len1 = inputStream.read(bytess);
                        //获取连接
                        String reqUrl = new String(bytess,0,len1);
                        //http://127.0.0.1/login.html  获取/login.html
                        String url = reqUrl.split("\r\n")[0].split(" ")[1];
                        // 浏览器 ——> 服务器端(tcpServer) 直接将静态资源给客户端
                       outputStream = socket.getOutputStream();
                       //客户端读取本地资源 到内存中
                        File file = new File("C:\\609" + url);
                        fileInputStream = new FileInputStream(file);
                        byte[] bytes = new byte[1024];
                        int len = fileInputStream.read(bytes);
                        outputStream.write(bytes,0,len);
                    } catch (Exception e) {
                        try {
                            outputStream.write("500".getBytes());
                        } catch (IOException ex) {
                            throw new RuntimeException(ex);
                        }
                    } finally {
                        try {
                            if (socket != null) {
                                socket.close();
                            }
                            if (outputStream != null) {
                                outputStream.close();
                            }
                            if (fileInputStream != null){
                                fileInputStream.close();
                            }
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                }
            }).start();
        }
    }
}

你可能感兴趣的:(网络,java,服务器)