什么是TCP/IP
TCP/IP(Transmission Control Protocol/Internet Protocol,传输控制协议/因特网互联协议)是一个用于网络通信的协议族,而不是单一的协议。这个协议族是互联网的基础,它定义了电子设备如何连入因特网以及数据如何在它们之间传输的标准。TCP/IP协议族实际上包含了上百个协议,但其中最重要的是TCP和IP两个核心协议。
IP协议(Internet Protocol)
IP协议是TCP/IP协议族中的网络层协议,负责将数据包从一个网络传送到另一个网络。每个连接到互联网的设备都会被分配一个唯一的IP地址,这个地址用于在网络中标识设备。IP协议通过IP地址将数据从源设备发送到目标设备,但它并不保证数据的可靠性、顺序或完整性。这些功能由更上层的协议,如TCP,来提供。
TCP协议(Transmission Control Protocol)
TCP协议是TCP/IP协议族中的传输层协议,负责在源端和目标端之间建立连接,并确保数据包的可靠传输。TCP通过序列号、确认应答、数据校验和以及重发控制等机制,确保数据按顺序、无差错、不丢失地从一端传输到另一端。TCP是面向连接的,即在数据传输之前需要建立连接,数据传输结束后需要断开连接。
TCP/IP协议族的重要性
TCP/IP协议族的重要性在于它提供了互联网通信的基石。无论是浏览网页、发送电子邮件、在线视频通话还是进行电子商务交易,都离不开TCP/IP协议的支持。这个协议族使得不同操作系统、不同硬件平台的设备能够相互通信,从而构建了一个全球性的互联网络。
总结
TCP/IP是一个包含众多协议的协议族,其中TCP和IP是两个最重要的协议。TCP负责确保数据的可靠传输,而IP负责数据的网络传输。这两个协议共同构成了互联网通信的基础,使得全球范围内的设备能够相互连接和通信。
二、什么是socket
Socket,即套接字,是一种通信协议,也是一个抽象层或编程接口,用于在网络中实现进程间的通信。具体来说,Socket允许应用程序通过网络发送和接收数据,可以对其进行像对文件一样的打开、读写和关闭等操作。Socket的概念最早由UNIX操作系统引入,后来被广泛应用于各种操作系统和编程语言中。
Socket的基本组成
Socket地址由两部分组成:IP地址和端口号。
- IP地址:用于标识网络中的设备,即网络层地址。
- 端口号:用于标识设备上的特定应用程序,即传输层地址。一个套接字地址可以唯一地标识网络中的一个通信实体。
Socket的类型
Socket主要有两种类型:
- 流式套接字(Stream Socket):基于TCP协议,提供面向连接、可靠的数据传输服务。数据在传输过程中会被分成多个数据包,按照顺序发送和接收。
- 数据报套接字(DatagramSocket):基于UDP协议,提供无连接、不可靠的数据传输服务。数据以数据报的形式发送,不保证数据包的顺序和完整性,但在出现差错的可能性较小或允许部分传输出错的应用场合,其通信效率较高。
Socket的工作流程
Socket在通信过程中会经历不同的状态,如CLOSED、LISTEN、SYN-SENT、SYN-RECEIVED、ESTABLISHED、FIN-WAIT-1、FIN-WAIT-2、CLOSE-WAIT、CLOSING、LAST-ACK、TIME-WAIT等,这些状态描述了Socket在建立连接、传输数据和关闭连接过程中的状态变化。
Socket的创建和通信过程大致如下:
- 创建Socket:应用程序通过调用Socket库中的相关函数来创建一个新的Socket。
- 绑定地址和端口:服务器端的Socket需要绑定到一个特定的IP地址和端口号上,以便监听来自客户端的连接请求。
- 监听连接:服务器端的Socket进入监听状态,等待客户端的连接请求。
- 建立连接:客户端的Socket通过调用connect方法,提供服务器端的IP地址和端口号,向服务器发起连接请求。服务器端的Socket在接收到连接请求后,会建立一个新的响应Socket来处理这个连接请求。
- 数据传输:连接建立后,客户端和服务器就可以通过Socket发送和接收数据了。
- 关闭连接:数据传输完成后,双方可以通过发送FIN包和回复ACK包来关闭连接。
Socket的编程模式
Socket编程可以分为阻塞模式和非阻塞模式,以及同步模式和异步模式。
- 阻塞模式:在阻塞模式下,Socket函数在操作未完成时会阻塞执行,直到操作完成。
- 非阻塞模式:在非阻塞模式下,Socket函数会立即返回,应用程序需要定期检查操作是否完成。
- 同步模式:在同步模式下,应用程序在发送或接收数据时需要等待操作完成。
- 异步模式:在异步模式下,应用程序可以在发送或接收数据时继续执行其他任务,当数据到达或发送完成时,操作系统会通知应用程序。
Socket的应用场景
Socket广泛应用于各种网络应用程序中,如Web服务器、文件传输服务器、即时通讯软件等。Web服务器通常使用流式套接字与客户端建立HTTP连接,接收客户端的请求并发送响应数据。文件传输应用程序(如FTP)可以使用流式套接字或数据报套接字实现文件的上传和下载。
综上所述,Socket是网络通信中不可或缺的一部分,它提供了一种可靠的方式来实现进程间的通信和数据传输。
三、client/server即C/S模式:
TCP/IP通信中,主要是进行C/S交互。废话不多说,下面看看具体交互内容:
服务端:建立socket,申明自身的port和IP,并绑定到socket,使用listen监听,然后不断用accept去查看是否有连接。如果有,捕获socket,并通过recv获取消息的内容,通信完成后调用closeSocket关闭这个对应accept到的socket。如果不需要等待任何客户端连接,那么用closeSocket直接关闭自身的socket。
客户端:建立socket,通过端口号和地址确定目标服务器,使用Connect连接到服务器,send发送消息,等待处理,通信完成后调用closeSocket关闭socket。
四、编程
1、server端
// Serverd.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//#include <iostream>
#include<winsock.h>
using namespace std;
#pragma comment(lib,"ws2_32.lib")void initialization();
int main()
{
//定义长度变量
int send_len = 0;
int recv_len = 0;
int len = 0;
//定义发送缓冲区和接受缓冲区
char send_buf[100];
char recv_buf[100];
//定义服务端套接字,接受请求套接字
SOCKET s_server;
SOCKET s_accept;
//服务端地址客户端地址
SOCKADDR_IN server_addr;
SOCKADDR_IN accept_addr;
initialization();
//填充服务端信息
server_addr.sin_family = AF_INET;
server_addr.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
server_addr.sin_port = htons(5010);
//创建套接字
s_server = socket(AF_INET, SOCK_STREAM, 0);
if (bind(s_server, (SOCKADDR*)&server_addr, sizeof(SOCKADDR)) == SOCKET_ERROR) {
cout << "套接字绑定失败!" << endl;
WSACleanup();
}
else {
cout << "套接字绑定成功!" << endl;
}
//设置 套接字为监听状态
if (listen(s_server, SOMAXCONN) < 0) {
cout << "设置监听状态失败!" << endl;
WSACleanup();
}
else {
cout << "设置监听状态成功!" << endl;
}
cout << "服务端正在监听连接,请稍后........" << endl;
//接受连接请求
len = sizeof(SOCKADDR);
s_accept = accept(s_server, (SOCKADDR*)&accept_addr, &len);
if (s_accept == SOCKET_ERROR) {
cout << "连接失败!" << endl;
WSACleanup();
return 0;
}
cout << "连接建立,准备接受数据" << endl;
//接收数据
while (1)
{
recv_len = recv(s_accept, recv_buf, 100, 0);
if (recv_len < 0) {
cout << "接受失败!" << endl;
break;
}
else {
cout << "客户端信息:" << recv_buf << endl;
}
cout << "请输入回复信息:";
cin >> send_buf;
send_len = send(s_accept, send_buf, 100, 0);
if (send_len < 0) {
cout << "发送失败!" << endl;
break;
}
}
//关闭套接字
closesocket(s_server);
closesocket(s_accept);
//释放DLL资源
WSACleanup();
return 0;
/*std::cout << "Hello World!\n";*/
}// 运行程序: Ctrl + F5 或调试 >“开始执行(不调试)”菜单
// 调试程序: F5 或调试 >“开始调试”菜单// 入门使用技巧:
// 1. 使用解决方案资源管理器窗口添加/管理文件
// 2. 使用团队资源管理器窗口连接到源代码管理
// 3. 使用输出窗口查看生成输出和其他消息
// 4. 使用错误列表窗口查看错误
// 5. 转到“项目”>“添加新项”以创建新的代码文件,或转到“项目”>“添加现有项”以将现有代码文件添加到项目
// 6. 将来,若要再次打开此项目,请转到“文件”>“打开”>“项目”并选择 .sln 文件void initialization()
{
//初始化套接字库
WORD w_req = MAKEWORD(2, 2);//版本号
WSADATA wsadata;
int err;
err = WSAStartup(w_req, &wsadata);
if (err != 0) {
cout << "初始化套接字库失败!" << endl;
}
else {
cout << "初始化套接字成功!" << endl;
}
//检测版本号
if (LOBYTE(wsadata.wVersion) != 2 || HIBYTE(wsadata.wHighVersion) != 2) {
cout << "套接字版本号不符!" << endl;
WSACleanup();
}
else {
cout << "套接字库版本正确!" << endl;
}
//填充服务端地址信息
}
2、client端
// ClientD.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//#include <iostream>
#include<winsock.h>
using namespace std;
#pragma comment(lib,"ws2_32.lib")
void initialization();
int main()
{
//定义长度变量
int send_len = 0;
int recv_len = 0;
//定义发送缓冲区和接受缓冲区
char send_buf[100];
char recv_buf[100];
//定义服务端套接字,接受请求套接字
SOCKET s_server;
//服务端地址客户端地址
SOCKADDR_IN server_addr;
initialization();
//填充服务端信息
server_addr.sin_family = AF_INET;
server_addr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
server_addr.sin_port = htons(5010);
//创建套接字
s_server = socket(AF_INET, SOCK_STREAM, 0);
if (connect(s_server, (SOCKADDR*)&server_addr, sizeof(SOCKADDR)) == SOCKET_ERROR) {
cout << "服务器连接失败!" << endl;
WSACleanup();
}
else {
cout << "服务器连接成功!" << endl;
}//发送,接收数据
while (1)
{
cout << "请输入发送信息:";
cin >> send_buf;
send_len = send(s_server, send_buf, 100, 0);
if (send_len < 0) {
cout << "发送失败!" << endl;
break;
}
recv_len = recv(s_server, recv_buf, 100, 0);
if (recv_len < 0) {
cout << "接受失败!" << endl;
break;
}
else {
cout << "服务端信息:" << recv_buf << endl;
}}
//关闭套接字
closesocket(s_server);//释放DLL资源
WSACleanup();
return 0;
//std::cout << "Hello World!\n";
}// 运行程序: Ctrl + F5 或调试 >“开始执行(不调试)”菜单
// 调试程序: F5 或调试 >“开始调试”菜单// 入门使用技巧:
// 1. 使用解决方案资源管理器窗口添加/管理文件
// 2. 使用团队资源管理器窗口连接到源代码管理
// 3. 使用输出窗口查看生成输出和其他消息
// 4. 使用错误列表窗口查看错误
// 5. 转到“项目”>“添加新项”以创建新的代码文件,或转到“项目”>“添加现有项”以将现有代码文件添加到项目
// 6. 将来,若要再次打开此项目,请转到“文件”>“打开”>“项目”并选择 .sln 文件
void initialization()
{
//初始化套接字库
WORD w_req = MAKEWORD(2, 2);//版本号
WSADATA wsadata;
int err;
err = WSAStartup(w_req, &wsadata);
if (err != 0) {
cout << "初始化套接字库失败!" << endl;
}
else {
cout << "初始化套接字成功!" << endl;
}
//检测版本号
if (LOBYTE(wsadata.wVersion) != 2 || HIBYTE(wsadata.wHighVersion) != 2) {
cout << "套接字版本号不符!" << endl;
WSACleanup();
}
else {
cout << "套接字库版本正确!" << endl;
}
//填充服务端地址信息
}