我有這個很長的代碼中,我在實現你類似的事情過去寫的,但它的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'應該使用現有的連接或建立一個)。在這種情況下,線程比叉子更合適。 –