欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 健康 > 养生 > Window下CLion实现本机通过socket通信-C++

Window下CLion实现本机通过socket通信-C++

2024/10/25 0:33:09 来源:https://blog.csdn.net/m0_74081875/article/details/140715176  浏览:    关键词:Window下CLion实现本机通过socket通信-C++

1.引言-什么是socket

socket即套接字,用于描述地址和端口,是一个通信链的句柄。应用程序通过socket向网络发出请求或者回应。

sockets(套接字)编程有三种,流式套接字(SOCK_STREAM),数据报套接字(SOCK_DGRAM),原始套接字(SOCK_RAW);前两种较常用。基于TCP的socket编程是采用的流式套接字

有可能多种协议使用同一种数据传输方式,所以在socket编程中,需要同时指明数据传输方式和协议。

2. socket常用函数

2.1 sockaddr_in

struct sockaddr_in这个结构体用来处理网络通信的地址

struct sockaddr_in {short            sin_family;       // 2 bytes e.g. AF_INET, AF_INET6unsigned short   sin_port;    //16位 2 bytes e.g. htons(3490)struct in_addr   sin_addr;     //32位 4 bytes see struct in_addr, belowchar             sin_zero[8];     // 8 bytes zero this if you want to
};
//另一个结构体 in_addr存放32位ip地址
struct in_addr {unsigned long s_addr;          // 4 bytes load with inet_pton()
};

端口号需要用 htons() 函数转换

2.2 htons()、 inet_addr()和inet_ntoa()

htons()作用是将端口号由主机字节序转换为网络字节序的整数值。(host to net)

inet_addr()作用是将一个IP字符串转化为一个网络字节序的整数值,用于sockaddr_in.sin_addr.s_addr。

inet_ntoa()作用是将一个sin_addr结构体输出成IP字符串(network to ascii)。比如:

printf("%s",inet_ntoa(mysock.sin_addr));

htonl()作用和htons()一样,不过它针对的是32位的(long),而htons()针对的是两个字节,16位的(short)。

与htonl()和htons()作用相反的两个函数是:ntohl()和ntohs()。

2.3 socket()

创建套接字

int socket(int af, int type, int protocol);

afIP地址的类型

  • AF_INET : IPv4

  • AF_INET6: IPV6

type:数据传输方式

  • SOCK_STREAM:面向连接的数据传输方式

  • SOCK_DGRAM:无连接的数据传输方式

protocol:传输协议

  • IPPROTO_TCP:TCP传输协议

  • IPPTOTO_UDP:UDP传输协议

返回值

  • 成功:0

  • 失败:-1

2.4 bind()

地址绑定,将套接字与地址关联

int bind(int sockfd, struct sockaddr *addr, socklen_t addrlen);

sockfd:socket文件描述符

addr:sockaddr 结构体变量的指针

addrlen:addr 变量的大小

返回值

  • 成功:0

  • 失败:-1

2.5 connect()

建立连接,创建与指定外部端口的连接

int connect(int sockfd, struct sockaddr *serv_addr, socklen_t addrlen);

sockfd:socket文件描述符

addr:sockaddr 结构体变量的指针

addrlen:addr 变量的大小

返回值

  • 成功:0

  • 失败:-1

2.6 listen()

让套接字进入被动监听状态(指当没有客户端请求时,套接字处于”睡眠“状态,只有当接收到客户端请求时,套接字才会被“唤醒”来响应请求),使得一个进程可以接收其他进程的请求,从而成为一个服务器进程

int listen(int sockfd, int backlog);

sockfd:被监听的套接字的标识符

backlog:请求队列的最大长度(能存放多少个客户端请求)

  • 请求队列:当套接字正在处理客户端请求时,如果有新的请求进来,套接字将把新的请求放入缓冲区,再从缓冲区取出请求 ,此缓冲区称为请求队列

返回值

  • 成功:0

  • 失败:-1

2.7 accept()

在一个套接口接收一个连接,当套接字处于监听状态时,可以通过accept()函数来接受客户端请求,accept()会阻塞程序进行,直到有新的请求到来

int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

sockfd:服务器端套接字的标识符

addr:sockaddr 结构体变量的指针

addrlen:addr 变量的大小

返回值

  • 成功:返回接收到的套接字的描述符

  • 失败:-1

2.8 send()

发送数据,将数据由指定的socket传给对方主机

int send(int s, const void * msg, int len, unsigned int falgs);

s:以建立好连接的socket标识符

msg:发送的消息内容

len:发送内容的长度

falgs:一般设为0

返回值

  • 成功:返回实际传送出去的字符数

  • 失败:-1

2.9 recv()

接受数据,接收远端主机经过指定的socket传来的数据,并把数据存到buf指定的内存空间

int recv(int sock, void *buf, int len, unsigned int flags);

sock:接收端套接字描述符

buf:指定缓冲区,存放接收到的数据

len:缓冲区的长度

flags:一般设为0

返回值

  • 成功:返回接收到的字符数

  • 失败:-1

3.客户端/服务端模式

在TCP/IP(Transmission Control Protocol Internet Protocol / 传输控制协议和网际协议,TCP/IP是一种网络协议,由TCP和IP两个协议组成。它负责在计算机网络中传输数据,并确保数据传输的可靠性,同时确定数据在网络中的路径。TCP/IP是网络通信的基础,也是网络上最常用的协议之一)网络应用中,通信的两个进程相互作用的主要模式是客户/服务器模式,即客户端向服务器发出请求,服务器接收请求后,提供相应的服务。因此需要一种机制为希望通信的进程间建立联系,为二者的数据交换提供同步,这就是基于客户/服务端模式的TCP/IP。

3.1 什么是TCP

TCP有三个关键步骤:三次握手,传输确认和四次挥手

构建思路:

服务端:建立socket,声明自身的端口号和地址并绑定到socket,使用listen打开监听,然后不断用accept去查看是否有连接,如果有,捕获socket,并通过recv获取消息的内容,通信完成后调用closeSocket关闭这个对应accept到的socket,如果不再需要等待任何客户端连接,那么用closeSocket关闭掉自身的socket。

客户端:建立socket,通过端口号和地址确定目标服务器,使用Connect连接到服务器,send发送消息,等待处理,通信完成后调用closeSocket关闭socket。

3.2 编程步骤

(1)服务端

1、加载套接字库,创建套接字(WSAStartup()/socket());

2、绑定套接字到一个IP地址和一个端口上(bind());

3、将套接字设置为监听模式等待连接请求(listen());

4、请求到来后,接受连接请求,返回一个新的对应于此次连接的套接字(accept());

5、用返回的套接字和客户端进行通信(send()/recv());

6、返回,等待另一个连接请求;

7、关闭套接字,关闭加载的套接字库(closesocket()/WSACleanup());

(2)客户端

1、加载套接字库,创建套接字(WSAStartup()/socket());

2、向服务器发出连接请求(connect());

3、和服务器进行通信(send()/recv());

4、关闭套接字,关闭加载的套接字库(closesocket()/WSACleanup());

3.3 学习代码

#include <stdio.h>
#include <winsock2.h>#pragma comment(lib, "ws2_32.lib")int main(int argc, char* argv[]) 
{WORD sockVersion = MAKEWORD(2, 2);WSADATA wsaData;if (WSAStartup(sockVersion, &wsaData) != 0){return 0;}SOCKET slisten = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);if (slisten == INVALID_SOCKET){printf("socket error !");return 0;}sockaddr_in sin;sin.sin_port = htons(8888);sin.sin_family = AF_INET;sin.sin_addr.S_un.S_addr = INADDR_ANY;if (bind(slisten, (LPSOCKADDR)&sin, sizeof(sin)) == SOCKET_ERROR){printf("bind error !");}if (listen(slisten, 5) == SOCKET_ERROR){printf("listen error !");return 0;}SOCKET sClient;sockaddr_in remoteAddr;int nAddrlen = sizeof(remoteAddr);char revData[255];while (true){printf("Wating for connecting... \r\n");sClient = accept(slisten, (SOCKADDR *)&remoteAddr, &nAddrlen);if (sClient == INVALID_SOCKET){printf("accept error !");continue;}printf("accept a connection: %s \r\n", inet_ntoa(remoteAddr.sin_addr));int ret = recv(sClient, revData, 255, 0);if (ret > 0){revData[ret] = 0x00;printf(revData);}const char * sendData = "hello, TCP Client";send(sClient, sendData, strlen(sendData), 0);closesocket(sClient);}closesocket(slisten);WSACleanup();return 0;
}

client.cpp

#include <WINSOCK2.H>
#include <STDIO.H>
#include <iostream>
#include <cstring>
using namespace std;
#pragma comment(lib, "ws2_32.lib")int main()
{WORD sockVersion = MAKEWORD(2,2);WSADATA data;if (WSAStartup(sockVersion, &data) != 0){return 0;}while (true){SOCKET sclient = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);if (sclient == INVALID_SOCKET){printf("Invalid socket!");return 0;}sockaddr_in serAddr;serAddr.sin_family = AF_INET;serAddr.sin_port = htons(8888);serAddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");if (connect(sclient, (sockaddr *)&serAddr, sizeof(serAddr)) == SOCKET_ERROR){printf("connect error!");closesocket(sclient);return 0;}string data;cin>>data;const char * sendData;sendData = data.c_str();send(sclient, sendData, strlen(sendData), 0);char recData[255];int ret = recv(sclient, recData, 255, 0);if (ret > 0){recData[ret] = 0x00;printf(recData);}closesocket(sclient);}WSACleanup();return 0;
}

3.4 客户端一直发送数据代码

server.cpp

#include <stdio.h>
#include <winsock2.h>#pragma comment(lib, "ws2_32.lib")int main(int argc, char* argv[])
{WORD sockVersion = MAKEWORD(2,2);WSADATA wsadata;if (WSAStartup(sockVersion, &wsadata) != 0){return 0;}SOCKET slisten = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);if (slisten == INVALID_SOCKET){printf("socket error!");return 0;}sockaddr_in sin;sin.sin_family = AF_INET;sin.sin_port = htons(6000);sin.sin_addr.S_un.S_addr = INADDR_ANY;if(bind(slisten, (LPSOCKADDR)&sin, sizeof(sin)) == SOCKET_ERROR){printf("bind error!");return 0;}if (listen(slisten, 5) == SOCKET_ERROR){printf("listen error!");return 0;}SOCKET sClient;sockaddr_in remoteAddr;int nAddrlen = sizeof(remoteAddr);char revData[255];while (true){printf("Waiting for connecting... \n");sClient = accept(slisten, (SOCKADDR *)&remoteAddr, &nAddrlen);if (sClient == SOCKET_ERROR){printf("accept error !");continue;}int ret = recv(sClient, revData, 255, 0);if (ret > 0 ){revData[ret] = 0x00;printf("Received: %s\n", revData);}const char * sendData = "Hello World from Server";send(sClient, sendData, strlen(sendData), 0);printf("Sent: %s\n", sendData);// Keep receiving messageswhile (true){ret = recv(sClient, revData, 255, 0);if (ret > 0){revData[ret] = 0x00;printf("Received: %s\n", revData);}}closesocket(sClient);}closesocket(slisten);WSACleanup();return 0;
}

client.cpp

#include <winsock2.h>
#include <stdio.h>
#include <iostream>
#include <cstring>#pragma comment(lib, "ws2_32.lib")int main()
{WORD sockVersion = MAKEWORD(2,2);WSADATA data;if(WSAStartup(sockVersion, &data) != 0){return 0;}SOCKET sclient = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);if(sclient == INVALID_SOCKET){printf("Invalid socket!");return 0;}sockaddr_in serAddr;serAddr.sin_family = AF_INET;serAddr.sin_port = htons(6000);serAddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");if (connect(sclient, (sockaddr *)&serAddr, sizeof(serAddr)) == SOCKET_ERROR){printf("connect error!");closesocket(sclient);return 0;}const char * sendData = "Hello World from Client";send(sclient, sendData, strlen(sendData), 0);printf("Sent: %s\n", sendData);char recData[255];int ret = recv(sclient, recData, 255, 0);if (ret > 0){recData[ret] = 0x00;printf("Received: %s\n", recData);}// Keep sending messageswhile (true){const char * helloData = "Hello";send(sclient, helloData, strlen(helloData), 0);printf("Sent: %s\n", helloData);// Sleep for 1 secondSleep(1000);}closesocket(sclient);WSACleanup();return 0;
}

3.5 用户在客户端输入数据代码

server.cpp

#include <stdio.h>
#include <winsock2.h>#pragma comment(lib, "ws2_32.lib")int main(int argc, char* argv[])
{WORD sockVersion = MAKEWORD(2,2);WSADATA wsadata;if (WSAStartup(sockVersion, &wsadata) != 0){return 0;}SOCKET slisten = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);if (slisten == INVALID_SOCKET){printf("socket error!");return 0;}sockaddr_in sin;sin.sin_family = AF_INET;sin.sin_port = htons(6000);sin.sin_addr.S_un.S_addr = INADDR_ANY;if(bind(slisten, (LPSOCKADDR)&sin, sizeof(sin)) == SOCKET_ERROR){printf("bind error!");return 0;}if (listen(slisten, 5) == SOCKET_ERROR){printf("listen error!");return 0;}SOCKET sClient;sockaddr_in remoteAddr;int nAddrlen = sizeof(remoteAddr);char revData[255];while (true){printf("Waiting for connecting... \n");sClient = accept(slisten, (SOCKADDR *)&remoteAddr, &nAddrlen);if (sClient == SOCKET_ERROR){printf("accept error !");continue;}// Keep receiving messageswhile (true){int ret = recv(sClient, revData, 255, 0);if (ret > 0){revData[ret] = 0x00;printf("Received: %s\n", revData);}else if (ret == 0) // Connection closed{printf("Client disconnected.\n");closesocket(sClient);break;}else if (ret == SOCKET_ERROR) // Error receiving{printf("recv error!\n");closesocket(sClient);break;}}}closesocket(slisten);WSACleanup();return 0;
}

client.cpp

#include <winsock2.h>
#include <stdio.h>
#include <iostream>
#include <cstring>#pragma comment(lib, "ws2_32.lib")int main()
{WORD sockVersion = MAKEWORD(2,2);WSADATA data;if(WSAStartup(sockVersion, &data) != 0){return 0;}SOCKET sclient = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);if(sclient == INVALID_SOCKET){printf("Invalid socket!");return 0;}sockaddr_in serAddr;serAddr.sin_family = AF_INET;serAddr.sin_port = htons(6000);serAddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");if (connect(sclient, (sockaddr *)&serAddr, sizeof(serAddr)) == SOCKET_ERROR){printf("connect error!");closesocket(sclient);return 0;}char sendData[255];while (true){std::cout << "Enter message: ";std::cin.getline(sendData, 255);if (strlen(sendData) == 0) {continue;}send(sclient, sendData, strlen(sendData), 0);printf("Sent: %s\n", sendData);}closesocket(sclient);WSACleanup();return 0;
}

4.在CLion建立客户端和服务端

修改CLion配置如下图

切换到此,点击运行即可进行通信

5. 运行效果

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com