网络编程总结-TCP/IP协议

TCP/IP协议

传输控制协议/因特网互联协议,又叫网络通讯协议。各个协议是Internet最基本的协议。

它定义了电子设备(如计算机)连入因特网的标准,以及数据如何在它们之间传输的标准。它既是互联网中的基本通信语言或协议,也是局域网的通信协议。

TCP/IP是一组包括TCP协议、IP协议、UDP协议、ICMP协议和其他一些协议的协议组。需要进行网络通信的计算机需要提供符合这些协议标准的程序以后,才能进行网络通信。

IP地址与端口号

ip协议简介

Internet Protocol简称IP,又译为网际协议或互联网协议

中文缩写为“网协”

为计算机网络相互连接进行通信而设计的协议

规定了计算机在因特网上进行通信时应当遵守的规则

任何厂家生产的计算机系统,只要遵守IP协议就可以与因特网互连互通。

是用在TCP/IP协议簇中的网络层协议

ip地址简介

互联网协议地址

IP地址是IP协议提供的一种统一的地址格式

它为互联网上的每一个网络和每一台主机分配一个逻辑地址,以此来屏蔽物理地址的差异。

IP地址被用来给Internet上的电脑一个编号。

ip地址在本地局域网上是惟一的

每一个IP地址包括两部分:网络地址和主机地址

网络ID标识在同一个物理网络上的所有宿主机,主机ID标识该物理网络上的每一个宿主机

每个IP地址包括两个标识码(ID),即网络ID和主机ID。同一个物理网络上的所有主机都使用同一个网络ID,网络上的一个主机(包括网络上工作站,服务器和路由器等)有一个主机ID与其对应。

 

物理地址:

又称为MAC地址

每个网卡在生产的时候,每个生产商都会给自己的网卡分配一个唯一的ID。

有些工具可以篡改掉。当然,如果局域网里面有两台MAC地址一样的机器,是会有问题的。

 

固定ip与动态ip

动态IP就是从DHCP服务器(路由器一般都有带,另外是一些系统上安装DHCP服务器)上动态分配一个IP,动态分配的这个IP根据DHCP服务器的设置不同,IP动态变化的时间也不同。

静态IP则是个人自己将IP手动绑定,IP的变化只能通过个人进行修改。

不同的网络设备一般会灵活使用两种方式,基本上在公司企业中都是两种模式共用

 

公网ip和私网ip

window上ipconfig查出来的是你本机的IP地址,也就是内网私有地址,此类地址仅在局域网使用,不能直接联通外网。

百度查出来的地址是你上网的共有地址,也许并不是你主机的地址,而是电信或联通分给你的地址,用于连接互联网。

 

出现这种规划的原因在于IPv4所能表示的IP太少而电脑太多以至于不够用,然而只有Public IP才能直接连接上网络,所以对于那些公司,学校,政府机构等场所,就可以集中使用私有的IP进行管理,而大家可以共用一个IP去连接上公网,这样,就省下了许多宝贵的Public IP

私网如常见的:172开头的和192开头的

当我们真正联网时,会先把数据发送到路由,然后再由路由进行处理实现真正的联网操作,路由的地址才是真正联网的Ip地址

A B C 类地址均为外网地址。为了便于内网访问,A B C类地址还留出了一部分私有地址作为内网地址供内网访问。具有内网ip的物理机可以通过NAT(网络地址转换)技术与外网通讯。

A类私有IP地址:

10.0.0.0~10.255.255.255

B类私有IP地址:

172.16.0.0~172.31.255.255

C类私有IP地址:

192.168.0.0~192.168.255.255

 

至于选择哪类私有地址,要根据内网的规模了。A B C 类内网规模依次减小。

有时看到b类的外网地址却使用的c类的私网,这个也是可以同过net技术实现的

169开头的是一个保留地址,是本地连接受限时系统自动分配的ip没有什么实际的意义,出现这种情况可能是服务器还没搭建好。

一个局域网内百度ip的地址是一样的

 

ip的唯一性

公网ip具有全球唯一性

ip地址分类

IPv4地址按照网络号和主机号的长度被分为五大类。A、B、C类用于为Internet(单播地址)中的设备接口分配地址,以及其他特殊情况下使用。类由地址中的头几位来定义:0为A类,10为B类,110为C类,1110为D类,1111为E类。D类地址供组播使用,E类地址保留。分类如下图所示:

由此可得出按此分类方式得到的IPv4地址空间划分:

这种IP地址结构分类的特点与缺陷是显而易见的,例如A类地址的网络数少但主机数多,C类地址的网络数多而主机数少,这往往会造成一个网络号内的主机号无法完全分配,造成IP地址资源的利用率低下的问题。从而使IPv4的地址资源很快就出现了枯竭的趋势,寻找进一步细分IP地址的方法也就成为了必然。

 

分类的目的:

A类保留给政府机构,B类分配给中等规模的公司,C类分配给任何需要的人,D类用于组播,E类用于实验

子网寻址(细分主机号)

最初分类寻址的方法很难为接入Internet的新网段分配一个新的网络号,随着20世纪80年代初局域网(Local Area Network,LAN)的发展这一问题更为突出。为了解决这一问题,人们自然想到一种方式,在一个站点接入Internet后为其分配网络号,然后由站点管理员进一步划分本地的子网数。也即在初始的网络号+主机号的结构的基础上,将主机号划分为子网号+主机号,这样就可以在不改变核心路由基础的前提下细分网络。这种方法被称为子网寻址。

子网掩码

子网掩码(subnet mask)又叫网络掩码、地址掩码、子网络遮罩

子网掩码将某个IP地址划分成网络地址和主机地址两部分。

使用的IPV6的话是没有子网掩码的概念,也没有网络号与主机号的概念

子网掩码是在IPv4地址资源紧缺的背景下为了解决lP地址分配而产生的虚拟lP技术,通过子网掩码将A、B、C三类地址划分为若干子网,从而显著提高了IP地址的分配效率,有效解决了IP地址资源紧张的局面。另一方面,在企业内网中为了更好地管理网络,网管人员也利用子网掩码的作用,人为地将一个较大的企业内部网络划分为更多个小规模的子网

 

子网掩码是一个32位的2进制数, 其对应网络地址的所有位都置为1,对应于主机地址的所有位都置为0。子网掩码告知路由器,地址的哪一部分是网络地址,哪一部分是主机地址

 

它的主要作用有两个,一是用于屏蔽IP地址的一部分以区别网络标识和主机标识,并说明该IP地址是在局域网上,还是在远程网上。二是用于将一个大的IP网络划分为若干小的子网络。

对于A类地址来说,默认的子网掩码是255.0.0.0;对于B类地址来说默认的子网掩码是255.255.0.0;对于C类地址来说默认的子网掩码是255.255.255.0。

 

通过子网掩码,就可以判断两个IP在不在一个局域网内部。

 

网关

网关(Gateway)又称网间连接器、协议转换器。

网关在网络层以上实现网络互连

大家都知道,从一个房间走到另一个房间,必然要经过一扇门。同样,从一个网络向另一个网络发送信息,也必须经过一道“关口”,这道关口就是网关。

说明:由于历史的原因,许多有关TCP/IP的文献曾经把网络层使用的路由器称为网关,在今天很多局域网采用都是路由来接入网络,因此通常指的网关就是路由器的IP

两个网络的通信过程

在没有路由器的情况下,两个网络之间是不能进行TCP/IP通信的,即使是两个网络连接在同一台交换机(或集线器)上,TCP/IP协议也会根据子网掩码(255.255.255.0)判定两个网络中的主机处在不同的网络里。

而要实现这两个网络之间的通信,则必须通过网关。如果网络A中的主机发现数据包的目的主机不在本地网络中,就把数据包转发给它自己的网关,再由网关转发给网络B的网关,网络B的网关再转发给网络B的某个主机

网关ip地址

网关的IP地址是具有路由功能的设备的IP地址,具有路由功能的设备有路由器、启用了路由协议的服务器(实质上相当于一台路由器)、代理服务器(也相当于一台路由器)。

网关必须是电脑自己所在的网段中的IP地址,而不能填写其他网段中的IP地址。

127.0.0.1

本地回路地址:127.0.0.1(自己访问自己网卡用,别人无法用这个地址访问自己,别人用网卡对外提供的ip,有几个网卡就有几个ip)(ip是一个int型非负数组成,每个字节由.分割)

在命令行看ip,用命令ipconfig

 

ping

(Packet Internet Groper)因特网包探索器

Ping是工作在 TCP/IP网络体系结构中应用层的一个服务命令, 主要是向特定的目的主机发送 ICMP(Iternet Control Message Protocol 因特网报文控制协议)Echo 请求报文,测试目的站是否可达及了解其有关状态

 

看自己的计算机能否与另一台计算机连通,用ping ip地址

ping自己ip可以看网卡是否有问题

有效端口:0~65535(2^16-1,尽量使用1024以上的,1024以下是重要程序知名程序或服务占用)(一个端口运行一个程序)

1024 ~ 49151 提供一般应用程序使用.(Win Socket开发选中的范围)

动态或私有端口: 49151 (2^15+2^14-1)~ 65535

UDP与TCP(高级协议)

Udp:

(User Datagram Protocol)用户数据报协议,

  • 面向无连接(发送数据无需建立连接)
  • 不可靠(不能确保UDP数据报最终达到目的地, 对接收的数据不发送确认, 无法确定是否到达目的地, 数据不会重发)
  • 不安全
  • 字节开销小,速度快。
  • 适合点对点传输(没有谁是服务器谁是客户端),并且安全性要求不高的网络应用程序。(如聊天)

Tcp:

(Transmission Control Protocol )传输控制协议

  • 面向连接
  • 提供可靠无差错的协议((当发送数据后, 要求对方返回一个确认信息, 如果没有接收到对方确认, 重发.))
  • 基于字节流 (对发送的数据排序, 每个发送字节关联一个序列号. 对方根据此序列号进行数据排序, 确保数据的顺序).
  • 安全
  • 三次握手(连接请求,连接成功,发送数据)。

都是传输层的协议,

Socket说明

套接字(socket):应用程序可以通过它发送或接收数据,是IP地址与端口的组合。

Socket就是网络驱动层提供给应用程序编程的接口和一种机制。

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

数据在两个Socket间通过IO传输。

 

UDP传输

DatagramSocket与DatagramPacket

建立发送端,接收端。

建立数据包。

调用Socket的发送接收方法。

关闭Socket。

发送端与接收端是两个独立的运行程序。

 

发送端

在发送端,要在数据报对象中明确目的地IP及端口。

DatagramSocket ds = new DatagramSocket();

byte[] by = “hello,udp”.getBytes();

DatagramPacket dp = new DatagramPacket(by,0,by.length,

InetAddress.getByName(“127.0.0.1”),10000);

ds.send(dp);

ds.close();

接收端

在接收端,要指定监听的端口。

DatagramSocket ds = new DatagramSocket(10000);

byte[] by = new byte[1024];

DatagramPacket dp = new DatagramPacket(by,by.length);

ds.receive(dp);

String str = new String(dp.getData(),0,dp.getLength());

System.out.println(str+"--"+dp.getAddress());

ds.close();

通过键盘录入获取要发送的信息。

BufferedReader br = new BufferedReader(

new InputStreamReader(System.in));

br.readLine();

将发送和接收分别封装到两个线程中。

main()线程和new Thread()线程

实例:实现字符串的发送

这样运行两个程序

打印出:

TCP传输

TCP报文格式

上图中有几个字段需要重点介绍下:

  (1)序号:Seq序号,占32位,用来标识从TCP源端向目的端发送的字节流,发起方发送数据时对此进行标记。

  (2)确认序号:Ack序号,占32位,只有ACK标志位为1时,确认序号字段才有效,Ack=Seq+1。

  (3)标志位:共6个,即URG、ACK、PSH、RST、SYN、FIN等,具体含义如下:

  (A)URG:紧急指针(urgent pointer)有效。

  (B)ACK:确认序号有效。

  (C)PSH:接收方应该尽快将这个报文交给应用层。

  (D)RST:重置连接。

  (E)SYN:发起一个新连接。

  • FIN:释放一个连接。

需要注意的是:

  (A)不要将确认序号Ack与标志位中的ACK搞混了。

  (B)确认方Ack=发起方Req+1,两端配对。

步骤

Socket和ServerSocket

建立客户端和服务器端

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

关闭socket

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

先运行服务器端再运行客户端

三次握手

TCP是面向连接的通信协议,通过三次握手建立连接

是指建立一个TCP连接时,需要客户端和服务端总共发送3个包以确认连接的建立。

 

首先Client端发送连接请求报文(SYN报文)

server段接收连接后回复ACK报文,并为这次连接分配资源。

Client端接收到ACK报文后也向Server段发送ACK报文,并分配资源,这样TCP连接就建立了。

 

Ack报文:ACK (Acknowledge character)即是确认字符,在数据通信中,接收站发给发送站的一种传输类控制字符。表示发来的数据已确认接收无误。

 

为什么要client要再发送一次确认:

为了防止已经失效的客户端请求报文,再次传到服务端,因而产生错误。

 

四次挥手

指断开一个TCP连接时,需要客户端和服务端总共发送4个包以确认连接的断开

 

1、先由客户端向服务器端发送一个FIN,请求关闭数据传输,客户端进入终止等待1状态2、当服务器接收到客户端的FIN时,向客户端发送一个ACK,服务器进入终止等待1状态,客户端收到服务器的确认请求后,此时,客户端就进入终止等待2状态,等待服务器发送连接释放报文

3、服务器向客户端发送一个FIN,告诉客户端应用程序关闭,服务器进入最后确认状态

当客户端收到服务器端的FIN是,回复一个ACK给服务器端,客户端进入时间等待状态

  • 经过2MSL(最长报文段寿命)的时间后,进入CLOSED状态。服务器只要收到了客户端发出的确认,立即进入CLOSED状态。

 

服务器结束TCP连接的时间要比客户端早一些。

 

为什么客户端最后还要等待2MSL

TCP允许不同的实现可以设置不同的MSL值。

 

  • 保证客户端发送的最后一个ACK报文能够到达服务器
  • 防止“已经失效的连接请求报文段”出现在新的连接中。客户端发送完最后一个确认报文后,在这个2MSL时间中,就可以使本连接持续的时间内所产生的所有报文段都从网络中消失。这样新的连接中不会出现旧连接的请求报文。

 

为什么建立连接是三次握手,而关闭连接却是四次挥手

这是因为服务端在LISTEN状态下,收到建立连接请求的SYN报文后,把ACK和SYN放在一个报文里发送给客户端。

而关闭连接时,当收到对方的FIN报文时,仅仅表示对方不再发送数据了但是还能接收数据,己方也未必全部数据都发送给对方了,所以己方可以立即close,也可以发送一些数据给对方后,再发送FIN报文给对方来表示同意现在关闭连接,因此,己方ACK和FIN一般都会分开发送。

 

如果已经建立了连接,但是客户端突然出现故障了怎么办?

TCP还设有一个保活计时器,显然,客户端如果出现故障,服务器不能一直等下去,白白浪费资源。服务器每收到一次客户端的请求后都会重新复位这个计时器,时间通常是设置为2小时,若两小时还没有收到客户端的任何数据,服务器就会发送一个探测报文段,以后每隔75分钟发送一次。若一连发送10个探测报文仍然没反应,服务器就认为客户端出了故障,接着就关闭连接。

TCP多线程

TCP网络程序的服务器端程序往往需要接受多个客户端的请求,如果总是单线程进行处理的话,很可能在请求较多的时候,有的请求无法得到及时的响应。要解决该问题,最好的办法就是采用多线程,用每一个线程处理一个请求。

 

客户端

客户端需要明确服务器的ip地址以及端口,这样才可以去试着建立连接,如果连接失败,会出现异常。

连接成功,说明客户端与服务端建立了通道,那么通过IO流就可以进行数据的传输,而Socket对象已经提供了输入流和输出流对象,通过getInputStream(),getOutputStream()获取即可。

与服务端通讯结束后,关闭Socket。

通过Socket建立对象并指定要连接的服务端主机以及端口。

Socket s = new Socket(“192.168.1.1”,9999);//给本机127.0.0.1
OutputStream out = s.getOutputStream();
out.write(“hello”.getBytes());
s.close();

 

代码:

查看代码
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.Socket;

public class ReverseClient {
public static void main(String args[]) throws Exception {
Socket sock = new Socket("127.0.0.1", 6999);
//获得流
InputStream in = sock.getInputStream();
OutputStream out = sock.getOutputStream();
//接收欢迎语
Thread.sleep(10);
int len = in.available();
byte[] buf = new byte[len];
in.read(buf);
System.out.println(new String(buf));
//读键盘,发送数据
BufferedReader br =new BufferedReader(new InputStreamReader(System.in));
String line;
while(true) {
line = br.readLine();
out.write((line+"\r\n").getBytes());
if("quit".equals(line))
break;
Thread.sleep(10);
len = in.available();
buf = new byte[len];
in.read(buf);
System.out.println(new String(buf));
}
sock.close();
}
}

服务器端

服务端需要明确它要处理的数据是从哪个端口进入的。

用命令行中telnet命令可以tcp服务器,如telnet www.sina.com.cn 80。输入一个字符就向服务器发送一个字符,删除键也会算一个字符发送

当有客户端访问时,要明确是哪个客户端,可通过accept()获取已连接的客户端对象,并通过该对象与客户端通过IO流进行数据传输。

 

当该客户端访问结束,关闭该客户端。

建立服务端需要监听一个端口

查看代码
 ServerSocket ss = new ServerSocket(9999);//监听指定端口
Socket s = ss.accept ();//该方法将一直等待,直到客户端连接到服务器上给定的端口。
InputStream in = s.getInputStream();//通过Socket进行通信
byte[] buf = new byte[1024];
int num = in.read(buf);
String str = new String(buf,0,num);
System.out.println(s.getInetAddress().toString()+”:”+str);
s.close();
ss.close();

代码:

查看代码
 import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
public class ReverseServer {
/**

 * 服务器程序,提供字符反向

 */
public static void main(String[] args) {
ServerSocket ss = null;
try {
//创建服务器,监听端口
ss = new ServerSocket(6999);
while(true) {
//等待用户的连接请求
Socket sock = ss.accept();
//开新线程,提供服务
new Thread(new ReverseServicer(sock)).start();
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if(ss!=null)
ss.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}}}}

查看代码
 import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.Socket;
public class ReverseServicer implements Runnable {
private Socket sock;
public ReverseServicer(Socket sock) {
this.sock = sock;
}
public void run() {
try {
//获得流
InputStream in = sock.getInputStream();
OutputStream out = sock.getOutputStream();
// 发送欢迎语
out.write("欢迎光临本小站,提供字符反向的功能".getBytes());
//接收客户端发送的数据,反向,发送给客户端
BufferedReader br =
new BufferedReader(new InputStreamReader(in));
String line;
while(true) {
line = br.readLine();
if("quit".equals(line))
break;
System.out.println(line);
System.out.println(line.length());
//a b d backspace c   c backspace d b a
StringBuilder sb = new StringBuilder(line);
sb.reverse();
System.out.println(sb);
out.write(sb.toString().getBytes());
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
sock.close();
} catch (IOException e) {
e.printStackTrace();
}

}}}

实例:把服务器端的时间发送到客户端

不用打印流包装,用转换流包装也可以。

先运行服务器端,再运行客户端,会打印:

多线程实例

TCP通信模型创建一个WEB服务器

IIS,Apache,Tomcat等WEB服务器基础原理都是采用Tcp通信模型的,只要上面代码ServerSocket监听的端口是80就可以通过在浏览器属于http://localhost/来访问该Socket,

即:ServerSocket ss = new ServerSocket(80);

在页面打印

TCP网络程序的工作原理

TCP客户端程序与TCP服务器端程序的交互过程:

服务器端程序创建一个ServerSocket,然后调用accept方法等待客户来连接。

客户端程序创建一个Socket并请求服务器端建立连接。

服务器端接收客户的连接请求,并创建一个新的Socket与该客户建立专线连接。

建立了连接的两个Socket在一个单独的线程(由服务器端程序创建)上对话。

服务器端开始等待i新的连接请求,当新的连接请求到达时,重复上述步过程。

跟Socket有关的流可通过关闭Socket来进行关闭

 

posted @ 2022-08-11 15:14  星光闪闪  阅读(438)  评论(0)    收藏  举报