C++控制台实现客户端与服务端即时通信(C/S)

简介: C++控制台实现客户端与服务端即时通信(C/S)

运用知识


这次的知识都在源代码中 可结合查看


ps:通信只设定了五次,do while换成while(1)再判断一下是否退出即可


源代码


服务器


// server.cpp
#include <iostream>
#include <cstdio>
#include <Winsock2.h>
#include<string.h>
#pragma comment(lib, "ws2_32.lib")
using namespace std;
int main()
{
  // 加载socket动态链接库(dll)
  WORD wVersionRequested;
  WSADATA wsaData;  // 这结构是用于接收Windows Socket的结构信息的
  int err;
  wVersionRequested = MAKEWORD(1, 1); // 请求1.1版本的WinSock库
  err = WSAStartup(wVersionRequested, &wsaData);
  if (err != 0) {
    return -1;      // 返回值为零的时候是表示成功申请WSAStartup
  }
  if (LOBYTE(wsaData.wVersion) != 1 || HIBYTE(wsaData.wVersion) != 1) {
    // 检查这个低字节是不是1,高字节是不是1以确定是否我们所请求的1.1版本
    // 否则的话,调用WSACleanup()清除信息,结束函数
    WSACleanup();
    return -1;
  }
  // 创建socket操作,建立流式套接字,返回套接字号sockSrv
  // SOCKET socket(int af, int type, int protocol);
  // 第一个参数,指定地址簇(TCP/IP只能是AF_INET,也可写成PF_INET)
  // 第二个,选择套接字的类型(流式套接字)
  // 第三个,特定地址家族相关协议(0为自动)
  SOCKET sockSrv = socket(AF_INET, SOCK_STREAM, 0);
  // 套接字sockSrv与本地地址相连
  // int bind(SOCKET s, const struct sockaddr* name, int namelen);
  // 第一个参数,指定需要绑定的套接字;
  // 第二个参数,指定该套接字的本地地址信息,该地址结构会随所用的网络协议的不同而不同
  // 第三个参数,指定该网络协议地址的长度
  // PS: struct sockaddr{ u_short sa_family; 
  //            char sa_data[14];};
  //                      sa_family指定该地址家族, sa_data起到占位占用一块内存分配区的作用
  //     在TCP/IP中,可使用sockaddr_in结构替换sockaddr,以方便填写地址信息
  // 
  //     struct sockaddr_in{ short sin_family; unsigned short sin_port; struct in_addr sin_addr; char sin_zero[8];};
  //     sin_family表示地址族,对于IP地址,sin_family成员将一直是AF_INET。
  //     sin_port指定将要分配给套接字的端口。
  //     sin_addr给出套接字的主机IP地址。
  //     sin_zero[8]给出填充数,让sockaddr_in与sockaddr结构的长度一样。
  //     将IP地址指定为INADDR_ANY,允许套接字向任何分配给本地机器的IP地址发送或接收数据。
  //     如果想只让套接字使用多个IP中的一个地址,可指定实际地址,用inet_addr()函数。
  SOCKADDR_IN addrSrv;
  addrSrv.sin_addr.S_un.S_addr = htonl(INADDR_ANY); // 将INADDR_ANY转换为网络字节序,调用 htonl(long型)或htons(整型)
  addrSrv.sin_family = AF_INET;
  addrSrv.sin_port = htons(6000);
  bind(sockSrv, (SOCKADDR*)&addrSrv, sizeof(SOCKADDR)); // 第二参数要强制类型转换
  // 将套接字设置为监听模式(连接请求), listen()通知TCP服务器准备好接收连接
  // int listen(SOCKET s,  int backlog);
  // 第一个参数指定需要设置的套接字,第二个参数为(等待连接队列的最大长度)
  listen(sockSrv, 10);
  // accept(),接收连接,等待客户端连接
  // SOCKET accept(  SOCKET s,  struct sockaddr* addr,  int* addrlen);
  // 第一个参数,接收一个处于监听状态下的套接字
  // 第二个参数,sockaddr用于保存客户端地址的信息
  // 第三个参数,用于指定这个地址的长度
  // 返回的是向与这个监听状态下的套接字通信的套接字
  // 客户端与用户端进行通信
  // send(), 在套接字上发送数据
  // int send( SOCKET s,  const char* buf,  int len,  int flags);
  // 第一个参数,需要发送信息的套接字,
  // 第二个参数,包含了需要被传送的数据,
  // 第三个参数是buffer的数据长度,
  // 第四个参数,一些传送参数的设置
  // recv(), 在套接字上接收数据
  // int recv(  SOCKET s,  char* buf,  int len,  int flags);
  // 第一个参数,建立连接后的套接字,
  // 第二个参数,接收数据
  // 第三个参数,接收数据的长度,
  // 第四个参数,一些传送参数的设置
  SOCKADDR_IN  addrClient;
  int len = sizeof(SOCKADDR);
  while (true){ // 不断等待客户端请求的到来
    SOCKET sockConn = accept(sockSrv, (SOCKADDR*)&addrClient, &len);
    char sendBuf[100];
    sprintf(sendBuf, "Welcome %s to the server program~ \nNow, let's start talking...\n", inet_ntoa(addrClient.sin_addr));
    send(sockConn, sendBuf, strlen(sendBuf) + 1, 0);  // 发送显示欢迎信息
    char recvBuf[100];
    recv(sockConn, recvBuf, 100, 0);
    printf("%s\n", recvBuf);    // 接收第一次信息
    char * sockConnName = "Client";
    printf("我们可以聊五句话");
    int n = 5;
    while (n--){
      printf("还剩%d次:\n", n + 1);
      char recvBuf[100];
      recv(sockConn, recvBuf, 100, 0);
      printf("%s Says: %s\n", sockConnName, recvBuf);   // 接收信息
      char talk[100];
      printf("Please enter what you want to say next(\"quit\"to exit):");
      gets(talk);
      send(sockConn, talk, strlen(talk) + 1, 0);      // 发送信息
      printf("\n");
    }
    printf("\nEnd talking... \n");
    closesocket(sockConn);
  }
  printf("\n");
  system("pause");
  return 0;
}


客户端


// client.cpp
#include <iostream>
#include <cstdio>
#include <Winsock2.h>
#include<string.h>
using namespace std;
#pragma comment(lib, "ws2_32.lib")
int main()
{
  // 加载socket动态链接库(dll)
  WORD wVersionRequested;
  WSADATA wsaData;  // 这结构是用于接收Wjndows Socket的结构信息的
  int err;
  wVersionRequested = MAKEWORD(1, 1); // 请求1.1版本的WinSock库
  err = WSAStartup(wVersionRequested, &wsaData);
  if (err != 0) {
    return -1;      // 返回值为零的时候是表示成功申请WSAStartup
  }
  if (LOBYTE(wsaData.wVersion) != 1 || HIBYTE(wsaData.wVersion) != 1) {
    // 检查这个低字节是不是1,高字节是不是1以确定是否我们所请求的1.1版本
    // 否则的话,调用WSACleanup()清除信息,结束函数
    WSACleanup();
    return -1;
  }
  // 创建socket操作,建立流式套接字,返回套接字号sockClient
  // SOCKET socket(int af, int type, int protocol);
  // 第一个参数,指定地址簇(TCP/IP只能是AF_INET,也可写成PF_INET)
  // 第二个,选择套接字的类型(流式套接字),第三个,特定地址家族相关协议(0为自动)
  SOCKET sockClient = socket(AF_INET, SOCK_STREAM, 0);
  // 将套接字sockClient与远程主机相连
  // int connect( SOCKET s,  const struct sockaddr* name,  int namelen);
  // 第一个参数:需要进行连接操作的套接字
  // 第二个参数:设定所需要连接的地址信息
  // 第三个参数:地址的长度
  SOCKADDR_IN addrSrv;
  addrSrv.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");    // 本地回路地址是127.0.0.1; 
  addrSrv.sin_family = AF_INET;
  addrSrv.sin_port = htons(6000);
  connect(sockClient, (SOCKADDR*)&addrSrv, sizeof(SOCKADDR));
  char recvBuf[100];
  recv(sockClient, recvBuf, 100, 0);
  printf("%s\n", recvBuf);
  send(sockClient, "Attention: A Client has enter...\n", strlen("Attention: A Client has enter...\n") + 1, 0);
  printf("我们可以聊五句话");
  int n = 5;
  do{
    printf("\n还剩%d次:", n);
    char talk[100];
    printf("\nPlease enter what you want to say next(\"quit\"to exit):");
    gets(talk);
    send(sockClient, talk, strlen(talk) + 1, 0);      // 发送信息
    char recvBuf[100];
    recv(sockClient, recvBuf, 100, 0);
    printf("%s Says: %s\n", "Server", recvBuf);   // 接收信息
  } while (--n);
  printf("End linking...\n");
  closesocket(sockClient);
  WSACleanup(); // 终止对套接字库的使用
  printf("\n");
  system("pause");
  return 0;
}
相关文章
|
5月前
|
消息中间件 Java C语言
消息队列 MQ使用问题之在使用C++客户端和GBase的ESQL进行编译时出现core dump,该怎么办
消息队列(MQ)是一种用于异步通信和解耦的应用程序间消息传递的服务,广泛应用于分布式系统中。针对不同的MQ产品,如阿里云的RocketMQ、RabbitMQ等,它们在实现上述场景时可能会有不同的特性和优势,比如RocketMQ强调高吞吐量、低延迟和高可用性,适合大规模分布式系统;而RabbitMQ则以其灵活的路由规则和丰富的协议支持受到青睐。下面是一些常见的消息队列MQ产品的使用场景合集,这些场景涵盖了多种行业和业务需求。
|
6月前
|
缓存 网络协议 Linux
c++实战篇(三) ——对socket通讯服务端与客户端的封装
c++实战篇(三) ——对socket通讯服务端与客户端的封装
149 0
|
6月前
|
定位技术 C++ Windows
第一人称射击游戏 C++控制台版(未完成)
第一人称射击游戏 C++控制台版(未完成)
第一人称射击游戏 C++控制台版(未完成)
|
6月前
|
C++
俄罗斯方块 C++控制台版
俄罗斯方块 C++控制台版
|
7月前
|
C++
c++实现通讯录管理系统(控制台版)
c++实现通讯录管理系统(控制台版)
|
7月前
C++socket客户端select异步连接发送接收数据
C++socket客户端select异步连接发送接收数据
132 0
|
7月前
|
Web App开发 网络协议 Linux
Linux C/C++ 开发(学习笔记十 ):实现http请求器(TCP客户端)
Linux C/C++ 开发(学习笔记十 ):实现http请求器(TCP客户端)
264 0
|
7月前
|
数据安全/隐私保护 C++
c++实现http客户端和服务端的开源库以及Base64加密密码
c++实现http客户端和服务端的开源库以及Base64加密密码
103 0
|
网络协议 C++ Python
Qt+C++ TCP发送接收信息客户端与服务端窗体
Qt+C++ TCP发送接收信息客户端与服务端窗体
148 0
Qt+C++ TCP发送接收信息客户端与服务端窗体
|
7月前
|
Java
java实战项目超市管理系统控制台版
java实战项目超市管理系统控制台版
下一篇
DataWorks