在当今数字化时代,互联网已经成为人们生活中不可或缺的一部分。而在互联网的背后,TCP/IP 协议扮演着至关重要的角色,堪称互联网的基石。
TCP/IP 协议是一组用于数据通信的协议集合,它的名字来源于其中最重要的两个协议:传输控制协议(TCP)和网际协议(IP)。自 20 世纪 70 年代末期以来,TCP/IP 协议已经成为全球互联网通信的通用语言,它定义了数据如何在网络上进行传输和路由。
TCP/IP 协议遵循分层模型,将网络通信分为四个层次:链路层、网络层、传输层和应用层。每层负责不同的功能,通过协同工作,确保数据从源头安全、高效地传输到目的地。
链路层负责在物理媒介上发送和接收数据,如以太网、Wi-Fi 等。网络层负责数据包的路由和转发,确保数据包能够跨越多个网络到达目的地。IP 协议是这一层的核心,它通过给每个数据包分配一个唯一的 IP 地址,确保数据能够正确地路由到目标计算机。
传输层负责提供端到端的数据传输服务。TCP 和 UDP 是这一层的两个主要协议。TCP 是一种面向连接的、可靠的传输层协议,它确保数据包按顺序到达,并且允许接收方确认数据包的接收。UDP 则是一种无连接的、不可靠的传输层协议,它不保证数据包的顺序或完整性,但速度更快,适用于对实时性要求高的应用。
应用层为应用软件提供网络服务,如 HTTP、FTP、SMTP 等。这些协议定义了客户端和服务器之间的通信规则,使得用户能够在互联网上进行各种活动,如浏览网页、发送电子邮件、下载文件等。
总之,TCP/IP 协议是现代互联网通信的基础,它的重要地位不可替代。
计算机网络是根据应用的需要发展而来的,它是将地理位置分散的计算机系统和通信设备连接起来,实现资源共享和信息传递的系统。计算机网络的功能主要表现在硬件资源共享、软件资源共享和用户间信息交换三个方面。
硬件资源共享可以在全网范围内提供对处理资源、存储资源、输入输出资源等昂贵设备的共享,从而使用户节省投资,也便于集中管理和均衡分担负荷。软件资源共享使得互联网上的用户可以远程访问各类大型数据库,可以通过网络下载某些软件到本地机上使用,可以在网络环境下访问一些安装在服务器上的公用网络软件,也可以通过网络登录到远程计算机上使用该计算机上的软件,这样可以避免软件研制上的重复劳动以及数据资源的重复存储,也便于集中管理。用户间信息交换是计算机网络最基本的功能,主要完成计算机网络中各个节点之间的系统通信,用户可以在网上传送电子邮件、发布新闻消息、进行电子购物、电子贸易、远程电子教育等。
通过 Wireshark 抓取 HTTP 协议的报文,可以分析传输层、网络层和数据链路层封装的信息。
传输层封装的是 TCP 协议,可以看到源端口号,目标端口号。以访问百度为例,先通过三次握手建立连接,第一次握手:客户端发送 syn 包(syn=j)到服务器,并进入 SYN_SEND 状态,等待服务器确认;第二次握手:服务器收到 syn 包,必须确认客户的 SYN(ack=j+1),同时自己也发送一个 SYN 包(syn=k),即 SYN+ACK 包,此时服务器进入 SYN_RECV 状态;第三次握手:客户端收到服务器的 SYN+ACK 包,向服务器发送确认包 ACK (ack=k+1),此包发送完毕,客户端和服务器进入 ESTABLISHED 状态,完成三次握手。
网络层封装的是 IP 包头,包括 IPV4 的版本,首部长度,协议类型是 TCP 协议,源 IP 地址,目标 IP 地址等。
数据链路层,协议类型是 OX0800 代表三层使用的是 IPV4 协议,源主机的 MAC 地址,目标主机的 MAC 地址等。
Java 中可以使用ServerSocket和Socket类实现基于 TCP/IP 协议的文件传输。服务端创建一个ServerSocket对象,绑定到指定端口,等待客户端的连接请求。客户端创建一个Socket对象,指定服务端的 IP 地址和端口,发出连接请求。连接建立后,客户端通过文件输入流读取本地文件,然后通过Socket的输出流向服务端发送文件数据。服务端通过Socket的输入流接收文件数据,并保存到本地文件中。
服务端代码:
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
public class TCPServer {
public static void main(String[] args) throws IOException {
// 获取 ServerSocket 对象,提供 TCP 连接服务
ServerSocket serverSocket = new ServerSocket(8888);
// 等待接收客户端的 TCP 连接申请
Socket socket = serverSocket.accept();
// 保存客户端发送的文件
// 获取 TCP 连接提供的字节输入流
InputStream is = socket.getInputStream();
// 把数据存入指定文件
FileOutputStream fos = new FileOutputStream(new File("D://receivedFile.txt"));
byte[] b = new byte[1024];
int len;
// 读取数据,保存数据
while ((len = is.read(b))!= -1) {
fos.write(b, 0, len);
}
// 关闭资源
fos.close();
is.close();
socket.close();
serverSocket.close();
}
}
客户端代码:
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.Socket;
public class TCPClient {
public static void main(String[] args) throws IOException {
// 创建了一个匿名的 InetAddress 独享,创建了一个 socket,进行连接
Socket socket = new Socket(InetAddress.getByName("127.0.0.1"), 8888);
// 创建输出流,用来发送字节流
OutputStream os = socket.getOutputStream();
// 本身我需要获取到本地的一个文件,所以我需要 input
FileInputStream fis = new FileInputStream(new File("D://originalFile.txt"));
// 具体读和写的过程
byte[] buffer = new byte[1024];
int len;
while ((len = fis.read(buffer))!= -1) {
// 再次提醒这样是为了防止读取到重复数据
os.write(buffer, 0, len);
}
// 关闭资源
fis.close();
os.close();
socket.close();
}
}
文件传输过程:首先服务端创建一个ServerSocket对象并绑定到指定端口,然后进入等待状态,等待客户端的连接请求。客户端创建一个Socket对象,指定服务端的 IP 地址和端口,发出连接请求。当服务端接收到客户端的连接请求后,建立连接,服务端通过accept()方法返回一个Socket对象。接着,客户端通过文件输入流读取本地文件,并通过Socket的输出流向服务端发送文件数据。服务端通过Socket的输入流接收文件数据,并保存到本地文件中。
服务器端代码:
import socket
import threading
# 创建一个 socket 对象
serversocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 获取本地主机名
host = socket.gethostname()
# 设置端口号
port = 9999
# 绑定端口号
serversocket.bind((host, port))
# 设置最大连接数,超过后排队
serversocket.listen(5)
client_list = []
# 用于存储所有连接的客户端 socket
def handle_client(clientsocket):
data = clientsocket.recv(1024).decode()
print(f"收到数据:{data}")
clientsocket.send("已收到您的消息".encode())
clientsocket.close()
client_list.remove(clientsocket)
while True:
# 建立客户端连接
clientsocket, addr = serversocket.accept()
print(f"连接地址:{str(addr)}")
client_list.append(clientsocket)
# 为每个新连接创建一个线程来处理
threading.Thread(target=handle_client, args=(clientsocket,)).start()
客户端代码:
import socket
# 创建一个 socket 对象
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 获取本地主机名
host = socket.gethostname()
# 设置端口号
port = 9999
# 连接服务,指定主机和端口
s.connect((host, port))
# 发送数据
s.send("Hello, server".encode())
# 接收响应数据
response = s.recv(1024).decode()
print(f"收到响应:{response}")
# 关闭连接
s.close()
def broadcast_message(message):
for client in client_list:
client.send(message.encode())
公共聊天室的服务端可以设定客户端连接个数上限,当达到上限时,新的客户端连接将被拒绝。客户端可以向服务端发送消息,服务端可以将消息广播给所有连接的客户端,客户端也可以接收服务端广播的消息。同时,客户端可以与服务端单独通信,即客户端发送给服务端的消息只有服务端可以看到,服务端回复的消息也只有发送消息的客户端可以看到。
服务端代码:
// MyServer.cpp : 定义控制台应用程序的入口点。
#include "stdafx.h"
#include <iostream>
#include <stdlib.h>
#include <winsock2.h>
// 引用头文件
#pragma comment(lib,"ws2_32.lib")
// 最大连接数
#define g_MaxConnect 20
int g_Connect = 0;
struct sock_params {
SOCKET hsock;
int nSockIndex;
};
// 线程实现函数
DWORD WINAPI threadpro(LPVOID pParam) {
sock_params* sockPam = (sock_params*)pParam;
SOCKET hsock = sockPam->hsock;
int nSockIndex = sockPam->nSockIndex;
char aIndex[4];
_itoa_s(nSockIndex, aIndex, 10);
char buffer[1024];
char sendBuffer[1024];
if (hsock!= INVALID_SOCKET) {
std::cout << "客户端 " << nSockIndex << " 加入服务器!" << std::endl;
}
while (1) {
// 循环接收发送的内容
int num = recv(hsock, buffer, 1024, 0);
// 阻塞函数,等待接收内容
if (num > 0) {
std::cout << "客户端 " << nSockIndex << ": " << buffer << std::endl;
memset(sendBuffer, 0, 1024);
char strValue[30] = "服务器 ";
strcat_s(strValue, aIndex);
strcpy_s(sendBuffer, strValue);
char strSpace[30] = " 收到数据如下: ";
strcat_s(sendBuffer, strSpace);
strcat_s(sendBuffer, buffer);
int ires = send(hsock, sendBuffer, sizeof(sendBuffer), 0);
// 回送消息
// cout << "Send to Client: " << sendBuffer << endl;
}
else {
std::cout << "客户端 " << nSockIndex << " 关闭!" << std::endl;
// cout << "Server Process " << nSockIndex << " Closed" << endl;
return 0;
}
}
return 0;
}
// 主函数
void main() {
WSADATA wsd;
// 定义 WSADATA 对象
WSAStartup(MAKEWORD(2, 2), &wsd);
SOCKET m_SockServer;
sockaddr_in serveraddr;
sockaddr_in serveraddrfrom;
SOCKET m_Server[g_MaxConnect];
serveraddr.sin_family = AF_INET;
// 设置服务器地址
serveraddr.sin_port = htons(4600);
// 设置端口号
serveraddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
m_SockServer = socket(AF_INET, SOCK_STREAM, 0);
int nStatus = bind(m_SockServer, (sockaddr*)&serveraddr, sizeof(serveraddr));
if (nStatus == 0) {
std::cout << "服务端启动成功!" << std::endl;
}
else {
std::cout << "服务端启动失败!" << std::endl;
return;
}
int iLisRet = 0;
int len = sizeof(sockaddr);
while (1) {
iLisRet = listen(m_SockServer, 0);
// 进行监听
m_Server[g_Connect] = accept(m_SockServer, (sockaddr*)&serveraddrfrom, &len);
// 同意连接
if (m_Server[g_Connect]!= INVALID_SOCKET) {
if (g_Connect > g_MaxConnect - 1) {
char WarnBuf[50] = "客户端连接个数:超限!";
int ires = send(m_Server[g_Connect], WarnBuf, sizeof(WarnBuf), 0);
}
else {
char cIndex[4];
_itoa_s(g_Connect, cIndex, 10);
char buf[50] = "你的服务端 ID: ";
strcat_s(buf, cIndex);
int ires = send(m_Server[g_Connect], buf, sizeof(buf), 0);
// 发送字符过去
// cout << buf << endl;
HANDLE m_Handel;
// 线程句柄
DWORD nThreadId = 0;
// 线程 ID
sock_params sockPam;
sockPam.hsock = m_Server[g_Connect];
sockPam.nSockIndex = g_Connect;
m_Handel = (HANDLE)::CreateThread(NULL, 0, threadpro, &sockPam, 0, &nThreadId);
CloseHandle(m_Handel);
}
++g_Connect;
}
}
WSACleanup();
}
客户端代码:
// MyClient.cpp : 定义控制台应用程序的入口点。
#include "stdafx.h"
#include <iostream>
#include <stdlib.h>
#include <stdio.h>
#include "winsock2.h"
#include <time.h>
#pragma comment(lib,"ws2_32.lib")
void main() {
WSADATA wsd;
// 定义 WSADATA 对象
WSAStartup(MAKEWORD(2, 2), &wsd);
SOCKET m_SockClient;
sockaddr_in clientaddr;
clientaddr.sin_family = AF_INET;
// 设置服务器地址
clientaddr.sin_port = htons(4600);
// 设置服务器端口号
clientaddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
m_SockClient = socket(AF_INET, SOCK_STREAM, 0);
if (m_SockClient == INVALID_SOCKET) {
printf("Sock 初始化失败: %d\n", WSAGetLastError());
WSACleanup();
return;
}
// 获取发送缓冲区和接送缓冲区大小
{
int optlen = 0;
int optval = 0;
optlen = sizeof(optval);
getsockopt(m_SockClient, SOL_SOCKET, SO_SNDBUF, (char*)&optval, &optlen);
printf("send buf len is %d\n", optval);
// 64 位 默认发送缓冲区 64k
getsockopt(m_SockClient, SOL_SOCKET, SO_RCVBUF, (char*)&optval, &optlen);
printf("Recv buf len is %d\n", optval);
// 64 位 默认接收缓冲区 64k
}
// 设定发送缓冲区大小
// optval = 1024 * 2; // setsockopt(ConnectSocket, SOL_SOCKET, SO_SNDBUF, (char*)&optval, optlen);
int nSuccess = connect(m_SockClient, (sockaddr*)&clientaddr, sizeof(clientaddr));
// 连接超时
if (nSuccess == 0) {
std::cout << "连接服务器成功!" << std::endl;
}
else {
std::cout << "连接服务器失败!" << std::endl;
return;
}
char buffer[1024];
char inBuf[1024];
int num = 0;
num = recv(m_SockClient, buffer, 1024, 0);
// 阻塞
if (num > 0) {
std::cout << buffer << std::endl;
char* pResult = strstr(buffer, "超限");
if (pResult!= NULL) {
std::cout << "服务器连接个数超限,不可用!" << std::endl;
system("pause");
return;
}
while (1) {
std::cout << "请输入要发送的消息:" << std::endl;
std::cin >> inBuf;
if (strcmp(inBuf, "exit") == 0) {
send(m_SockClient, inBuf, sizeof(inBuf), 0);
// 发送退出指令
return;
}
int send_len = send(m_SockClient, inBuf, sizeof(inBuf), 0);
if (send_len < 0) {
std::cout << "发送失败!请检查服务器是否开启!" << std::endl;
system("pause");
return;
}
int recv_len = recv(m_SockClient, buffer, 1024, 0);
// 接收客户端发送过来的数据
if (recv_len >= 0) {
std::cout << buffer << std::endl;
}
}
}
}
公共聊天室的实现过程:服务端首先初始化WSADATA对象,创建一个SOCKET对象并绑定到指定的端口号。然后,服务端进入一个无限循环,监听来自客户端的连接请求。当一个客户端请求连接时,服务端接受请求并创建一个新的SOCKET对象来与该客户端通信。服务端为每个连接的客户端创建一个线程来处理通信,线程函数接收客户端发送的消息,并将消息加上服务器的标识后回送给客户端。如果客户端关闭连接,线程函数退出。服务端还可以设置客户端连接个数上限,当达到上限时,新的客户端连接将被拒绝。客户端首先初始化WSADATA对象,
TCP/IP 协议作为互联网的基石,其重要性不言而喻。从分层模型来看,各层功能明确且相互协作,共同确保了数据在网络中的高效、准确传输。
应用层的众多协议为用户提供了丰富多样的网络服务,无论是文件传输、远程登录、电子邮件还是网络管理和域名解析,都极大地满足了人们在互联网时代的各种需求。这些协议的存在使得用户能够轻松地进行各种网络活动,实现资源共享和信息交流。
运输层的 TCP 和 UDP 协议各具特点,满足了不同应用场景的需求。TCP 的可靠性保证了数据传输的准确性和完整性,适用于对数据传输质量要求较高的场景;而 UDP 的高效性则使其在实时性要求高的应用中发挥了重要作用。
网络层的 IP 协议通过制定新地址,确保了两台主机能够准确区分彼此,并实现跨网络的数据传输。ARP 协议和路由协议的协同作用,使得数据包能够顺利地找到目标主机并进行转发。
网络接口层的以太网协议对电信号进行分组并形成数据帧,同时 MAC 地址的唯一性和以太网的广播形式确保了数据在局域网内的正确传输。
TCP/IP 协议的实际应用案例进一步展示了其强大的功能和灵活性。文件传输案例中,Java 和 Python 实现的基于 TCP/IP 协议的文件传输,为用户提供了高效的数据传输方式。Python 通信案例中的多用户支持、消息广播、客户端身份验证和持久化连接等扩展功能,丰富了网络通信的应用场景。公共聊天室案例则展示了 TCP/IP 协议在实现多人实时通信方面的能力。
总之,TCP/IP 协议的重要性不仅在于其作为互联网通信的基础,更在于其各层功能的协同作用和实际应用中的广泛适用性。它为我们的数字生活提供了坚实的支撑,推动了互联网技术的不断发展和进步。
相关文章推荐: