【问题标题】:C++ Multi-Client TCP ServerC++ 多客户端 TCP 服务器
【发布时间】:2015-11-25 17:33:26
【问题描述】:

我想用 C++ 制作一个带有 TCP 协议的服务器和客户端程序。服务器必须能够同时处理多个客户端。但问题是,例如,在启动服务器后,我以服务器的 IP 地址和端口作为参数运行了 2 个客户端。接下来,两个客户端都向服务器发送数据。起初,两个客户端都可以向服务器发送数据,并且服务器能够读取数据。但是,一旦服务器从第二个客户端接收到数据,它似乎就停止从第一个客户端接收数据。你有什么解决办法吗?

这是服务器代码

using namespace std;

void *task1(void *);

static int connFd;
void error(const char *msg)
{
    perror(msg);
    exit(1);
}

int main(int argc, char* argv[])
{
    int pId, portNo, listenFd;
    socklen_t len; //store size of the address
    bool loop = false;
struct sockaddr_in svrAdd, clntAdd;

pthread_t threadA[3];

if (argc < 2)
{
    cerr << "Syntam : ./server <port>" << endl;
    return 0;
}

portNo = atoi(argv[1]);

if((portNo > 65535) || (portNo < 2000))
{
    cerr << "Please enter a port number between 2000 - 65535" << endl;
    return 0;
}

//create socket
listenFd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);

if(listenFd < 0)
{
    cerr << "Cannot open socket" << endl;
    return 0;
}

bzero((char*) &svrAdd, sizeof(svrAdd));

svrAdd.sin_family = AF_INET;
svrAdd.sin_addr.s_addr = INADDR_ANY;
svrAdd.sin_port = htons(portNo);

//bind socket
if(bind(listenFd, (struct sockaddr *)&svrAdd, sizeof(svrAdd)) < 0)
{
    cerr << "Cannot bind" << endl;
    return 0;
}

    listen(listenFd, 5);

    int noThread = 0;

    while (noThread < 3)
    {
        socklen_t len = sizeof(clntAdd);
        cout << "Listening" << endl;

    //this is where client connects. svr will hang in this mode until           client conn
        connFd = accept(listenFd, (struct sockaddr *)&clntAdd, &len);

        if (connFd < 0)
        {
            cerr << "Cannot accept connection" << endl;
            return 0;
        }
        else   
        {
            cout << "Connection successful" << endl;
        }

        pthread_create(&threadA[noThread], NULL, task1, NULL); 

        noThread++;
    }

    for(int i = 0; i < 3; i++)
    {
        pthread_join(threadA[i], NULL);
    }  
}

void *task1 (void *dummyPt)
{
    cout << "Thread No: " << pthread_self() << endl;
    char test[256];
    bzero(test, 256);
    bool loop = false;
    while(!loop)
    {       
        bzero(test, 256);     
        int n = read(connFd, test, 255);
        if (n < 0) error("ERROR reading from socket");
        printf("Here is the message: %s\n",test);            
    }
    cout << "\nClosing thread and conn" << endl;
    close(connFd);
}

还有客户端代码

using namespace std;

int main (int argc, char* argv[])
{
    int listenFd, portNo;
    bool loop = false;
    struct sockaddr_in svrAdd;
    struct hostent *server;

    if(argc < 3)
    {
        cerr<<"Syntax : ./client <host name> <port>"<<endl;
        return 0;
    }

    portNo = atoi(argv[2]);

    if((portNo > 65535) || (portNo < 2000))
    {
        cerr<<"Please enter port number between 2000 - 65535"<<endl;
        return 0;
    }       

    //create client skt
    listenFd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);

    if(listenFd < 0)
    {   
        cerr << "Cannot open socket" << endl;
        return 0;
    }

    server = gethostbyname(argv[1]);

    if(server == NULL)
    {
        cerr << "Host does not exist" << endl;
        return 0;
    }

    bzero((char *) &svrAdd, sizeof(svrAdd));
    svrAdd.sin_family = AF_INET;

    bcopy((char *) server -> h_addr, (char *) &svrAdd.sin_addr.s_addr, server -> h_length);

    svrAdd.sin_port = htons(portNo);

    int checker = connect(listenFd,(struct sockaddr *) &svrAdd, sizeof(svrAdd));

    if (checker < 0)
    {
        cerr << "Cannot connect!" << endl;
        return 0;
    }

    //send stuff to server
    for(;;)
    {
        char s[300];
        //cin.clear();
        //cin.ignore(256, '\n');
        cout << "Enter stuff: ";
        bzero(s, 300);
        cin.getline(s, 300);

        write(listenFd, s, strlen(s));
    }
}

【问题讨论】:

  • 您得到了原始问题的答案。尝试实施该答案会导致您遇到新错误。您不应该通过添加错误来破坏您的帖子,这在仍然在帖子中的原始问题的上下文中是没有意义的。相反,为错误发布一个新问题。我已将问题重新编辑为如何保持答案的完整性

标签: c++ sockets tcp


【解决方案1】:

Yor connFd 是一个全局变量,您可以从主线程和所有处理线程访问它。这不行!想象一下 - 您已经接受了第一个连接并将变量设置为接收套接字。您已经生成了开始读取的处理线程。接下来你知道,另一个连接正在出现,你也正在接收它!这一刻connFd 指向新连接,所以已经在使用它的线程会突然切换到新连接!当然不好。

解决此问题的方法是以不跨线程共享的方式将连接传递给线程。最简单的方法是使用 C++ 线程类。

例如,这是说明上述想法的代码片段:

void handle_connection(int fd) {
   ... <your task1 code>
}

... 
std::vector<std::thread> threads;
...
int conn = accept(listenFd, (struct sockaddr *)&clntAdd, &len);
threads.push_back(std::thread(&handle_connection, conn));
...

... (in the end)
for (auto&& t : threads)
    t.join();

【讨论】:

  • 实施您的建议后,我遇到了一些错误。请看我的更新。
猜你喜欢
  • 1970-01-01
  • 2019-04-18
  • 2020-07-15
  • 1970-01-01
  • 2016-10-05
  • 2019-02-26
  • 2019-02-26
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多