Fork me on GitHub

《网络编程》

网络编程》

作者:冯皓林

完稿:2016.1.2x--2016.2.X

WHAT?什么是网络编程?

网络编程解决的是计算机之间的数据传输问题。

 

区别:

网页编程:基于html页面(网页)客户端与服务端进行数据传输。(servlethtmlcss等)

 

计算机网络:计算机网络就是指分布在不同地域的计算机通过外部设备连接起来,以达到了互相通讯或者是资源共享的功能。

 

网络模型:

 

OSI(Open System Interconnection开放系统互连)参考模型:

 

 

 

TCP/IP参考模型(沙漏模型)

 

 

为什么会有以上两个模型?

这就得引入网络分层的概念

网络中涵盖的内容很多,为了便于理解,也为了方便建设,怎么办呢?我们就把他们划分成不同的层次,每层都有自己独立的功能,哪层出问题了,就到对应的层去找并解决问题。

 

那么网络分层是怎样的呢?国际化标准组织就将网络分层定成模型,来体现网络分层的这种概念。把网络分层搭成一个标准模型,无论是什么网络都以基于该层模型,或以该层模型为主。

 

为何TCP/IP协议栈设计成沙漏型的?(可暂时跳过不看)

趋势:通信网的退化

“网络在退化,我指的是IP网络在退化,所有的逻辑全部在纵向上挤向两端,上端管协议逻辑,下端管实际传输;在横向上挤向中心,所有的控制平面逻辑正在趋向于中心化。因此IP就退化了,最终只剩下一个连接器意义的东西,没有它不行,因此,它就是精化。鉴于此意义,我并不看好IPv6,虽然它解决了IP地址不够用的问题。”【1

 

TCP/IP协议背后的内涵?(可暂时跳过不看)

[我又要装逼了:“规律使我们开阔,历史使我们更加深刻。”古语有道是:“读史使人明智。”

确实是如此的,想要了解一个事件或者一种技术的深度内涵,一定要用历史的眼光来看待!!

所以为什么要多不同学科的读书?因为不同学科的书带给你的是不同的视角去观察和分析问题。所有的事物都是稍众即逝的,要想彻底把握它,必须要了解它的前世今生,如果你不了解太阳巨石文化和罗马帝国和美索不达米亚,你就很难理解今天西方国家的政策和行为;如果你理解了商周文化和先秦地缘政治学,你就会知道,其实汉字并非人们想象的那么博大精深!对待技术也一样,了解技术历史虽然不能让你写出规范的代码,但它可以给你思想上的启迪,给你灵感,让你深入理解技术内幕,甚至你能基于思想创造出新的东西,否则,你掌握的仅仅是规律,换种说法就是,你只记住了公式,更确切来说的话,你掌握的仅仅是一门匠艺,你可能富有工匠精神,写负责任的代码,优雅地实现功能,但最终你会被层出不穷的新的匠艺所淹没。]-->这段是有毒的鸡汤,爱看不看吧,我有时候,有点感悟就喜欢瞎叨叨。

 

正文:

Tcp/Ip的进化历史:

1、原始的样子:

 

图解终端A与终端BTCP/IP 进行传输控制、分组交换通讯。

为什么会有端到端的传输?

思想源头之一是为了减轻中间节点实现传输控制的复杂性。

 

TCP/IPTCP over IP):叫这个名字,很明显,TCPIP相互独立成层。

事件(IP的分离):IP协议从TCP协议中分离出来,成了TCP/IP

 

问:什么是IP

答:ip仅仅是一个报文分组交换的服用层,为了提供IP尽力而为的语义。

 

问:为什么分离?

答:设计哲学:

报文交换角度:

报文交换,IP报文交换是核心中的核心。

它的职责:

            1分组交换(负责传输报文,这也是分组交换的核心思想。)

            2传输控制(服务类别相关的控制功能,比如丢包重传,按序交付等等。)

 

结论:如果IP实现了丢包重传按序交付,那么那些不需要该功能的服务的效率会被拖累,也正是因为这样,IP才从TCP中分离开!

 

            推论1:现在的TCP把包交换的功能分给IP了,自己仅仅留下了一个控制功能,履行端到端的传输控制。

            端到端的传输控制 (IPTCP中分离出来后的样子,如图所示) 

            2、进化.... ....

  

            推论2ip可以支持无丢包重传、按序交付服务。看图。

 

              3、再次进化... ...

 

                   

 

于是,分层模型就出来了,这直接影响了后面几十年!

               推论3应用层的高度的可扩展性,分层模型之后,百花齐放的时代到了,既然TCPUDP都能复用IP,那么TCP之上,UDP之上的多种服用同样也能复用TCPUDP

              4、再次进化... ...

 

启示:应用程序之所以可以爆炸式增长,得益于端到端的传输控制以及分层模型。

如果没有端到端的控制,那么每实现或者优化一种传输控制功能,都需要修改中间节点的实现,而想让节点的实现者们为了支持一种协议达成共识是不可能的。如果没有分层模型,中间节点就会显得不像现在这么标准化(封闭),任何的应用创新都可能波及到中间节点,以至于应用创新几乎不可能!

 

 

              5、究极进化... ...

      最终TCP/IPIP完胜,鉴于以往的投资以及上述广域网技术的成熟,它们统统退化成了一种链路层技术,这个退化直接得益于分层模式的递归特性,该特性允许逻辑意义的任意封装和再封装!

 

 

 

 

:分组交换也称为包交换,它将用户通信的数据划分成多个更小的等长数据段,在每个数据段的前面加上必要的控制信息作为数据段的首部,每个带有首部的数据段就构成了一个分组。首部指明了该分组发送的地址,当交换机收到分组之后,将根据首部中的地址信息将分组转发到目的地,这个过程就是分组交换。能够进行分组交换的通信网被称为分组交换网。

 

TCP真的很复杂吗?

     总有人说TCP很复杂,文档太多且杂,然而其核心却是十分简单的!所谓的那些复杂的东西都是一些可插拔的针对特定场景的优化!最原始的TCP是没有延迟确认和捎带确认的,而引入这些机制是为了优化网络行为和提高吞吐量,可是却影响了延迟,为了干掉糊涂窗口,又引入很多优化,正是这些让协议变得复杂起来,如果读一下理论方面的书,单帧的停等协议就是TCP协议的核心!

 

网络通讯的三要素:

1.IP地址,计算机在互联网上的唯一标识。

2.端口:用于标识进程的逻辑地址,不同进程的标识。其实可以理解为就是一个数字的标识(用于标识软件)。像什么?门牌号。

3.协议。规范了数据发送的格式。

 

 

 

WHEN?

WHY?

进阶篇《关于socket的设计》将会讲解。

HOW?

基础篇:ip、端口、协议

Ip地址:

其实ip地址的本质是由32个二进制数据组成的,为了方便我们的记忆,所以就把32个二进制数据分成四段,每段8个。每段有28次方种可能,即可以表示0-255之间的数。

 

00000000-00000000-00000000-00000000

[---------------网络号----------------][--主机号--]

 

子网掩码:用于标识网络号主机号

 

IP地址的类别:

AIP地址:一个网络号+三个主机号 (可以配224次方台主机) 政府使用

BIP地址:两个网络号+两个主机号 (可以配216次方台主机) 事业单位、国企

CIP地址:三个网络号+一个主机号 (可以配28次方台主机)  我们老百姓使用。。。

 

InetAdress类:

Java是面向对象的语言,任何事物都可以用一个类来表示,所以java使用了一个类描述了IP

首先,来学习一个IP地址类,java有关网络编程的API都放在了java.net的包里。

 

查阅文档发现:

 

 

 

 

 

InetAddress类中几个常用的方法:

getLocalHost()      返回一个IP地址对象

getHostName()       返回此IP地址的主机名

getHostAddress()    返回 IP 地址字符串(以文本表现形式)。

getByName(String host) 在给定主机名、字符串的IP地址、域名的情况下,创建IP地址对象。

getAllByName(String host)  在给定主机名的情况下,根据系统上配置的名称服务返回其 IP 地址所组成的数组。

 

好,先看看getLocalHost()方法的文档描述:

 

看完后,咱应该干嘛?写DEMO啊,你那么呆萌,不写谁知道啊?好,写呗~~

 

public class Demo1

{

public static void main(String[] args) throws UnknownHostException

    {

    InetAddress ip = InetAddress.getLocalHost();

    System.out.println("主机名: " + ip.getHostName());

    System.out.println("ip号: " + ip.getHostAddress());

    }

}

 

 

那么问题来了?我现在拿到的只是我本机的InetAddress对象,如果我要跟其他的主机通讯的话,很明显,我必须拿到的是其他主机的InetAddress对象,那该怎么拿呢?

 

先看文档吧:

 

看完文档后,我们很容易想到,如果我知道计算机网络上任意一台主机的名称,我就能创建出代表这台主机的IP地址对象,SO~~再来个DEMO~

public class Demo1

{

public static void main(String[] args) throws UnknownHostException

    {

    // InetAddress ip = InetAddress.getLocalHost();

       // 给定域名,创建IP地址对象

InetAddress ip = InetAddress.getByName("www.baidu.com");

       // 给定主机名和IP地址字符串创建IP地址对象,请自行完成

    System.out.println("主机名: " + ip.getHostName());

    System.out.println("ip号: " + ip.getHostAddress());

    }

}

 

 

为什么写域名可以找到主机呢?只发图不说话:

 

 

 

端口:

IP使用了一个类来描述,那么端口是不是也要用一个类来描述呢?不,我们知道端口只是一个数字标识,所以没必要专门用一个类来描述。

端口的范围:0--65535

是不是所有的端口都可以使用呢?

不是,因为只要涉及到通讯的软件,都要使用到端口,而windows本身就需要进行检查更新什么的,所以本身就占用了一些端口。

 

 

公认端口(windows系统已经使用的端口):0--1023

比如:

http协议:80

ftp协议:21

注册端口:1024--49152它们松散地绑定了一些服务。

动态或私有的端口(我们可以使用的端口):49162--65535

 

使用建议:1024--65535  

如果出现占用的情况就换一个咯~~~

协议(UDP&TCP):

数据在传输过程中,经过外部设备(分线器、路由器、集线器等),需要按协议经过加帧处理,帧头加一些地址信息(标记发送到哪里),帧尾加数据的话(一般用户加密,比如循环冗余校验码checksum),数据包继续经过网线传输到下一个外部设备(分线器、路由器、集线器等),到达该外部设备的话,就要进行解包,即按协议进行解包。

 

UDP(用户数据报协议)

1、将数据极其源和目的封装为数据包,不需要建立连接(不需要确认通讯的对象是否在线)。类似:快递(发货地址,收货地址)

2、每个数据包大小限制在64K

3、因为无连接,所以不可靠(有可能出现数据包丢失)

4、因为不需要建立连接,所以速度快

5UDP的通讯是不分客户端和服务端的,只分发送端和接收端。

如:人说话、飞秋、凌波、CS

 

例如:物管的对讲机、视频会议、游戏

 

讲究的是效率,实效性,而不是数据的完整性。

 

传输方式:【封装成多个数据包传输,到了目标主机,再重新组装

TCP(传输控制协议)

1、面向连接,有特有的通道

2、在连接中传输大数据量

3、通过三次握手机制连接,可靠协议

4、通信前必须建立连接,效率稍低

 

如:打电话,文件的传送.

 

传输方式:【建立数据通道,通道内进行数据传输。

 

Socket:

DatagramSocketDatagramPacket

1、Sokcet就是为网络服务提供的一种机制。

2、通信的两端都有Socket

3、网络通信其实就是Socket间的通讯。

4、数据在Socket间通过IO传输。

 

网络服务的端点,就好比两个港口。

 

java中,网络通讯又称作为socket(套接字,插座)通讯,通讯的两端都必须安装上socket对象,不同的协议会产生不同的socket对象(就好比我们生活中的插座,有两孔插和三孔插)。

 

Socket(插座),其实,我觉得命名命得挺好的。只发图不说话。

 

 

UDP协议的通讯插座:

开发步骤:

     1、建立发送端和接收端。

     2、建立数据包。

     3、调用socket的发送和接收的方法。

     4、关闭socket

 

注意: 发送端和接收端是两个独立的运行的程序。

先看文档:

 

 

 

Udp(用户数据报协议)通讯用到的两个类:

类 DatagramSocket    UDP协议的服务类

类 DatagramPacket    数据包类

 

 

牛刀小试一下?

 

数据报套接字要使用的构造方法:

 

DatagramSocket() 

 

 

 

数据包要使用的构造方法:

DatagramPacket(byte[] buf, int offset, int length, InetAddress address, int port)

参数说明:

buf:要发送数据的字节数组。

length:发送的数据长度。以字节为单位

address:发送的ip地址。

port:端口号。

 

发送端:

public class SenderDemo1

{

public static void main(String[] args) throws Exception

    {

    // 1、建立UDP服务(先建立码头)

DatagramSocket datagramSocket = new DatagramSocket();

// 2、准备数据,封装成数据包(准备货物,装进集装箱)

String data = "Hello World!!";

       // 参数说明:你要发送的数据是什么?发送数据的长度是多少?你要发到哪去?你要发给谁啊?

DatagramPacket packet = new DatagramPacket(data.getBytes(),data.getBytes().length,InetAddress.getLocalHost(),9090);

// 3、发送(运送集装箱)

datagramSocket.send(packet);

// 4、释放端口

datagramSocket.close();

    }

}

 

 

接收端:

public class ReceiveDemo1

{

/**

 * 建立UDP接收端的思路:

 * 1、建立UDP socket服务因为要接收数据,必须明确一个端口号。

 * 2、创建数据包,用于存储收到的数据。方便用数据包对象的方法解析这些数据。

 * 3、使用socket服务的receive方法将接收的数据存储到数据包中。

 * 4、通过数据包的方法解析数据包中的数据。

 * 5、关闭资源。

 * @param args

 * @throws Exception

 */

public static void main(String[] args) throws Exception

    {

System.out.println("接收端启动.......");

// 1、建立UDP接收端,并且要监听一个端口

DatagramSocket datagramSocket = new DatagramSocket(9090);

// 2、准备一个空的数据包,准备接收数据

byte[] buf = new byte[1024];

DatagramPacket packet = new DatagramPacket(buf,buf.length);// 数据包本身不具备存储的能力,是借用了buf字节数组进行存储的

System.out.println("receive方法之前...");

// 3、调用udp的服务,接收数据包

datagramSocket.receive(packet);// 接收发送过来的数据包,内容已经存储到字节数组中。

// receive方法是一个阻塞型的方法,如果没有接收到数据包,会一直等待下去。

// 4、通过数据包对象的方法,解析其中的数据,比如:地址、端口、数据内容等

String ip = packet.getAddress().getHostAddress();

int port = packet.getPort();

String text = new String(packet.getData(),0,packet.getLength());

System.out.println(" ip: " + ip + " port: " + port + " text: " + text);

System.out.println("接收端接收到的数据:" + new String(buf,0,packet.getLength()));// packet.getLength() 获取数据包本次接收的字节个数

//System.out.println("接收端接收到的数据:" + new String(buf));

System.out.println("receive方法之后...");

// 5、关闭资源(释放端口)

datagramSocket.close();

    }

 

}

为什么要这样写?

byte[] buf = new byte[1024];

DatagramPacket packet = new DatagramPacket(buf,buf.length);

 

因为本身数据包不具备数据存储能力,所以借用字节数组要实现数据存储的能力。所以真正的数据存储应该是存储在字节数组里面去的。另外,一般传入一个数组的作用也是为了起到缓冲的作用,提高数据传输的效率。

 

注意:先运行接收端,再运行发送端。

案例:

下面我们写一个例子给FeiQ发送消息:

 

*凡是网络编程软件都要对数据进行加密,这样子做的目的是考虑到了安全性问题。

 * 而加密的方式是最简单的就是接收指定格式的字符串。如果接收到的数据不符合所要

 * 求的格式,那么该数据就会直接丢弃,不处理。

 * 飞Q要求接收的数据格式:

 *     version:time:sender:ip:flag:content

 *     version:版本。1.0

 *     time:时间。

 *     sender:发送者。

 *     ip:发送者的IP地址。

 *     flag:发送的标识,32(标识你要聊天)

 *     content:才是你真正希望要发送的内容。

 

public class FQDemo

{

public static void main(String[] args) throws Exception

    {

    // 1、建立UDP服务

DatagramSocket datagramSocket = new DatagramSocket();

// 2、准备要发送的数据,封装成数据包

String data = getData("Hello FQ!");

byte[] buf = data.getBytes();

       // 169.254.147.26 为我feiQ的IP地址

DatagramPacket packet = new DatagramPacket(buf, buf.length, InetAddress.getByName("169.254.147.26"), 2425);

// 3、发送数据

datagramSocket.send(packet);

// 4、释放资源(释放端口)

datagramSocket.close();

    }

public static String getData(String content)

{

StringBuilder sb = new StringBuilder();

sb.append("1.0:")

  .append(System.currentTimeMillis()+":")

  .append("Dante:")

  .append("192.168.1.1:")

  .append("32:")

  .append(content);

return sb.toString();

}

}

 

 

上面的例子,只是实现了一对一的消息发送,那如果我们要想群发消息,该怎么做呢?

UDP协议中有个IP地址称为广播IP地址,给广播IP地址发送消息,在同一个网段的人都可以接收到。

 

广播IP地址的格式:网络号+255  主机号为255IP地址就是一个广播IP地址

 

Udp群聊:

需求分析:

 

/**

 * 群聊的发送端

 * @author Dante Fung

 *

 */

public class ChatSender extends Thread

{

@Override

public void run()

{

try

        {

// 1、建立UDP服务

        DatagramSocket socket = new DatagramSocket();

        // 2、准备要发送的数据,把数据封装到数据包中

        String data = null;

        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

        // 准备数据包

        DatagramPacket p = null;

        while((data=br.readLine()) != null)

        {

         System.out.println(data);

         // 把数据封装到数据包中,群发,用广播IP地址

         p = new DatagramPacket(data.getBytes(),data.getBytes().length,InetAddress.getByName("10.10.20.255"),9090);

         socket.send(p);

        }

        // 关闭资源

        socket.close();

        }

        catch (Exception e)

        {

        e.printStackTrace();

        }

}

}

 

/**

 * 群聊的接收端

 * @author Dante Fung

 *

 */

public class ChatReceive extends Thread

{

@Override

public void run()

{

try

        {

// 1、建立UDP服务

        DatagramSocket socket = new DatagramSocket(9090);

        // 2、准备一个空的数据包,接收数据

        byte[] buf = new byte[1024];

        DatagramPacket packet = new DatagramPacket(buf,buf.length);

        // 3、不断的接收数据

        boolean flag = true;

        while(flag)

        {

         socket.receive(packet);

         // 获取对方的ip地址对象

         InetAddress ip = packet.getAddress();

         System.out.println(ip.getHostAddress() + "说:" + new String(buf,0,packet.getLength()));

        }

        // 4、关闭资源(释放端口)

        socket.close();

        }

        catch (Exception e)

        {

        e.printStackTrace();

        }

}

}

 

public class ChatMain

{

public static void main(String[] args) throws Exception

    {

    // 创建线程对象

ChatSender sender = new ChatSender();

ChatReceive receive = new ChatReceive();

// 启动线程

sender.start();

receive.start();

    }

}

 

 

UDP协议数据包丢失:

什么情况下数据包才会丢失:

1. 带宽不足的情况

2. cpu的处理不足的时候也会丢失。

 

 

TCP协议的通讯插座:

开发步骤:

SocketServerSocket

1、建立客户端和服务端。

2、建立连接后,通过Socket中的IO进行数据传输。

3、关闭Socket

同样,客户端与服务端是两个独立的应用程序。

 

 

使用UDP协议通讯的时候,数据包有可能会出现丢失。有些数据是一个字节都不允许丢失的,比如说:软件的安装包。为了保证数据的完整性,那么建议使用tcp协议进行通讯。

 

UDP协议的特点:

1、要把数据封装到数据包中才能发送,面向无连接的。

2、每个数据包的大小不超过64Kb

3、因为UDP是面向无连接,所以不可靠(所以有可能会出现数据丢包)。

4、因为是面向无连接的,所以效率高。

5udp是不分客户端与服务端的,只分发送端与接收端。

 

TCP协议的特点:

1tcp协议是基于IO流进行数据传输的,面向连接。TCP的客户端一旦启动,马上就要建立连接。

2、传输的数据是没有大小限制的。

3tcp是面向连接的,通讯之前tcp是通过三次握手机制,保证数据的完整性。

4、面向连接的,效率稍低。

5tcp是分客户端和服务端的。

 

什么是三次握手机制?

   还是那句话,不同的协议,产生不同的Socket。现在来学习一下TCP协议的Socket

先看文档:

客户端类:

 

服务端类:

 

 

服务端:

 

/**

 * Tcp服务端

 * @author Dante Fung

 *

 */

public class Demo2Server

{

/**

 * 建立服务端的的思路:

 * 1、创建服务端socket对象。通过serverSocket对象。

 * 2、服务端必须对外提供一个端口,否则客户端无法连接。(主机上的服务端程序,那么端口就是用于标识这服务端程序的进程)

 * 3、获取连接过来的客户端对象。

 * 4、通过客户端对象获取socket流读取客户端发来的数据,并打印到控制台上。

 * 5、关闭资源。

 * @param args

 * @throws Exception

 */

public static void main(String[] args) throws Exception

    {

    // 1、建立tcp服务,而且要监听一个端口。

ServerSocket serverSocket = new ServerSocket(9090);

// 2、接收客户端的请求连接

/**

 * 按常理来说,tcp是基于IO流进行数据传输的,第二步应该是获取对应输入流对象,读取客户端输送的数据。

 * 非常遗憾,serverSocket没有getInputStream()这个方法,但是!

 * 谁有呢?socket有。那socket有跟serverSocket有什么关系呢?

 * 有,serverSocket可以产生sokcet

 */

System.out.println("====accept之前===");

Socket socket = serverSocket.accept();// 是一个阻塞型的方法,如果没有客户端与其连接,那么会一直等待下去。

//3、通过socket对象获取输入流,读取客户端发来的数据

InputStream in = socket.getInputStream();

byte[] buf = new byte[1024];

int length = in.read(buf);

System.out.println("tcp的服务端接收到的数据:" + new String(buf,0,length));

System.out.println("====accept之后===");

// 服务端要反馈数据给客户端:使用客户端socket对象的输出流给客户端返回数据。

OutputStream outputStream = socket.getOutputStream();

outputStream.write("客户端您辛苦了!!".getBytes());

// 4、关闭资源

socket.close();

// 正常情况下,做成服务器的话,一般是不关闭的。

//serverSocket.close();

    }

}

客户端:

/**

 * Tcp客户端

 * @author Dante Fung

 *

 */

public class Demo1Client

{

   /**

 * 客户端发送数据到服务端

 * 

 * Tcp传输:客户端建立过程

 * 1、创建tcp客户端socket服务,使用的是socket对象。建议该对象一创建就要明确目的地,即要连接的主机。

 * 2、如果建立连接成功,说明数据传输的通道已经建立。

 *     该通道就是socket流,是低层建立好的。既然是流,说明这里有输入,又有输出。

 *     想要输入或者输出流对象,可以找socket来获取。

 *     可以通过getOutputStream()、getInputStream()来获取两个字节流对象。

 * 3、使用输出流,将数据写出。

 * 4、关闭资源。

 * @param args

 * @throws Exception

 */

public static void main(String[] args) throws Exception

    {

    // 1、建立TCP服务,TCP的客户端一旦启动,马上就要建立连接。

Socket socket = new Socket(InetAddress.getLocalHost(),9090);

// 2、获取对应的流对象,因为TCP是基于IO流进行数据传输的。

String data = "这是我的第一个TCP例子!!";

OutputStream outputStream = socket.getOutputStream();

// 3、把数据写出

outputStream.write(data.getBytes());

// 读取服务端反馈的数据

InputStream inputStream = socket.getInputStream();

byte[] buf = new byte[1024];

int len = inputStream.read(buf);

System.out.println("tcp的服务器收到的数据:" + new String(buf,0,len));

// 4、关闭资源

socket.close();

    }

}

服务端的控制台:

 

客户端的控制台:

 

 

问题:为什么ServerSocket没有设置获取流对象的方法呢?

 

 

 

下面继续写多一个例子:

 

Tcp传输文件服务端

需求:

      1服务器端接收多个客户端的连接

      2、一旦建立连接,马上给客户端发送图片

 

 

单线程版本的代码:

public class ImageServer extends Thread

{

@Override

public void run()

{

try

        {

        // 1、建立tcp服务端

ServerSocket serverSocket = new ServerSocket(9090);

// 2、接收客户端的请求连接

Socket socket = serverSocket.accept();

// 3、给客户端发送图片数据

// 3.1、获取输入流对象

BufferedInputStream bis = new BufferedInputStream(new FileInputStream("c:aa.jpg"));

// 3.2、获取输出流对象

OutputStream out = socket.getOutputStream();

// 3.3、回写图片数据

byte[] buf = new byte[1024];

int len = 0;

while((len = bis.read(buf)) != -1)

{

out.write(buf, 0, len);

}

// 4、释放资源

bis.close();

socket.close();

        }

        catch (Exception e)

        {

         e.printStackTrace();

         throw new RuntimeException(e);

        }

}

}

改造为多线程后的代码:

// 改造为多线程版本的代码

public class ImageServer extends Thread

{

private Socket socket;

// 用于存储ip地址

private HashSet<String> ips = new HashSet<String>();

public ImageServer(Socket socket)

    {

this.socket = socket;

    }

@Override

public void run()

{

try

{

// 3、给客户端发送图片数据

// 3.1、获取输入流对象

BufferedInputStream bis = new BufferedInputStream(new FileInputStream("c:/aa.jpg"));

// 3.2、获取输出流对象

OutputStream out = socket.getOutputStream();

// 3.3、回写图片数据

byte[] buf = new byte[1024];

int len = 0;

while((len = bis.read(buf)) != -1)

{

out.write(buf, 0, len);

}

// 获取对方的IP地址对象

String ip = socket.getInetAddress().getHostAddress();

/**

 * 由于是多个客户端可以下载且每个客户端独立且唯一,因此,统计的时候

 * 不应该有重复的ip地址,所以,我们将选用一个集合来存储,根据以上的需求

 * 我应该选择的是set集合(set集合里面的元素是不允许重复的),set集合

 * 有哪些子接口?treeSet(用于排序用的) & hashSet(不允许重复)

 */

if(ips.add(ip))

{

System.out.println("恭喜:" + ip + "用户下载成功!!  当前下载的人数是:" + ips.size());

}

// 4、释放资源

bis.close();

socket.close();

    }

    catch (Exception e)

    {

     e.printStackTrace();

     throw new RuntimeException(e);

    }

}

public static void main(String[] args) throws Exception

    {

    // 1、建立tcp服務端

ServerSocket serverSocket = new ServerSocket(9090);

// 不断接受客户端的连接

while(true)

{

// 2、接收客户端的请求连接

Socket socket = serverSocket.accept();

// 为每个客户端开启一条线程

new ImageServer(socket).start();

}

}

}

多个客户端之一:

public class ImageClient

{

public static void main(String[] args) throws Exception

    {

    // 1、建立TCP服务,TCP的客户端一旦启动,马上就要建立连接。

Socket socket = new Socket(InetAddress.getLocalHost(),9090);

// 2、获取对应的流对象,因为TCP是基于IO流进行数据传输的。

String data = "这是我的第一个TCP例子!!";

OutputStream outputStream = socket.getOutputStream();

// 3、把数据写出

outputStream.write(data.getBytes());

// 读取服务端反馈的数据

InputStream inputStream = socket.getInputStream();

BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("D:/c.jpg"));

byte[] buf = new byte[1024];

int len = 0;

while((len = inputStream.read(buf)) != -1)

{

bos.write(buf, 0, len);

}

// 4、关闭资源

socket.close();

    }

}

 

模拟Tomcat服务器

 

TomcatDemo:

/**

 * 需求:

 * 模拟服务器给浏览器输送数据,同时证明浏览器与服务器之间的通讯是

 * 使用了tcp协议

 * @author Dante Fung

 *

 */

public c lass TomcatDemo extends Thread

{

private Socket socket;

public TomcatDemo(Socket socket)

    {

    this.socket = socket;

    }

// 写多线程的任务

@Override

public void run()

{

    try

    {

    OutputStream out = socket.getOutputStream();

    // 向浏览器输出数据

    out.write("<font size='36px' color='red'>你好啊,浏览器!!!</font>".getBytes());

    socket.close();

    }

    catch (Exception e)

    {

    e.printStackTrace();

    }

}

public static void main(String[] args) throws Exception

    {

    ServerSocket serverSocket = new ServerSocket(8080);

    // 不断接收浏览器的请求

    while(true)

    {

     Socket socket = serverSocket.accept();

     new TomcatDemo(socket).start();

    }

    }

}

 

实验结果:

 

 

注意:浏览器作为客户端使用的是tcp协议与服务器进行通讯,不一定所有的浏览器都是可以得到以上的效果,因为有的浏览器有严格的检测,有可能会发现你的服务器是假的tomcat服务器。

 

练习:

TCP协议-练习-文本转换客户端-客户端

需求:客户端输入字母数据,发送给服务端,服务端收到后显示在控制台,并将该数据转成大写返回给客户端。

 

参考答案:

客户端:

/**

 * TCP文本转换客户端

 * @author Dante Fung

 *

 */

public class TransClient

{

public static void main(String[] args) throws Exception

    {

System.out.println("客户端启动... ...");

    /*

     * 思路:

     * 客户端:

     * 1、先有socket端点。

     * 2、客户端的数据源:键盘。

     * 3、客户端的目的:socket。

     * 4、接收服务端的数据,源:socket。

     * 5、将数据显示在控制台,目的:控制台。

     * 6、在这些流中操作的数据都是文本数据。

     * 

     * 转换客户端:

     * 1、创建socket客户端对象。

     * 2、获取键盘录入。

     * 3、将录入的信息发送给socket输入流。

     */

// 1、创建socket客户端对象。

Socket socket = new Socket("192.168.1.102",10004);

// 2、获取键盘录入。

BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

// 3、socket输出流

/**

 * 你输出数据的时候,是不是保证了数据原样性不变的出去了。

 * PrintWriter

 * 

 * 记住一点,我们写socket流的时候,凡是名称里面带有out、in的,我们都视为socket流。

 * 凡是不带in、out的我们都视为一般流,本地流。

 * 注意:这不是规范,这只是我总结的规律,便于能从名字中一眼看出是什么流。

 */

// BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));

// 使用个高级一点的打印流

/*

 * 参数说明:

 * 1、字节输出流

 * 2、是否自动刷新

 *   还带自动换行。

 */

PrintWriter out = new PrintWriter(socket.getOutputStream(),true);

// 4、 socket的输入流,读取服务端返回的大写数据。

InputStream in = socket.getInputStream();

// 为了操作字符方便,我们将字节输入流转换为字符输入流。

InputStreamReader isr = new InputStreamReader(in);

// 为了让读取效率高些,我们转换后的字符输入流提供缓冲功能(装饰者模式)。

BufferedReader bufIn = new BufferedReader(isr);

String line = null;

/**

 * readLine方法是一个阻塞的方法。

 */

while((line=br.readLine()) != null )

{

if("over".equals(line))

{

break;

}

// 这是带着自动刷新功能在打印

out.println(line);

// 读取服务端发送回来的一行大写数据

String upperStr = bufIn.readLine();

System.out.println("客户端===" + upperStr);

System.out.println(upperStr);

}

// 5、释放资源(关闭流,并给socket打上结束标记)。

socket.close();

    }

}

 

服务端:

/**

 * Tcp文本转换服务端

 * @author Dante Fung

 *

 */

public class TransServer

{

public static void main(String[] args) throws Exception

    {

System.out.println("服务器端启动... ...");

    /*

     * 文本转换服务端:

     * 分析:

     * 1、ServerSocket对象。

     * 2、获取socket对象。

     * 3、源:socket,读取客户端发送过来需要转换的数据。

     * 4、目的:显示在控制台上。

     * 5、将数据转换成大写发送给客户端。

     */

// 1、

ServerSocket serverSocket = new ServerSocket(10004);

// 2、获取socket对象。

Socket socket = serverSocket.accept();

// 获取Ip

String ip = socket.getInetAddress().getHostAddress();

System.out.println(ip + ".........connected");

// 3、获取socket字节读取流,转换并装饰。

BufferedReader brIn = new BufferedReader(new InputStreamReader(socket.getInputStream()));

// 4、获取socket字节输出流,转换并装饰(用这个方法的时候,记得在写出数据的时候,记得调用一下刷新方法)。

// BufferedWriter bwOut = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));

PrintWriter pwOut = new PrintWriter(socket.getOutputStream(),true);

String line = null;

while((line=brIn.readLine()) != null)

{

System.out.println(line);

pwOut.println(line.toUpperCase());

}

// 5、释放资源

socket.close();

serverSocket.close();

    }

}

 

客户端控制台:

 

服务端控制台:

 

Tcp协议 -- 常见问题解析:

 

问题:tcp通讯两端在互相等待,谁也动不了。

原因:

1、你使用了阻塞式方法,缺少结束标记。

2、你在IO操作的时候没有将数据刷到socket的输入输出流中。

客户端:

/**

 * TCP文本转换客户端

 * @author Dante Fung

 *

 */

public class TransClient

{

public static void main(String[] args) throws Exception

    {

System.out.println("客户端启动... ...");

    /*

     * 思路:

     * 客户端:

     * 1、先有socket端点。

     * 2、客户端的数据源:键盘。

     * 3、客户端的目的:socket。

     * 4、接收服务端的数据,源:socket。

     * 5、将数据显示在控制台,目的:控制台。

     * 6、在这些流中操作的数据都是文本数据。

     * 

     * 转换客户端:

     * 1、创建socket客户端对象。

     * 2、获取键盘录入。

     * 3、将录入的信息发送给socket输入流。

     */

// 1、创建socket客户端对象。

Socket socket = new Socket("192.168.1.102",10004);

// 2、获取键盘录入。

BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

// 3、socket输出流

/**

 * 你输出数据的时候,是不是保证了数据原样性不变的出去了。

 * PrintWriter

 * 

 * 记住一点,我们写socket流的时候,凡是名称里面带有out、in的,我们都视为socket流。

 * 凡是不带in、out的我们都视为一般流,本地流。

 * 注意:这不是规范,这只是我总结的规律,便于能从名字中一眼看出是什么流。

 */

BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));

// 使用个高级一点的打印流

/*

 * 参数说明:

 * 1、字节输出流

 * 2、是否自动刷新

 *   还带自动换行。

 */

// PrintWriter out = new PrintWriter(socket.getOutputStream(),true);

// 4、 socket的输入流,读取服务端返回的大写数据。

InputStream in = socket.getInputStream();

// 为了操作字符方便,我们将字节输入流转换为字符输入流。

InputStreamReader isr = new InputStreamReader(in);

// 为了让读取效率高些,我们转换后的字符输入流提供缓冲功能(装饰者模式)。

BufferedReader bufIn = new BufferedReader(isr);

String line = null;

/**

 * readLine方法是一个阻塞的方法,读到换行标记才认为一行数据读完。

 * 否则,会认为数据没有读完,一直等待数据过来。

 */

while((line=br.readLine()) != null )

{

if("over".equals(line))

{

break;

}

// 这是带着自动刷新功能在打印

// out.println(line);

bw.write(line + "\r\n");

bw.flush();

// 读取服务端发送回来的一行大写数据

String upperStr = bufIn.readLine();

System.out.println("客户端===" + upperStr);

System.out.println(upperStr);

}

// 5、释放资源(关闭流,并给socket打上结束标记)。

socket.close();

    }

}

服务端:

/**

 * Tcp文本转换服务端

 * @author Dante Fung

 *

 */

public class TransServer

{

public static void main(String[] args) throws Exception

    {

System.out.println("服务器端启动... ...");

    /*

     * 文本转换服务端:

     * 分析:

     * 1、ServerSocket对象。

     * 2、获取socket对象。

     * 3、源:socket,读取客户端发送过来需要转换的数据。

     * 4、目的:显示在控制台上。

     * 5、将数据转换成大写发送给客户端。

     */

// 1、

ServerSocket serverSocket = new ServerSocket(10004);

// 2、获取socket对象。

Socket socket = serverSocket.accept();

// 获取Ip

String ip = socket.getInetAddress().getHostAddress();

System.out.println(ip + ".........connected");

// 3、获取socket字节读取流,转换并装饰。

BufferedReader brIn = new BufferedReader(new InputStreamReader(socket.getInputStream()));

// 4、获取socket字节输出流,转换并装饰(用这个方法的时候,记得在写出数据的时候,记得调用一下刷新方法)。

BufferedWriter bwOut = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));

// PrintWriter pwOut = new PrintWriter(socket.getOutputStream(),true);

String line = null;

/**

 * 阻塞式方法,读到换行标记才认为一行数据读完。否则,会认为数据没有读完,

 * 一直等待数据过来。

 */

while((line=brIn.readLine()) != null)

{

System.out.println(line);

// pwOut.println(line.toUpperCase());

bwOut.write(line.toUpperCase() + "\r\n");// 换行符为阻塞式方法readLine()的结束标记

bwOut.flush();//将数据从BufferedReader 或 BufferedWriter刷到socket的输入输出流中

}

// 5、释放资源

socket.close();

serverSocket.close();

    }

}

TCP文本文件上传:

需求:客户端上传文本文件,服务器端接收后,将该文本文件写在服务器的磁盘里。

 

参考答案:

服务端:

package com.dantefung.net.tcp;

 

import java.io.BufferedReader;

import java.io.BufferedWriter;

import java.io.FileWriter;

import java.io.InputStreamReader;

import java.io.PrintWriter;

import java.net.ServerSocket;

import java.net.Socket;

 

/**

 * Tcp协议,文本文件上传服务端

 * @author Dante Fung

 *

 */

public class UploadServer

{

public static void main(String[] args) throws Exception

    {

    // 1、建立tcp服务。

ServerSocket serverSocket = new ServerSocket(10005);

// 2、接收客户端请求。

Socket socket = serverSocket.accept();

System.out.println(socket.getInetAddress().getHostAddress() + "......connected");

// 3、获取socket的字节输入流对象,转换并装饰。

BufferedReader bufIn = new BufferedReader(new InputStreamReader(socket.getInputStream()));

// 4、创建字符输入流。

BufferedWriter bw = new BufferedWriter(new FileWriter("src\\com\\dantefung\\net\\tcp\\server.txt"));

// 不断读取客户端发送过来的数据。

String line = null;

while((line=bufIn.readLine()) != null)

{

// if("over".equals(line)) break;

bw.write(line);

bw.newLine();

// 缓冲区默认是8kb,一旦装满会自动刷出,但一般情况下,缓冲区都未被装满

// 因此,需要自己手动刷新一下数据,将缓冲区的数据输出去。

bw.flush();

}

// 5、给客户端回写消息

PrintWriter out = new PrintWriter(socket.getOutputStream(),true);

out.println("上传成功!!!");

// 6、释放资源.

bw.close();

socket.close();

serverSocket.close();

    }

}

 

客户端:

package com.dantefung.net.tcp;

 

import java.io.BufferedReader;

import java.io.FileReader;

import java.io.InputStreamReader;

import java.io.PrintWriter;

import java.net.InetAddress;

import java.net.Socket;

 

/**

 * Tcp协议,文本文件上传客户端。

 * @author Dante Fung

 *

 */

public class UploadClient

{

public static void main(String[] args) throws Exception

    {

    // 1、建立tcp通讯插座。

Socket socket = new Socket(InetAddress.getLocalHost(),10005);

// 2、创建字符输入流,读取本地的文本文件。

BufferedReader br = new BufferedReader(new FileReader("src\\com\\dantefung\\net\\tcp\\client.txt"));

// 3、获取socket的输出流,将该流包装成打印流(打印流,可指定自动刷新和输出自动换行)。

PrintWriter out = new PrintWriter(socket.getOutputStream(),true);

// 4、不断地给服务端写数据

String line = null;

while((line=br.readLine()) != null)

{

out.println(line);

}

// 告诉服务端,客户端的数据写完了。

socket.shutdownOutput();

// out.println("over");// 结束标记,软件开发中一般先发送一个当前时间的毫秒值给服务端,服务端到时就直接以这个毫秒值作为客户端结束写数据的标记

// 5、读取服务端返回的数据。

BufferedReader bufIn = new BufferedReader(new InputStreamReader(socket.getInputStream()));

String str = bufIn.readLine();

System.out.println(str);

// 6、释放资源.

socket.close();

    }

}

 

 

进阶篇:关于socket的设计(思想):

Socket四问:

 

Socket是什么?

 

概述:

     Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口。在设计模式中,Socket其实就是一个门面(Facade)模式,它把复杂的TCP/IP协议族隐藏在Socket接口后面,对用户来说,一组简单的接口就是全部,让Socket去组织数据,以符合指定的协议。

 

概念:

     网络应用程序是通过网络使用通信协议实现进程间的通信,TCP/IP就是网络上常用的协议之一,在进行网络应用程序设计时,TCP/IP协议的核心内容被封装在操作系统中。网络应用程序要使用TCP/IP协议来实现自己的功能,只能通过由系统提供给用户的TCP/IP协议编程接口来实现。因此,可以说设计网络应用程序就是利用网络编程接口(API)进行程序设计的过程。在Windows环境下的网络应用程序编程接口叫Windows Sockets,即套接口(插座)

 

图解:

    当主机A上的网络应用程序A要发送数据时,通过调用数据发送函数首先将要发送的一段信息写入Socket中,Socket中的内容通过主机A的网络管理软件由主机A的网络接口卡发送到主机B,主机B的网络接口卡接收到这段信息后,再传给主机B的网络管理软件,网络管理软件将这段信息保存在主机BSocket中,然后程序B才能在Socket中读取并使用这段信息。由此看来,Socket本质是通信过程中所要使用的一些缓冲区及一些相关的数据结构

 

套接字(插座)的分类:

   (1)流式套接字。(TCP协议)

   (2)数据包套接字。(UDP协议)

   (3)原始套接字。

 

流式套接字:

   它提供了一种可靠的、面向连接的双向数据传输服务,实现数据无差错、无重复地发送。流失套接口内设流量控制,被传输的数据看作是无记录边界的字节流。

   

适用性:

    在TCP/IP协议族中,使用TCP协议来实现字节流的传输,当用户想要发送大批量的数据或者对数据的传输有较高的要求时,使用流式套接口。

 

数据包套接字:

它提供了一种无连接、不可靠的双向数据传输服务。数据包以独立的包形式被发送,并保留了记录边界,不提供可靠性保证。数据在传输过程中可能会丢失或重复,并且不能保证在接收端数据按发送顺序接收。在TCP/IP协议族中,使用UDP协议来实现数据报套接口。

 

适用性:

    在同一台计算机上或负载较轻的LAN上,因为出现差错的可能性较小,所以可以使用数据报套接口进行数据传输,这样通信的质量可以得到保证,并且通信的效率较高。

 

原始套接字:

该套接口允许对较低层协议(如IPICMP)进行直接访问,常用于检验新的网络协议实现,也可用于测试新配置或安装的网络设备。

 

Socket在哪里?

 

 

【你越会生活,就越能理解程序设计】

生活场景:

       你要打电话给一个朋友,先拨号,朋友听到电话铃声后提起电话,这时你和你的朋友就建立起了连接,就可以讲话了。等交流结束,挂断电话结束此次交谈。    生活中的场景就解释了这工作原理,也许TCP/IP协议族就是诞生于生活中,这也不一定。

 

 

socket相关的几个(专业)定义:

1IP地址:即依照TCP/IP协议分配给本地主机的网络地址,两个进程要通讯,任一进程首先要知道通讯对方的位置,即对方的IP

2)端口号:用来辨别本地通讯进程,一个本地的进程在通讯时均会占用一个端口号,不同的进程端口号不同,因此在通讯前必须要分配一个没有被访问的端口号。

3)连接:指两个进程间的通讯链路。

4)半相关:网络中用一个三元组可以在全局唯一标志一个进程:

(协议,本地地址,本地端口号)

这样一个三元组,叫做一个半相关,它指定连接的每半部分。

4)全相关:一个完整的网间进程通信需要由两个进程组成,并且只能使用同一种高层协议。也就是说,不可能通信的一端用TCP协议,而另一端用UDP协议。因此一个完整的网间通信需要一个五元组来标识:

(协议,本地地址,本地端口号,远地地址,远地端口号)

这样一个五元组,叫做一个相关(association),即两个协议相同的半相关才能组合成一个合适的相关,或完全指定组成一连接。

(感觉回归到了离散数学了。。。)

 

客户/服务器模式

建立基于以下两点:

1)首先,建立网络的起因是网络中软硬件资源、运算能力和信息不均等,需要共享,从而造就拥有众多资源的主机提供服务,资源较少的客户请求服务这一非对等作用。

2)其次,网间进程通信完全是异步的,相互通信的进程间既不存在父子关系,又不共享内存缓冲区,因此需要一种机制为希望通信的进程间建立联系,为二者的数据交换提供同步,这就是基于客户/服务器模式的TCP/IP

 

 

 

HOW

详见上面的TCPUDP的例子。     

 

 

 

一些总结:

回忆HTTP协议

HTTP协议即超文本传送协议(Hypertext Transfer Protocol ),是Web联网的基础,也是手机联网常用的协议之一,HTTP协议是建立在TCP协议之上的一种应用。

HTTP连接最显著的特点是客户端发送的每次请求都需要服务器回送响应,在请求结束后,会主动释放连接。从建立连接到关闭连接的过程称为“一次连接”

再看 Socket建立连接

建立Socket连接至少需要一对套接字,一个运行于客户端,称为ClientSocket,另一个运行于服务器端,称为ServerSocket 。套接字之间的连接过程分为三个步骤:服务器监听,客户端请求,连接确认。

服务器监听:服务器端套接字并不定位具体的客户端套接字,而是处于等待连接的状态,实时监控网络状态,等待客户端的连接请求。 

客户端请求:指客户端的套接字提出连接请求,要连接的目标是服务器端的套接字。为此,客户端的套接字必须首先描述它要连接的服务器的套接字,指出服务器端套接字的ip地址和端口号,然后就向服务器端套接字提出连接请求。

连接确认:当服务器端套接字监听到或者说接收到客户端套接字的连接请求时,就响应客户端套接字的请求,建立一个新的线程,把服务器端套接字的描述发给客户端,一旦客户端确认了此描述,双方就正式建立连接。而服务器端套接字继续处于监听状态,继续接收其他客户端套接字的连接请求。

Socket连接与TCP/IP连接

创建Socket连接时,可以指定使用的传输层协议是哪一个,Socket可以支持不同的传输层协议(TCP或UDP),当使用TCP协议进行连接时,该Socket连接就是一个TCP连接。

Socket连接与HTTP连接

很多情况下,需要服务器端主动向客户端推送数据,保持客户端与服务器数据的实时与同步。此时若双方建立的是Socket连接,服务器就可以直接将数据传送给客户端;若双方建立的是HTTP连接,则服务器需要等到客户端发送一次请求后才能将数据传回给客户端,因此,客户端定时向服务器端发送连接请求,不仅可以保持在线,同时也是在“询问”服务器是否有新的数据,如果有就将数据传给客户端。

http协议是应用层的协义 :一个是发动机(Socket),提供了网络通信的能力 ,一个是轿车(Http),提供了具体的方式 

两个计算机之间的交流无非是两个端口之间的数据通信,具体的数据会以什么样的形式展现,是以不同的应用层协议来定义的。如HTTP、FTP...Socket是对端口通信开发的工具,它要更底层一些。

get和post

http协议的两种方法,这两种方法有本质的区别,get只有一个流,参数附加在url后,大小个数有严格限制且只能是字符串。post的参数是通过另外的流传递的,不通过url,所以可以很大,也可以传递二进制数据,如文件的上传。

TCP/IP、Http、Socket的区别

IP协议对应于网络层,TCP协议对应于传输层,而HTTP协议对应于应用层。平时说的最多的socket是什么呢,实际上socket是对TCP/IP协议的封装,Socket本身并不是协议,而是一个API。通过Socket,我们才能使用TCP/IP协议。

Socket跟TCP/IP协议没有必然的联系,Socket编程接口在设计的时候,就希望也能适应其他的网络协议。所以说,Socket的出现只是使得程序员更方便地使用TCP/IP协议栈而已,是对TCP/IP协议的抽象,从而形成了我们知道的一些最基本的函数接口,比如create、listen、connect、accept、send、read和write等等。

记住:TCP/IP只是一个协议栈,就像操作系统的运行机制一样,必须要有具体的实现,同时还要提供对外的操作接口,程序员才能使用。

那么HTTP协议(即超文本传送协议)就好像是轿车,提供了封装或者显示数据的具体形式,Socket是发动机,提供了网络通信的能力。且HTTP连接最显著的特点是客户端发送的每次请求都需要服务器回送响应,在请求结束后,会主动释放连接。从建立连接到关闭连接的过程称为“一次连接”。

而传输层的TCP是基于网络层的IP协议,而应用层的HTTP协议又是基于传输层的TCP协议,又Socket本身不是协议,就像上面所说,它只是提供了一个针对TCP/UDP或套接字之间的连接过程。分为三个步骤:服务器监听,客户端请求,连接确认。

参考资料:

1】:http://blog.csdn.net/dog250/article/details/18959371

2】:http://kb.cnblogs.com/page/188594/

3】:http://www.pinjiao.com/lunwenqikan/jisuanjilunwen/lunwen23442.html

4】:http://www.lxway.com/446048542.htm

 

 

 





posted @ 2016-03-17 15:28  Dantefung  阅读(387)  评论(0编辑  收藏  举报