(含源码)基于TCP的Socket编程学习总结

 前言:

本章是关于Soket编程的学习总结,通过概念+源码来进行学习记录。

若想了解使用到的技术,如数据传输的方式、MFC实现客户端、服务端双向通信。 较为详细的Socket的总结见下面两个链接:

C++实现Windows下服务端与客户端Socket通信(一)

(详细源码)C++ socket 传输不同类型数据的四种方式

实现MFC双向通信、protobuf数据传输的源码免费下载链接

Socket通信源码CSDN下载

一、Socket编程介绍

1.什么是Socket

(1)Socket即套接字,用于描述地址和端口。应用程序通过Socket向网络发出请求或者回应。

(2)Socket就是操作系统提供给程序员操作【网络协议栈】的接口,我们可以通过Socket接口来控制协议栈工作,从而实现跨主机的网络通信。

2.Socket编程原理

(1)Socket编程有三种,流式套接字(SOCK_STREAM),数据报套接字(SOCK_DGRAM),原始套接字(SOCK_RAW)。最常用的是SOCK_STREAM、SOCK_DGRAM。

(2)基于TCP的Socket编程采用的是SOCK_STREAM流式套接字,而基于UDP的Socket编程采用的是SOCK_DGRAM数据报套接字。

(3)SOCK_STREAM表示面向连接的数据传输方式。数据可以准确无误地到达另一台计算机,如果损坏或丢失,可以重新发送,但效率相对较慢。常用的HTTP协议就使用SOCK_STREAM传输数据,因为要确保数据的正确性,否则网页不能正常解析。

(4)SOCK_DGRAM表示无连接的数据传输方式。计算机只管传输数据,不作数据校验,如果数据在传输中损坏,或者没有到达另一台计算机,是没有办法补救的。也就是说,数据错了就错了,无法重传。因为SOCK_DGRAM所做的校验工作少,所以效率比SOCK_STREAM高。

 3.基于TCP的Socket编程

(1)TCP概念

        TCP,Transmission Control Protocol,传输控制协议,基于字节流的传输层通信协议。

(2)TCP协议特点

        ①基于流的方式;

        ②面向连接;

        ③可靠通信方式;

        ④通信连接维护是面向通信的两个端点的,而不考虑中间网段和节点;

        ⑤传输数据大小限制,一旦连接建立,双方可以按统一的格式传输数据。

(3)TCP的三次握手(建立连接)

        ①客户端主动向服务端发送一个连接请求,进入SYN_SEND状态。

        ②服务端收到请求后,回客户端一个响应,进入SYN_RECV状态。

        ③客户端收到服务端的响应后,回服务端一个确认信息,进入Established状态。

(4)TCP四次挥手(断开连接)

        ①(一次挥手)客户端主动向服务端发送一个释放连接的请求,进入FIN_WAIT_1状态。

        ②(二次挥手)服务端收到请求后,回客户端一个确认响应,进入CLOSE_WAIT状态。

        ③客户端收到服务器的确认回应后,客户端不再发送数据,但仍然会接收服务端的数据,进入FIN_WAIT_2状态。

        ④(三次挥手)服务端将最后的数据发送给客户端后,就向客户端发送释放连接请求,进入LAST_ACK状态。

        ⑤(四次挥手)客户端收到服务端释放连接请求后,回服务端一个确认信息后,进入TIME_WAIT状态。

        注意:此时TCP连接还没有释放,必须再经过2 * MSL(最长报文段寿命)的时间后,当客户端撤销相应的TCB后,才进入CLOSED状态。

        ⑥服务端收到客户端的确认后,立即进入CLOSED状态。

 (5)基于TCP的Socket通信模型

 二、Socket通信程序实现

1.服务端程序

(1)初始化WSA

WORD sockVersion = MAKEWORD(2, 2);//调用2.2版本的socket
WSADATA wsaData;//WSA(Windows Sockets Asynchronous)异步套接字
//将指定版本的socket与应用程序绑定
if (WSAStartup(sockVersion, &wsaData) != 0)//返回为0则表示初始化成功
	return 0;

(2)创接套接字

SOCKET listenSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (listenSocket == INVALID_SOCKET)
{
	cout << "socket error:" << WSAGetLastError() << endl;
	WSACleanup();
	return 0;
}

(3)绑定IP和端口

sockaddr_in serverAddr;
serverAddr.sin_family = AF_INET;
serverAddr.sin_port = htons(8888);//htons用于更改IP或端口的字节顺序,htons:主机->网络;ntohs:网络->主机
serverAddr.sin_addr.S_un.S_addr = INADDR_ANY;
if (::bind(listenSocket, (SOCKADDR*)&serverAddr, sizeof(serverAddr)) == SOCKET_ERROR)
{
	cout << "bind error !" << endl;
	closesocket(listenSocket);
	WSACleanup();
	return 0;
}

(4)开始监听

if (listen(listenSocket, 5) == SOCKET_ERROR)
{
	cout << "listen error !" << endl;
	closesocket(listenSocket);
	WSACleanup();
	return 0;
}

(5)循环接收客户端数据

while (true)
{
	SOCKET sClient = INVALID_SOCKET;//初始化一个接受的客户端socket
	sockaddr_in remoteAddr;		//接收客户端的远程地址
	int nAddrlen = sizeof(remoteAddr);
	cout << "等待登录..." << endl;
	sClient=accept(listenSocket,(SOCKADDR*)&remoteAddr, &nAddrlen);
	if (sClient == INVALID_SOCKET)
	{
		cout << "accept error !" << WSAGetLastError() << endl;
		closesocket(listenSocket);
		WSACleanup();
		return 0;
	}
	cout << "用户登录地址:" << inet_ntoa(remoteAddr.sin_addr) << endl;
	//可增加多线程接收多个客户端数据
	CreateThread(NULL, 0, ThreadFunc, (LPVOID)sClient, 0, NULL);
}

(6)关闭客户端连接

While(true)
{
    //循环接收客户端数据
    ......
    closesocket(listenSocket);
}

(7)释放套接字资源

while(true)
{
    ....
}
WSACleanup();
return 0;

2.客户端程序

(1)初始化WSA

WORD sockVersion = MAKEWORD(2, 2);
WSADATA wsaData;//WSA(Windows Sockets Asynchronous)异步套接字
if (WSAStartup(sockVersion, &wsaData) != 0)
	return;

(2)创建客户端Socket

m_clientSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (m_clientSocket == INVALID_SOCKET)
{
	MessageBox("Invalid Socket!", "错误", MB_ICONERROR);
	return;
}

(3)请求连接

//建立一个客户端地址
sockaddr_in serAddr;
serAddr.sin_family = AF_INET;
serAddr.sin_port = htons(8888);
serAddr.sin_addr.S_un.S_addr = inet_addr("127.1.0.1");
//客户端向服务端请求连接
if (connect(m_clientSocket, (sockaddr*)&serAddr, sizeof(serAddr)) == SOCKET_ERROR)
{
	MessageBox("Connect Error!!", "错误", MB_ICONERROR);
	closesocket(m_clientSocket);
	return;
}

(4)客户端发送数据

//与服务端连接成功,开始发送消息
char sendToServerBuff[1024] = "服务端,我来啦~";
send(m_clientSocket,*(&sendToServerBuff), sizeof(sendToServerBuff), 0);

(5)接收服务端数据

//接收服务端返回的消息
char buffFromServer[1024];
int recvDataLen = recv(m_clientSocket, buffFromServer, sizeof(buffFromServer), 0);
if (recvDataLen > 0)
{
	MessageBox(buffFromServer);
}

(6)断开连接

closesocket(m_clientSocket);

(7)释放套接字资源

WSACleanup();

  • 2
    点赞
  • 50
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论
Socket编程是网络通信的基础,它是操作系统提供的一种接口,用于应用程序之间进行通信。源码层面的理解通常涉及到操作系统内核、网络协议栈和编程语言库的底层实现。 在Linux或Unix系统中,socket编程主要涉及到以下几个部分的源码: 1. **内核空间**:Linux的内核提供了socket相关的系统调用(如`accept()`, `bind()`, `connect()`, `listen()`等),这些在内核的网络子系统(net/core)中的socket.c文件中实现。 2. **协议栈**:例如TCP/IP协议栈中,`net/tcp`目录下的代码处理了TCP连接的建立、数据传输等。`netinet/in.h`和`arpa/inet.h`等头文件定义了IP地址和端口号的结构。 3. **用户空间库**:编程语言的Socket API(如C++的`boost::asio`或Python的`socket`模块)通常由编程语言标准库或者第三方库实现,它们会调用上述内核接口。例如,在C库中,`unistd.h`和`sys/socket.h`头文件包了函数声明。 4. **编程语言源码**:使用Socket编程的应用程序(如服务器或客户端)会直接调用这些库提供的API,编写相应的网络请求处理逻辑。 学习源码涉及的内容较多,建议从理解基本的网络通信原理开始,逐步深入到操作系统层面和特定编程语言的细节。如果你对某个具体编程语言的Socket库感兴趣,我可以为你提供更详细的指引,或者针对一个特定问题进行解释。以下是几个相关问题:
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

abcdefzzzz

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值