使用多线程来实现可与多个客户端通信的服务端。
当客户端连接上服务端之后,为该客户端创建一个新的线程,在该线程中与客户端进行通信。服务端程序中的主线程负责监听并接受客户端的连接请求,创建与客户端通信的线程。
另外,这是一个在windows下实现的回音(客户端发啥服务端就回传给客户端啥)服务端。
服务端
注意下多线程下共享内存,还有printf
的使用,需要线程之间同步,共享资源同一时间只能有一个线程使用。
#include <stdio.h>
#include <windows.h>
#include <process.h>
#pragma comment(lib,"ws2_32.lib")
#define SERVER_IP "127.0.0.1"
#define SERVER_PORT 1234
#define MAX_CLIENT_NUMS 50U
SOCKET serverSocket = { 0 };
SOCKADDR_IN serverSocketAddr = { 0 };
volatile unsigned int currentClientNums = { 0 };
typedef struct
{
char isOnline;
SOCKET clientSocket;
SOCKADDR_IN clientSocketAddr;
HANDLE threadFunc;
}CLIENT_t, * PCLIENT_t;
CLIENT_t client[MAX_CLIENT_NUMS] = { 0 };
void ClientThreadFunc(void*);
#define RECEIVE_BUFF_SIZE 200U
CRITICAL_SECTION cs_printf;
CRITICAL_SECTION cs_currentClientNums;
int main()
{
WSADATA wsadata = { 0 };
if (WSAStartup(MAKEWORD(2, 2), &wsadata) != 0)
{
printf("初始化DLL失败!\n");
return 0;
}
serverSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (serverSocket == INVALID_SOCKET)
{
printf("创建服务端TCP套接字失败,错误代码为:%d\n", WSAGetLastError());
goto END;
}
serverSocketAddr.sin_family = AF_INET;
serverSocketAddr.sin_addr.S_un.S_addr = inet_addr(SERVER_IP);
serverSocketAddr.sin_port = htons(SERVER_PORT);
if (bind(serverSocket, &serverSocketAddr, sizeof(serverSocketAddr)) == SOCKET_ERROR)
{
printf("绑定服务端套接字失败,错误代码为:%d\n", WSAGetLastError());
goto END;
}
if (listen(serverSocket, SOMAXCONN) == SOCKET_ERROR)
{
printf("服务端进入监听状态失败,错误代码为:%d\n", WSAGetLastError());
goto END;
}
if (!InitializeCriticalSectionAndSpinCount(&cs_printf, 4000))
{
printf("关键段(临界区)cs_printf初始化失败!\n");
DeleteCriticalSection(&cs_printf);
goto END;
}
InitializeCriticalSection(&cs_currentClientNums);
printf("服务端已经开启,IP:%s,端口:%d\n", inet_ntoa(serverSocketAddr.sin_addr), ntohs(serverSocketAddr.sin_port));
while (1)
{
static SOCKET clientSocket = { 0 };
static SOCKADDR_IN clientSocketAddr = { 0 };
static unsigned int cLength = sizeof(clientSocketAddr);
static unsigned int temp = { 0 };
clientSocket = accept(serverSocket, &clientSocketAddr, &cLength);
if (clientSocket == SOCKET_ERROR)
{
EnterCriticalSection(&cs_printf);
printf("客户端连接错误,错误代码为:%d\n", WSAGetLastError());
LeaveCriticalSection(&cs_printf);
continue;
}
else
{
EnterCriticalSection(&cs_currentClientNums);
temp = currentClientNums + 1;
LeaveCriticalSection(&cs_currentClientNums);
if (temp > MAX_CLIENT_NUMS)
{
send(clientSocket, "连接已断开,已经超过可连接的最大数量!\n", strlen("连接已断开,已经超过可连接的最大数量!\n"), 0);
EnterCriticalSection(&cs_printf);
printf("有新的客户端连接请求,IP:%s,端口:%d,但已经超过可连接的最大数量!\n", inet_ntoa(clientSocketAddr.sin_addr), ntohs(clientSocketAddr.sin_port));
LeaveCriticalSection(&cs_printf);
closesocket(clientSocket);
}
else
{
for (size_t i = 0; i < MAX_CLIENT_NUMS; i++)
{
if (client[i].isOnline == 0)
{
client[i].isOnline = 1;
client[i].clientSocket = clientSocket;
memcpy(&client[i].clientSocketAddr, &clientSocketAddr, sizeof(clientSocketAddr));
client[i].threadFunc = _beginthread(ClientThreadFunc, 0, &client[i]);
EnterCriticalSection(&cs_currentClientNums);
EnterCriticalSection(&cs_printf);
currentClientNums++;
printf("当前已有 %d 个客户端连接,最多可连接 %d 个客户端\n", currentClientNums, MAX_CLIENT_NUMS);
LeaveCriticalSection(&cs_printf);
LeaveCriticalSection(&cs_currentClientNums);
break;
}
}
}
}
}
DeleteCriticalSection(&cs_currentClientNums);
END:
closesocket(serverSocket);
WSACleanup(wsadata);
return 0;
}
void ClientThreadFunc(void* arg)
{
PCLIENT_t client = (PCLIENT_t)arg;
char receiveBuff[RECEIVE_BUFF_SIZE] = { 0 };
int result = { 0 };
unsigned short clientPort = ntohs(client->clientSocketAddr.sin_port);
IN_ADDR clientIP = client->clientSocketAddr.sin_addr;
EnterCriticalSection(&cs_printf);
printf("有新的客户端连接,IP:%s,端口:%d\n", inet_ntoa(clientIP), clientPort);
LeaveCriticalSection(&cs_printf);
send(client->clientSocket, "Hello i am server\n", strlen("Hello i am server\n"), 0);
while (1)
{
result = recv(client->clientSocket, receiveBuff, RECEIVE_BUFF_SIZE, 0);
if (result > 0)
{
if (result >= RECEIVE_BUFF_SIZE)
{
receiveBuff[RECEIVE_BUFF_SIZE - 1] = '\0';
}
else
{
receiveBuff[result] = '\0';
}
send(client->clientSocket, receiveBuff, strlen(receiveBuff), 0);
EnterCriticalSection(&cs_printf);
printf("收到来自IP:%s,端口:%d客户端的信息:%s\n", inet_ntoa(clientIP), clientPort, receiveBuff);
LeaveCriticalSection(&cs_printf);
}
else if (result == 0)
{
EnterCriticalSection(&cs_printf);
printf("客户端断开连接,IP:%s,端口:%d\n", inet_ntoa(clientIP), clientPort);
LeaveCriticalSection(&cs_printf);
break;
}
else
{
EnterCriticalSection(&cs_printf);
printf("与客户端通信时发生异常,IP:%s,端口:%d\n", inet_ntoa(clientIP), clientPort);
LeaveCriticalSection(&cs_printf);
break;
}
}
closesocket(client->clientSocket);
client->isOnline = 0;
EnterCriticalSection(&cs_currentClientNums);
currentClientNums--;
LeaveCriticalSection(&cs_currentClientNums);
_endthread();
}
客户端
#include <stdio.h>
#include <windows.h>
#include <process.h>
#pragma comment(lib,"ws2_32.lib")
#define SERVER_IP "127.0.0.1"
#define SERVER_PORT 1234
SOCKADDR_IN serverSocketAddr = { 0 };
SOCKET clientSocket = { 0 };
#define BUFF_SIZE 200U
char sendBuff[BUFF_SIZE] = { 0 };
char receiveBuff[BUFF_SIZE] = { 0 };
void RecevieThreadFunc(void*);
volatile char isOnline = { 0 };
CRITICAL_SECTION cs_printf;
CRITICAL_SECTION cs_isOnline;
int main()
{
WSADATA wsadata = { 0 };
if (WSAStartup(MAKEWORD(2, 2), &wsadata) != 0)
{
printf("初始化DLL失败!\n");
return 0;
}
clientSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (clientSocket == INVALID_SOCKET)
{
printf("创建客户端TCP套接字失败,错误代码为:%d\n", WSAGetLastError());
goto END;
}
serverSocketAddr.sin_family = AF_INET;
serverSocketAddr.sin_addr.S_un.S_addr = inet_addr(SERVER_IP);
serverSocketAddr.sin_port = htons(SERVER_PORT);
if (connect(clientSocket, &serverSocketAddr, sizeof(serverSocketAddr)) == SOCKET_ERROR)
{
printf("连接服务端失败,错误代码为:%d\n", WSAGetLastError());
goto END;
}
if (!InitializeCriticalSectionAndSpinCount(&cs_printf, 4000))
{
printf("关键段(临界区)cs_printf初始化失败!\n");
DeleteCriticalSection(&cs_printf);
goto END;
}
InitializeCriticalSection(&cs_isOnline);
printf("已经连接上服务端,服务端IP:%s,端口:%d\n", inet_ntoa(serverSocketAddr.sin_addr), ntohs(serverSocketAddr.sin_port));
isOnline = 1;
_beginthread(RecevieThreadFunc, 0, NULL);
while (1)
{
EnterCriticalSection(&cs_isOnline);
if (!isOnline)
{
LeaveCriticalSection(&cs_isOnline);
goto END;
}
LeaveCriticalSection(&cs_isOnline);
EnterCriticalSection(&cs_printf);
printf("请输入需要发送至服务端的数据:\n");
LeaveCriticalSection(&cs_printf);
scanf("%s", sendBuff);
send(clientSocket, sendBuff, strlen(sendBuff), 0);
Sleep(100);
}
DeleteCriticalSection(&cs_isOnline);
END:
closesocket(clientSocket);
WSACleanup(wsadata);
return 0;
}
void RecevieThreadFunc(void* arg)
{
int result = { 0 };
while (1)
{
result = recv(clientSocket, receiveBuff, BUFF_SIZE, 0);
if (result > 0)
{
if (result >= BUFF_SIZE)
{
receiveBuff[BUFF_SIZE - 1] = '\0';
}
else
{
receiveBuff[result] = '\0';
}
EnterCriticalSection(&cs_printf);
printf("收到来自服务端的信息:%s\n", receiveBuff);
LeaveCriticalSection(&cs_printf);
}
else if (result == 0)
{
EnterCriticalSection(&cs_isOnline);
EnterCriticalSection(&cs_printf);
printf("服务端断开连接\n");
isOnline = 0;
LeaveCriticalSection(&cs_printf);
LeaveCriticalSection(&cs_isOnline);
break;
}
else
{
EnterCriticalSection(&cs_printf);
printf("与服务端通信时发生异常\n");
LeaveCriticalSection(&cs_printf);
break;
}
}
closesocket(clientSocket);
_endthread();
}
结果
服务端
客户端
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)