我有这个很长的代码中,我在实现你类似的事情过去写的,但它的CPP,如果你想太多,我会在这里发布,但基本上使用消息队列或者多流程规划似乎有点对我没用,如果我是你,我会以下列方式将其编程
客户端代码 - >两个线程 Server代码 - >两个线程
Client
有两个线程,和三种功能,Connect
/Send
/Receive
Connect
- 这个函数处理到服务器的连接,无论您是使用TCP它所处理的听,接受,或者如果你使用一些基于UDP由协议,它只是处理它 - 确保你有一些connectin
Send
- 该函数一些数据发送到服务器
Receive
这functino从服务器接收数据
的Client
的流动将是以下:
Client
连接到服务器上Main thread
- 连接到服务器
Client
之后创建Second thread
- 论
Main thread
- Client
进入一些循环,它从用户读取的数据作为输入,然后调用Send
功能,并将其发送到服务器
- 在
Second thread
- Client
进入某个循环,调用Receive
从服务器接收数据并在接收到数据时打印它VED
,处理Client
,现在大概Server
Server
- 有三个功能,有一种全局数据结构,称为链接 - 这将通过它的所有线程共享目录(链表明显),WaitForConnection
Receive
SendToAll
WaitForConnection
- 简单地调用socket API对“接受”功能(如果你使用TCP),或者如果你使用的是其他由协议,这个功能菊st阻止它尝试等待传入连接,当某个连接到达时,此函数将连接注册到名为Connection-List
的所有连接的全局链接列表中,并带有适当的套接字和客户端数据
SendToAll
- 只需重复所有Connection-List
并为该列表中的每个连接,它发送一些数据通过
Receive
只是收到一些数据,但设置一些超时首先,这是非常重要的,以便接收不会阻止太久!
的Server
,流量为以下几点:
Main thread
及牡丹Second thread
Main thread
进入一些循环,从而不断地得到连接,并将它们添加到Connection-List
- 内调用
WaitForConnection
Second thread
进入某个循环,该循环通过Connection-List
进行迭代,对于Connection-List
内的每个连接,接收在相应的插座被调用时,如果一些数据是接收,SendToAll
调用与所接收的数据,如果超时,没有任何反应,该循环继续且之后执行下一个循环迭代
SendToAll
将内Connection-List
这是一些非常简单的多客户与广播服务器架构的数据到所有客户端应该是很容易实现!我希望这可以帮助你
我提前道歉,因为这是我写的前一阵子所以它有很多内它筑巢的代码,很多评论!
-------------------------客户端代码----------------
// Main.cpp
#include <thread>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <iostream>
#pragma comment (lib, "Ws2_32.lib")
#define NAME_LENGTH 40
#define DEFAULT_BUFLEN 512
#define DEFAULT_PORT "8888"
void Receive(SOCKET* pscktConnection);
void Send(SOCKET* pscktConnection);
void main()
{
// Variable definition
int nResult;
int nNameLength = 0;
char pcNameBuffer[NAME_LENGTH];
SOCKET sckConnection = NULL;
WSADATA wsaData;
addrinfo addrAddressFormat;
addrinfo* paddrServerAddress;
// Code section
// Initialize Winsock
nResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
// If winsock dll loading has failed
if (nResult != 0)
{
std::cout << "Failed loading winsock DLL" << std::endl;
}
// DLL loaded successfully
else
{
//Setup connection info
ZeroMemory(&addrAddressFormat, sizeof(addrAddressFormat));
addrAddressFormat.ai_family = AF_INET;
addrAddressFormat.ai_socktype = SOCK_STREAM;
addrAddressFormat.ai_protocol = IPPROTO_TCP;
// Resolve the server address and port with the address setting into our final address
nResult = getaddrinfo("10.0.0.5", DEFAULT_PORT, &addrAddressFormat, &paddrServerAddress);
// Address resolving has failed
if (nResult != 0)
{
std::cout << "Some error has occured during connection establishment" << std::endl;
}
else
{
// Request user for his name
pcNameBuffer[0] = '\0';
std::cout << "PLEASE ENTER YOUR NAME -> ";
std::cin.getline(pcNameBuffer, NAME_LENGTH);
std::cout << std::endl << std::endl ;
// Creating the socket
sckConnection = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
// Connecting
nResult = connect(sckConnection, paddrServerAddress->ai_addr, (int)paddrServerAddress->ai_addrlen);
// Creating of the socket has failed
if (nResult == SOCKET_ERROR)
{
std::cout << "Creating of the socket has failed" << std::endl;
}
// Send server user's name
else
{
// Measure the name length
while (pcNameBuffer[nNameLength] != '\0')
{
++nNameLength;
}
// If invalid name
if (nNameLength == 0)
{
pcNameBuffer[0] = 'G';
pcNameBuffer[1] = 'U';
pcNameBuffer[2] = 'E';
pcNameBuffer[3] = 'S';
pcNameBuffer[4] = 'T';
pcNameBuffer[5] = '\0';
nNameLength = 6;
}
nResult = send(sckConnection, pcNameBuffer, nNameLength + 1, 0);
// An error has occured while sending server the user's name
if (nResult <= 0)
{
std::cout << "Some error has occured" << std::endl;
}
// Good to go
else
{
std::thread Read(Receive, &sckConnection);
Send(&sckConnection);
}
}
}
}
// cleanup resources
WSACleanup();
}
/*
* [Description]: This method is used only to read messages from server and print them
* [Paramaters]:
* pscktServerSocket - The address of the our connection socket
* [Return Value]: none
*/
void Receive(SOCKET* pscktConnection)
{
// Variable definition
int nReceivedBytes;
int nBufferLen = DEFAULT_BUFLEN;
char pcBuffer[DEFAULT_BUFLEN];
// Code section
// Keep this operation running constantly
while (true)
{
// Read from server -- NO TIME OUT NEEDED
nReceivedBytes = recv((*pscktConnection), pcBuffer, nBufferLen, 0);
// More than zero bytes received
if (nReceivedBytes > 0)
{
// Set a zero termination to simulate a string
pcBuffer[nReceivedBytes] = '\0';
std::cout << pcBuffer << std::endl;
}
// Server has closed the connection probably
else
{
// TODO - CLOSE CONNECTION
}
}
}
/*
* [Description]: This method is used only to send messages to the server
* [Paramaters]:
* pscktServerSocket - The address of the our connection socket
* [Return Value]: none
*/
void Send(SOCKET* pscktConnection)
{
// Variable definition
int nSentBytes;
int nBufferLen = DEFAULT_BUFLEN;
char pcBuffer[DEFAULT_BUFLEN];
// Code section
pcBuffer[0] = '\0';
// Keep this operation running constantly
while (true)
{
int nSentBytes = 0;
// Read
std::cin.getline(pcBuffer, nBufferLen);
// Go through string untill backslash 0
while (pcBuffer[nSentBytes] != '\0')
{
// Increase the number of bytes we want to send
++nSentBytes;
}
pcBuffer[nSentBytes] = '\0';
nSentBytes = send((*pscktConnection), pcBuffer, nSentBytes, 0);
// An error has occured
if (nSentBytes == SOCKET_ERROR)
{
// TODO - HANDLE ERROR;
}
}
}
`
-------------------------服务器代码----------- -----
// Source.cpp
#include <thread>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <iostream>
#include "Client.h"
#include "Connections.h"
#pragma comment (lib, "Ws2_32.lib")
#define NAME_LENGTH 40
#define DEFAULT_BUFLEN 512
#define DEFAULT_PORT "8888"
#define MAX_CONNECTIONS 5
// Globals
Connections* conAllConnections = Connections::getInstance();
bool LoadServerSocket(SOCKET* pscktServerSocket);
void Dispatcher(SOCKET* pscktServerSocket);
void SendAll(ClientNode* pclndSenderAddress, char* pcMessageBuffer, int nLength);
void HandleConnections();
void main()
{
// Variable definition
int nResult;
SOCKET sckServerSocket = NULL;
WSADATA wsaData;
// Code section
// Initialize Winsock
nResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
// If winsock dll loading has failed
if (nResult != 0)
{
std::cout << "Failed loading winsock DLL" << std::endl;
}
// DLL loaded successfully
else
{
// If failed loading the server socket
if (!LoadServerSocket(&sckServerSocket))
{
std::cout << "Failed loading the server socket!" << std::endl;
}
else
{
std::thread dispatch(Dispatcher,&sckServerSocket);
//dispatch.join();
HandleConnections();
}
}
// cleanup resources
WSACleanup();
}
/*
* [Description]: This method is used to load and bind server socket into some pointer.
* [Paramaters]:
* pscktServerSocket - a pointer variable that we would like to load our socket into the address this pointer
* is pointing at
* [Return Value]: A boolean indication of whether our socket was created successfully
*/
bool LoadServerSocket(SOCKET* pscktServerSocket)
{
// Variable definition
int nResult;
bool bWasServerSocketCreated = false;
bool bWasAddressResolved = false;
addrinfo addrAddressFormat;
addrinfo* paddrFinalAddress = NULL;
// Code section
// Fil addrAddressFormat with zeros, and set correct settings of our desired socket
ZeroMemory(&addrAddressFormat, sizeof(addrAddressFormat));
addrAddressFormat.ai_family = AF_INET;
addrAddressFormat.ai_socktype = SOCK_STREAM;
addrAddressFormat.ai_protocol = IPPROTO_TCP;
//addrAddressFormat.ai_flags = AI_PASSIVE;
// Resolve the server address and port with the address setting into our final address
nResult = getaddrinfo("10.0.0.5", DEFAULT_PORT, &addrAddressFormat, &paddrFinalAddress);
// If resolving of the address was successful
if (nResult == 0)
{
// Set address resolving bool indication to true
bWasAddressResolved = true;
// Create server socket
(*pscktServerSocket) = socket(paddrFinalAddress->ai_family,
paddrFinalAddress->ai_socktype,
paddrFinalAddress->ai_protocol);
// Socket creating was successful
if ((*pscktServerSocket) != INVALID_SOCKET)
{
// Set socket creation indication to true
bWasServerSocketCreated = true;
// Bind our socket into our address
nResult = bind((*pscktServerSocket),
paddrFinalAddress->ai_addr,
(int)paddrFinalAddress->ai_addrlen);
// In case binding failed
if (nResult == SOCKET_ERROR)
{
closesocket((*pscktServerSocket));
bWasServerSocketCreated = false;
}
}
}
// Freeing resources
if (bWasAddressResolved)
{
freeaddrinfo(paddrFinalAddress);
}
return (bWasServerSocketCreated);
}
/*
* [Description]: This uses the loaded server socket and handles incoming requests for connections
* [Paramaters]:
* pscktServerSocket - a pointer to the loaded server socket
* [Return Value]: none
*/
void Dispatcher(SOCKET* pscktServerSocket)
{
// Variable definition
int nResult;
char pcBuffer[NAME_LENGTH];
DWORD timeout = 1500;
SOCKET sckClientSocket;
Client clntNewClient;
// Code section
// Keep this running constantly
while (true)
{
// Keep this running as long as we have the sufficient amount of connections
while (conAllConnections->getNumOfConnections() < MAX_CONNECTIONS)
{
// Attempt listening on the server socket
nResult = listen((*pscktServerSocket), MAX_CONNECTIONS);
// Listening was a failure
if (nResult == SOCKET_ERROR)
{
std::cout << "Failed listening with the server socket" << std::endl;
// HANDLE ERROR - TODO
}
// Listening was successful
else
{
std::cout << "Listening...." << std::endl;
// Accept a client socket
sckClientSocket = accept((*pscktServerSocket), NULL, NULL);
// Accepting was a failure
if (sckClientSocket == INVALID_SOCKET)
{
std::cout << "Client accepting has failed" << std::endl;
// HANDLE ERROR - TODO
}
// Client was added successfully
else
{
setsockopt(sckClientSocket, SOL_SOCKET, SO_RCVTIMEO, (char*)&timeout, sizeof(timeout));
nResult = recv(sckClientSocket, pcBuffer, NAME_LENGTH, 0);
// If received a valid username
if (nResult > 0)
{
timeout = 1;
std::cout << "New Client -> " << pcBuffer << std::endl;
clntNewClient.setClientSocket(sckClientSocket);
clntNewClient.setIsAdmin(false);
clntNewClient.setClientName(pcBuffer);
setsockopt(clntNewClient.getClientSocket(), SOL_SOCKET, SO_RCVTIMEO, (char*)&timeout, sizeof(timeout));
conAllConnections->Add(clntNewClient);
// Receive until the peer shuts down the connection
}
}
}
}
}
}
/*
* [Description]: This method forwards a message to all other clients but the client who sent it
* [Paramaters]:
* pclndSenderAddress - The address of the client node who sent the
* pcMessageBuffer- a pointer to the message buffer
* nLength - the length of the message
* [Return Value]: none
*/
void SendAll(ClientNode* pclndSenderAddress, char* pcMessageBuffer, int nLength)
{
// Variable definition
int nError;
int nResult;
Client clntCurrentClient;
ClientNode* pclndCurrentNode;
ClientNode* pclndNextNode;
// Code section
// Set first node
pclndCurrentNode = conAllConnections->getFirst();
// Go through all connections
while (pclndCurrentNode != NULL)
{
// Save the next node in this phase of the code in order to avoid corruption of memory
// in case node would be deleted from dynamic allocated memory and we wont be able to call the next val upon it
pclndNextNode = pclndCurrentNode->getNext();
// Compare addresses, we do not want to forward the message to the sender
if (pclndCurrentNode != pclndSenderAddress)
{
clntCurrentClient = pclndCurrentNode->getClient();
// Forward the message
nResult = send(clntCurrentClient.getClientSocket(), pcMessageBuffer, nLength, 0);
// An error has occured
if (nResult == SOCKET_ERROR)
{
nError = WSAGetLastError();
// TODO -- handle later
}
}
// Forward current node
pclndCurrentNode = pclndNextNode;
}
}
/*
* [Description]: This method handles and receives messages from our clients and forwards them
* [Paramaters]: none
* [Return Value]: none
*/
void HandleConnections()
{
// Variable definition
int nIndex;
int nError;
int nRecvLen;
int nNameLen;
int nRecvbuflen = DEFAULT_BUFLEN;
char pcBuffer[DEFAULT_BUFLEN + NAME_LENGTH + 3];
Client clntCurrentClient;
ClientNode* pclndCurrentNode;
ClientNode* pclndNextNode;
// Code section
// Keep this going constantly
while (true)
{
pclndCurrentNode = conAllConnections->getFirst();
// Go through all connections
while (pclndCurrentNode != NULL)
{
clntCurrentClient = pclndCurrentNode->getClient();
// Save the next node in this phase of the code in order to avoid corruption of memory
// in case node would be deleted from dynamic allocated memory and we wont be able to call the next val upon it
pclndNextNode = pclndCurrentNode->getNext();
// Attempt receiving data from client
nRecvLen = recv(clntCurrentClient.getClientSocket(), pcBuffer, nRecvbuflen, 0);
// An error has occured
if (nRecvLen <= 0)
{
nError = WSAGetLastError();
// if not a timeout error
if (nError != 10060)
{
std::cout << "Client removed" << std::endl;
// Socket error, remove connection
conAllConnections->Remove(pclndCurrentNode);
}
}
// No error has occured
else
{
//// The purpose of this part of the code is only to place a [CLIENTNAME]
//// prefix within the begining of each message
////--------------------------------////
// Get client's name length
nNameLen = clntCurrentClient.getNameLength();
nIndex = nRecvLen - 1;
// Copy the message some offset forward -- offset is (namn length + 4)
while (nIndex >= 0)
{
// Copy letter (namelen + 4) times forward
pcBuffer[nIndex + nNameLen + 4] = pcBuffer[nIndex];
// Reduce nIndex
--nIndex;
}
pcBuffer[0] = '[';
nIndex = 0;
// Place clients name within message
while (nIndex < nNameLen)
{
// + 1 for offset
pcBuffer[nIndex + 1] = (clntCurrentClient.getClientName())[nIndex];
// Increase nIndex
++nIndex;
}
pcBuffer[nIndex + 1] = ']';
pcBuffer[nIndex + 2] = ':';
pcBuffer[nIndex + 3] = ' ';
////--------------------------------////
//// No longer adding a prefix code
SendAll(pclndCurrentNode, pcBuffer, nRecvLen + nNameLen + 4);
}
// Forward current node
pclndCurrentNode = pclndNextNode;
}
}
}
////////////////////////////////////////////////////
// Connections.h
#ifndef CONNECTIONS_H
#define CONNECTIONS_H
#include "ClientNode.h"
class Connections
{
private:
// Data members
static Connections* _Instance;
int nNumOfConnections;
ClientNode* pclndFirst;
// Ctor
Connections();
public:
// Methods
void Add(Client clntNewClient);
void Remove(ClientNode* pclndClientToRemove);
int getNumOfConnections();
ClientNode* getFirst();
// Static methods
static Connections* getInstance();
};
#endif
////////////////////////////////////////////////////
// Connections.cpp
#include "Connections.h"
// Set instance to null
Connections* Connections::_Instance = NULL;
/* ------- PRIVATE CTOR -------
* [Description]: This method is the constructor of the Connections
* [Paramaters]: none
* [Return Value]: none
*/
Connections::Connections()
{
this->nNumOfConnections = 0;
this->pclndFirst = NULL;
}
/*
* [Description]: This method returns the amount of connections currently within our linked list
* [Paramaters]: none
* [Return Value]: The amount of connections
*/
int Connections::getNumOfConnections(){
return (this->nNumOfConnections);
}
/*
* [Description]: This method returns a pointer to the first client node within our connection list
* [Paramaters]: none
* [Return Value]: A pointer to the first client node
*/
ClientNode* Connections::getFirst()
{
return (this->pclndFirst);
}
/*
* [Description]: This method adds a new client to the linkedlist of clients
* [Paramaters]:
* clntNewClient - The new client struct
* [Return Value]: none
*/
void Connections::Add(Client clntNewClient)
{
// Create a new client node
ClientNode* pclndNewClientNode = new ClientNode;
// Set the client node's client
pclndNewClientNode->setClient(clntNewClient);
// Set the client node's next client pointer to point at the currenly first address
pclndNewClientNode->setNext(this->getFirst());
// Set the first client pointer to point at the new client node's address (Push it within the linked list)
this->pclndFirst = pclndNewClientNode;
// Increase the number of connection
++(this->nNumOfConnections);
}
/*
* [Description]: This method removes a client from our linked list of connections
* [Paramaters]:
* pclndClientToRemove - The address of the client node we wish to remove
* [Return Value]: none
*/
void Connections::Remove(ClientNode* pclndClientToRemove){
// Variable definition
int nIndex;
ClientNode* pclndCurrentNode;
// Code section
pclndCurrentNode = this->getFirst();
// Checking if we need to remove the first node
if (pclndCurrentNode == pclndClientToRemove)
{
// Jump over deleted node
this->pclndFirst = pclndClientToRemove->getNext();
// Free memory
delete pclndClientToRemove;
// Decrease amount of connections
--(this->nNumOfConnections);
}
// We do not need to remove the first one
else
{
// Go through all ClientNodes addresses
for (nIndex = 0; nIndex < (this->nNumOfConnections - 1); ++nIndex)
{
// If the next node is the node we wish to delete
if (pclndCurrentNode->getNext() == pclndClientToRemove)
{
// Set the current node next node to be the next node of the node we wish to delete
pclndCurrentNode->setNext(pclndClientToRemove->getNext());
// free dynamically allocated memory
delete pclndClientToRemove;
// break outside the loop
break;
// Decrease amount of connections
--(this->nNumOfConnections);
}
// Next node is not the node we whish to delete
else
{
// Move to the next node
pclndCurrentNode = pclndCurrentNode->getNext();
}
}
}
}
/*
* [Description]: This method returns the only instance of Connections (SINGLETON PATTERN)
* [Paramaters]: none
* [Return Value]: A pointer to the single instance of connection
*/
Connections* Connections::getInstance(){
// If instance was not instantiated yet
if (_Instance == NULL)
{
// Call CTOR
_Instance = new Connections();
}
return (_Instance);
}
////////////////////////////////////////////////////
// ClientNode.h
#ifndef CLIENTNODE_H
#define CLIENTNODE_H
#include "Client.h"
class ClientNode
{
// Data members
Client clntClient;
ClientNode* pclntNextClient;
public:
// Access methods
void setNext(ClientNode* pclndNextNode);
void setClient(Client clntNewClient);
Client getClient();
ClientNode* getNext();
};
#endif
////////////////////////////////////////////////////
// ClientNode.cpp
#include "ClientNode.h"
/*
* [Description]: This method sets the next node our node would be pointing add
* [Paramaters]:
* pclndNextNode - The address of the next node we want this node to point at
* [Return Value]: none
*/
void ClientNode::setNext(ClientNode* pclndNextNode)
{
this->pclntNextClient = pclndNextNode;
}
/*
* [Description]: This method sets the client struct we want our current node to contain
* [Paramaters]:
* clntNewClient - New client
* [Return Value]: none
*/
void ClientNode::setClient(Client clntNewClient)
{
this->clntClient = clntNewClient;
}
/*
* [Description]: This method returns the client instance our node contains
* [Paramaters]: none
* [Return Value]: Our client
*/
Client ClientNode::getClient()
{
return (this->clntClient);
}
/*
* [Description]: This method returns the next node our node points at
* [Paramaters]: none
* [Return Value]: The address of the next node this node is pointing at
*/
ClientNode* ClientNode::getNext()
{
return (this->pclntNextClient);
}
////////////////////////////////////////////////////
// Client.h
#ifndef CLIENT_H
#define CLIENT_H
#include <WinSock2.h>
#define MAX_CLIENT_NAME_LEN = 40
class Client
{
// Data members
SOCKET scktClientSock;
char* szClientName;
bool bIsAdmin;
int nNameLength;
public:
// Access methods
void setClientSocket(SOCKET scktClientSock);
SOCKET getClientSocket();
void setClientName(char* szClientName);
char* getClientName();
void setIsAdmin(bool bIsAdmin);
bool getIsAdmin();
int getNameLength();
// Other methods
};
#endif
////////////////////////////////////////////////////
// Client.h
#include "Client.h"
/*
* [Description]: This method changes the SOCKET data member of the Client class
* [Paramaters]:
* _scktClientSock - the new socket client that is being set
* [Return Value]: none
*/
void Client::setClientSocket(SOCKET _scktClientSock)
{
this->scktClientSock = _scktClientSock;
}
/*
* [Description]: This method retrieves the client's socket
* [Paramaters]: none
* [Return Value]: The socket client
*/
SOCKET Client::getClientSocket()
{
return (this->scktClientSock);
}
/*
* [Description]: This method changes the client's name
* [Paramaters]:
* _szClientName - a zero terminated string that describes the new client's name
* [Return Value]: none
*/
void Client::setClientName(char* _szClientName)
{
// Variable definition
int nIndex = -1;
// Code section
this->szClientName = new char[41];
// Copy string char per char
do
{
++nIndex;
this->szClientName[nIndex] = _szClientName[nIndex];
} while (_szClientName[nIndex] != '\0');
// Name length is equal to index
this->nNameLength = nIndex;
}
/*
* [Description]: This method returns a pointer to the first char of the zero terminated client string
* [Paramaters]: none
* [Return Value]: a pointer to the string
*/
char* Client::getClientName()
{
return (this->szClientName);
}
/*
* [Description]: This method is used to set whether the client is an admin or not
* [Paramaters]:
* _bIsAdmin - a boolean indication of whether the user is an admin or not
* [Return Value]: none
*/
void Client::setIsAdmin(bool _bIsAdmin)
{
this->bIsAdmin = _bIsAdmin;
}
/*
* [Description]: This method determines whether the user is an admin or not
* [Paramaters]: none
* [Return Value]: A boolean indication of whether the user is an admin or not
*/
bool Client::getIsAdmin()
{
return (this->bIsAdmin);
}
/*
* [Description]: This method retrieves the client's name length
* [Paramaters]: none
* [Return Value]: the name length
*/
int Client::getNameLength()
{
return (this->nNameLength);
}
再次,这是一些很旧的代码,我已经写了我道歉,如果它不是那么好,但它肯定工程...还注意到,我已经包含服务器代码中的许多不同的模式,每个由以下
////////////////////////////////////////////////////
分隔只有两个连接的客户端是很容易的,因为那么服务器基本上只是充当代理:服务器从客户端A接收的所有内容只是发送到客户端B,反之亦然。通过直接的“发送”呼叫将每个“recv”连接起来。在一个过程中完成所有工作(请记住过程彼此独立)。 –
我要么使用'select'来处理单个线程中的所有连接,要么使用线程(使用'pthread'函数)。分叉将是我的最后选择。 – user3386109
如果你想发送消息给另一个客户端(从服务器),那么你至少需要一个连接...当你使用'fork'时,你应该有现有的连接,然后让进程通信或至少建立一些分叉后连接到另一个(在你的情况下'insidePortal'应该使用现有的连接或建立一个)。在这种情况下,线程比叉子更合适。 –