一、服务端
#include <stdio.h>
#include <WinSock2.h>
#include "gyklib.h"
#include <Ws2tcpip.h>
#include <process.h>
#pragma comment(lib, "ws2_32.lib")
#pragma comment(lib, "gyklib.lib")
// 定义聊天室最大人数
#define MAX_CLI_NUM 256
// 定义消息最大值为1KB
#define MAX_MSG_SIZE 1024
// 当前聊天室人数
int current_cli_num = 0;
// 所有连接的数组
SOCKET* cli_socket_arr = new SOCKET[MAX_CLI_NUM]{ 0 };
// 创建一个互斥体
HANDLE hmutex = CreateMutex(NULL, FALSE, NULL);
// 发送消息的方法
void SendMsgToOther(SOCKET* socket, char* msg);
// 处理客户端连接的函数,多线程中使用
unsigned WINAPI HandleClient(void* arg);
int main()
{
printf("ChatRoom Server开启\n");
// 线程句柄
HANDLE hThread;
// 初始化socker网络库
InitSocketNet();
// 3、建立socket连接,AF_INET:表示ipv4协议
// SOCK_STREAM:表示TCP连接
SOCKET sockSrv = socket(AF_INET, SOCK_STREAM, 0);
if (INVALID_SOCKET == sockSrv)
{
printf("socket errorno = %d\n", GetLastError());
return -1;
}
// 配置连接参数
SOCKADDR_IN addrSrv;
addrSrv.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
addrSrv.sin_family = AF_INET;
addrSrv.sin_port = htons(6000);
// 绑定socket
if (SOCKET_ERROR == bind(sockSrv, (SOCKADDR*)&addrSrv, sizeof(SOCKADDR)))
{
printf("bind errorno = %d\n", GetLastError());
return -1;
}
// 4、监听 listen
if (SOCKET_ERROR == listen(sockSrv, 100))
{
printf("listen errorno = %d\n", GetLastError());
return -1;
}
// 客户端地址
SOCKADDR_IN addrCli;
int len = sizeof(SOCKADDR);
while (TRUE)
{
// 5、分配一台分机去处理客户端的连接
printf("等待客户端连接中...\n");
SOCKET sockConn = accept(sockSrv, (SOCKADDR*)&addrCli, &len);
// 等待互斥体变为有信号状态
WaitForSingleObject(hmutex, INFINITE);
cli_socket_arr[current_cli_num] = sockConn;
current_cli_num++;
char ip_str[INET6_ADDRSTRLEN];
const char* ip = InetNtop(AF_INET, &(addrCli.sin_addr), ip_str, INET_ADDRSTRLEN);
if (ip != NULL) {
printf("客户端:{%s}连接成功!\n", ip);
}
// 释放互斥体
ReleaseMutex(hmutex);
// 开启一个线程去处理这个连接的消息
hThread = (HANDLE)_beginthreadex(NULL, 0, &HandleClient, &sockConn, 0, NULL);
}
// 8、关闭总机
closesocket(sockSrv);
WSACleanup();
system("pause");
return 0;
}
unsigned WINAPI HandleClient(void* arg)
{
// 获取连接
SOCKET socket = *(SOCKET*)arg;
// 获取数据
char* clientMsg = new char[MAX_MSG_SIZE];
while (TRUE)
{
// 将消息置为0
memset(clientMsg, 0, MAX_MSG_SIZE);
// 获取消息
if (recv(socket, clientMsg, MAX_MSG_SIZE, 0) > 0)
{
printf("收到客户端消息:%s\n", clientMsg);
// 消息获取之后,需要发送给其他的人
SendMsgToOther(&socket, clientMsg);
}
else // 断开连接,跳出while循环
{
break;
}
}
// 等待互斥体变为有信号状态
WaitForSingleObject(hmutex, INFINITE);
// 移除数组中失效的连接
int currentIndex = current_cli_num;
for (int i = 0; i < current_cli_num; i++)
{
if (socket == cli_socket_arr[i])
{
currentIndex = i;
}
if (i == (current_cli_num - 1))
{
cli_socket_arr[i] = 0;
}
else if (i >= currentIndex)
{
cli_socket_arr[i] = cli_socket_arr[i + 1];
}
}
current_cli_num--;
// 释放互斥体
ReleaseMutex(hmutex);
// 销毁堆数据
delete[] clientMsg;
return 0;
}
void SendMsgToOther(SOCKET* socket, char* msg)
{
for (int i = 0; i < current_cli_num; i++)
{
SOCKET s = cli_socket_arr[i];
printf("s:%x socket:%x\n", s, *socket);
if (s != *socket)
{
SocketSend(s, msg, strlen(msg));
}
}
}
二、客户端
#include <string> // 包含strcpy_s函数的头文件
#include <stdio.h>
#include <stdlib.h>
#include <WinSock2.h>
#include <Ws2tcpip.h>
#include <windows.h>
#include "gyklib.h"
#include <process.h>
#include <ctype.h>
// 使用 #pragma comment(lib, "ws2_32.lib") 可以使你的代码更加自包含,因为你不需要依赖外部的项目设置。
#pragma comment(lib, "ws2_32.lib")
#pragma comment(lib, "gyklib.lib")
#define MAX_NAME_SIZE 255
#define MAX_MSG_SIZE 1024
// 用户的名称
char* name = new char[255] {"DEFAULT"};
// 用户输入的消息
char* msg = new char[MAX_MSG_SIZE];
// 要发送的数据
char* newMsg = new char[MAX_NAME_SIZE + MAX_MSG_SIZE];
// 处理收到的消息
unsigned HandleRecv(void* arg);
// 处理发送的消息
unsigned HandleSend(void* arg);
int main(int argc, char* argv[])
{
HANDLE hSendThread, hRecvThread;
if (argc != 2) {
MessageBox(0, "命令行启动,请添加启动参数!\n例如:./ChatRoomClient.exe [name]\n其中name表示你的名称\n", "警告", MB_ICONWARNING);
return 1; // 退出并返回错误码
}
// 获取聊天室昵称
errno_t err = strcpy_s(name, MAX_NAME_SIZE, argv[1]);
if (err != 0)
{
fputs("strcpy_s error\n", stdout);
}
// 初始化socker网络库
InitSocketNet();
// 3、建立socket连接,AF_INET:表示ipv4协议
// SOCK_STREAM:表示TCP连接
SOCKET sockCli = socket(AF_INET, SOCK_STREAM, 0);
if (INVALID_SOCKET == sockCli)
{
printf("socket errorno = %d\n", GetLastError());
return -1;
}
// 4、配置要连接的服务器
SOCKADDR_IN addrSrv;
memset(&addrSrv, 0, sizeof(addrSrv));
addrSrv.sin_family = AF_INET;
addrSrv.sin_port = htons(6000);
// 假设我们有一个 IP 地址的字符串表示
const char* ip_str = "192.168.1.9";
if (inet_pton(AF_INET, ip_str, &(addrSrv.sin_addr.s_addr)) <= 0) {
// 错误处理
printf("inet_pton errorno = %d\n", GetLastError());
return -1;
}
// 5、连接服务器
if (SOCKET_ERROR == connect(sockCli, (SOCKADDR*)&addrSrv, sizeof(SOCKADDR)))
{
printf("connect errorno = %d\n", GetLastError());
return -1;
}
// 创建线程监听服务器消息
hRecvThread = (HANDLE)_beginthreadex(NULL, 0, &HandleRecv, &sockCli, 0, NULL);
// 创建线程监听用户输入信息,并发送给服务器
hSendThread = (HANDLE)_beginthreadex(NULL, 0, &HandleSend, &sockCli, 0, NULL);
// 等待线程结束后,才结束主线程
WaitForSingleObject(hRecvThread, INFINITE);
WaitForSingleObject(hSendThread, INFINITE);
// 7、关闭套接字
closesocket(sockCli);
WSACleanup();
system("pause");
return 0;
}
unsigned HandleRecv(void* arg)
{
// 获取连接
SOCKET socket = *(SOCKET*)arg;
// 获取数据
char* msg = new char[MAX_NAME_SIZE + MAX_MSG_SIZE];
while (TRUE)
{
// 将消息置为0
memset(msg, 0, MAX_NAME_SIZE + MAX_MSG_SIZE);
// 获取消息
if (recv(socket, msg, MAX_NAME_SIZE + MAX_MSG_SIZE, 0) > 0)
{
// 将消息打印到控制台
fputs(msg, stdout);
}
else // 断开连接,跳出while循环
{
break;
}
}
return 0;
}
unsigned HandleSend(void* arg)
{
// 获取连接
SOCKET socket = *(SOCKET*)arg;
while (TRUE)
{
fgets(msg, MAX_NAME_SIZE + MAX_MSG_SIZE, stdin);
if (strlen(msg) == 2 && tolower(msg[0]) == 'q') {
fputs("退出聊天室!\n", stdout);
exit(0);
}
else
{
sprintf_s(newMsg, MAX_NAME_SIZE + MAX_MSG_SIZE, "[%s]:%s", name, msg);
SocketSend(socket, newMsg, strlen(newMsg));
}
}
return 0;
}
三、静态库gyklib
#include <WinSock2.h>
#include <stdio.h>
#include "gyklib.h"
#include <Windows.h>
#include <io.h>
// 确保在包含WinSock2.h之前定义这些宏
#define WIN32_LEAN_AND_MEAN
#define _WINSOCK_DEPRECATED_NO_WARNINGS
int SocketRecv(int sock, char* buf, int dataSize)
{
// 目前收到的数据量
int numsRecvSoFar = 0;
// 未收到的数据量
int numsRemainingToRecv = dataSize;
printf("enter SocketRecv\n");
// 循环接收数据
while (true)
{
int bytesRead = recv(sock, &buf[numsRecvSoFar], numsRemainingToRecv, 0);
printf("###bytesRead = %d, numsRecvSoFar = %d, numsRemainingToRecv = %d\n",
bytesRead, numsRecvSoFar, numsRemainingToRecv);
if (bytesRead == numsRemainingToRecv)
{
return 0;
}
else if (bytesRead > 0)
{
numsRecvSoFar += bytesRead;
numsRemainingToRecv -= bytesRead;
continue;
}
else if (bytesRead < 0 && errno == EAGAIN)
{
continue;
}
else
{
return -1;
}
}
}
int SocketSend(int sock, char* buf, int dataSize)
{
// 目前发送的数据量
int numsSendSoFar = 0;
// 未发送的数据量
int numsRemainingToSend = dataSize;
printf("enter SocketSend\n");
// 循环接收数据
while (true)
{
int bytesSend = send(sock, &buf[numsSendSoFar], numsRemainingToSend, 0);
printf("###bytesSend = %d, numsRecvSoFar = %d, numsRemainingToRecv = %d\n",
bytesSend, numsSendSoFar, numsRemainingToSend);
if (bytesSend == numsRemainingToSend)
{
return 0;
}
else if (bytesSend > 0)
{
numsSendSoFar += bytesSend;
numsRemainingToSend -= bytesSend;
continue;
}
else if (bytesSend < 0 && errno == EAGAIN)
{
continue;
}
else
{
return -1;
}
}
}
void AddToSystem(const char* appName)
{
HKEY hKEY;
char CurrentPath[MAX_PATH];
char SysPath[MAX_PATH];
long ret = 0;
LPSTR FileNewName;
LPSTR FileCurrentName;
DWORD type = REG_SZ;
DWORD size = MAX_PATH;
// regedit win + R
LPCTSTR Rgspath = "Software\\Microsoft\\Windows\\CurrentVersion\\Run";
// 获取系统目录
GetSystemDirectory(SysPath, size);
// 获取当前模块的完整路径
GetModuleFileName(NULL, CurrentPath, size);
// 复制文件
FileCurrentName = CurrentPath;
FileNewName = lstrcat(lstrcat(lstrcat(SysPath, "\\"), appName), ".exe");
struct _finddata_t Steal;
if (_findfirst(FileNewName, &Steal) != -1)
{
MessageBox(0, "该程序已经安装过了!", "消息提示", NULL);
return;
}
int ihow = MessageBox(0, "该程序只允许用于合法的用途!\n继续运行该程序将使这台机器处于被监控的状态!\n如果您不想这样,请按“取消”按钮退出。\n按下“是”按钮该程序将被复制到您的机器上,并随系统启动自动 运行。\n按下“否”按钮,程序只运行一次,不会在您的系统内留下任何东西。", "警告", MB_YESNOCANCEL | MB_ICONWARNING | MB_TOPMOST);
if (ihow == IDCANCEL) exit(0);
if (ihow == IDNO) return;
// 复制文件到系统目录,并添加到注册表中
printf("FileCurrentName: %s\nFileNewName: %s\n", FileCurrentName, FileNewName);
ret = CopyFile(FileCurrentName, FileNewName, TRUE);
if (!ret)
{
MessageBox(0, "复制程序失败,请重试!", "错误", NULL);
return;
}
// 加入注册表
ret = RegOpenKeyEx(HKEY_LOCAL_MACHINE, Rgspath, 0, KEY_WRITE, &hKEY);
if (ret != ERROR_SUCCESS)
{
RegCloseKey(hKEY);
return;
}
// 设置Key
ret = RegSetValueEx(hKEY, appName, NULL, type, (const unsigned char*)FileNewName, size);
if (ret != ERROR_SUCCESS)
{
RegCloseKey(hKEY);
return;
}
RegCloseKey(hKEY);
}
void HideMyself()
{
HWND hwnd = GetForegroundWindow();
ShowWindow(hwnd, SW_HIDE);
}
int InitSocketNet() {
// 1、初始化网络库
WORD wVersionRequested;
WSADATA wsaData;
int err;
wVersionRequested = MAKEWORD(2, 2);
// 2、初始化套接字库
err = WSAStartup(wVersionRequested, &wsaData);
if (err != 0)
{
printf("WSAStartup errorNum = %d\n", GetLastError());
return err;
}
if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2)
{
printf("LOBYTE errorNum = %d\n", GetLastError());
WSACleanup();
return -1;
}
}