c - C中的多线程TCP聊天

标签 c multithreading tcp chat

我在用 C 编写具有以下功能的命令行聊天应用程序时遇到未知问题。

  • 客户端可以连接到服务器(IP + 端口 + 昵称)
  • 客户端可以使用"@Username Message"向其他客户端发送消息

该实现实际上应该可以正常工作,我可以连接到服务器,但是当我作为客户端发送消息时不知何故没有任何反应。我认为可能是线程的问题。

提前致谢!

client.c:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <pthread.h>

void error(const char *msg);            //Funktion fuer errormessages
void *scocket_reader (void *sock);      //Funktion fuer client thread socketreader


// Thread Funktion socket reader
void *scocket_reader (void *sock)
{
    int n;
    char buffer[256];

    printf("Wurde gestartet warte auf nachrichten\n");

    //Endloslesen vom Server
    while(1)
    {
        bzero(buffer,256);
        n = read(*(int *)sock,buffer,255);

        if (n < 0)
            error("ERROR reading from socket");

        printf("%s",buffer);
    }

    return NULL;
}

//Error Funktion
void error(const char *msg)
{
    perror(msg);
    exit(0);
}

int main(int argc, char *argv[])
{
    int sockfd, portno, n;
    struct sockaddr_in serv_addr;
    struct hostent *server;
    char buffer[256];

    //Thread init
    pthread_t pt;

    if (argc < 4) {
       fprintf(stderr,"usage %s hostname port username\n", argv[0]);
       exit(0);
    }

    portno = atoi(argv[2]);
    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd < 0)
        error("ERROR opening socket");

    server = gethostbyname(argv[1]);
    if (server == NULL) {
        fprintf(stderr,"ERROR, no such host\n");
        exit(0);
    }

    bzero((char *) &serv_addr, sizeof(serv_addr));
    serv_addr.sin_family = AF_INET;
    bcopy((char *)server->h_addr,
         (char *)&serv_addr.sin_addr.s_addr,
         server->h_length);
    serv_addr.sin_port = htons(portno);

    if (connect(sockfd,(struct sockaddr *) &serv_addr,sizeof(serv_addr)) < 0)
        error("ERROR connecting");


    // Register @server
    bzero(buffer,256);
    strcpy(buffer,argv[3]);
    //sende namen
    n = write(sockfd,buffer,strlen(buffer));
    bzero(buffer,256);

    // Start Listener Thread
    pthread_create (&pt, NULL, scocket_reader, &sockfd);

    while(1)
    {
        //Nachrichten schreiben endlos
        if ( fgets(buffer,255,stdin) != NULL )
            if (buffer[0] != '\n')
            {
                n = write(sockfd,buffer,strlen(buffer));
                if (n < 0)
                    error("ERROR writing to socket");
            }
        bzero(buffer,256);
    }

    //socket schliessen
    close(sockfd);

    return 0;
}

server.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <pthread.h>

void *client_scocket_reader (void *usernr);         //Thread Funktion für socket reader
void error(const char *msg);                        //Error function

#define MAXUSER 10

// User Structur
struct User
{
    socklen_t clilen;
    struct sockaddr_in cli_addr;
    int newsockfd;
    char username[16];
};
struct User user[MAXUSER], tmpUser;
int user_anz = 0;
struct sockaddr_in serv_addr;


//Client Listener Thread -----------------------------------------------
void *client_scocket_reader (void *usernr)
{
    int mynr = *(int *)usernr , n, foundone=0, i= 0;

    char buffer[256], text[256], text_sp[256], uname[20], private_m[256];
    char *splitter;

    strcpy(text_sp,"000");

    printf("Wurde gestartet ich bin User %d\n", mynr);

    while(1)
    {
        foundone = 0;
        bzero(buffer,256);
        //Lesen und arten auf nachricht
        n = read(user[mynr].newsockfd,buffer,255);
        printf("----------%d-------------\n",n);
        if (n <= 0)
        {
            printf("ERROR reading from socket\n");
            user_anz--;
            return NULL;
        }

        //Server Messages
        if ( strstr(buffer, "@Server") )
        {
            splitter = strtok(buffer," ");
            splitter = strtok(NULL," ");
            if (splitter == NULL)
            {
                n = write(user[mynr].newsockfd, "Server: Not a cmd\n", 18);
                continue;
            }
            if (strchr(splitter,'\n'))              // Zeilenumbrüche entfernen
                splitter[strlen(splitter)-1] = '\0';

            printf("--->%s<--\n",splitter);
            //Who Command
            if (strcmp(splitter,"who") == 0)
            {
                n = write(user[mynr].newsockfd, "----Userlist----\n", 17);
                for (i = 0 ; i < user_anz ; i++ )
                {
                    strcpy(private_m, user[i].username);
                    strcat(private_m, "\n");
                    n = write(user[mynr].newsockfd, private_m, strlen(private_m));
                }
                n = write(user[mynr].newsockfd, "----------------\n", 17);
            }
            // logout
            else if (strcmp(splitter,"logout") == 0)
            {
                n = write(user[mynr].newsockfd, "you are disconnected\n", 21);
                user_anz--;
                return NULL;
            }
            else if (strcmp(splitter,"history") == 0)
            {
                n = write(user[mynr].newsockfd, "Server: Not implemented\n", 24);
            }
            else if (strcmp(splitter,"help") == 0)
            {
                n = write(user[mynr].newsockfd, "----Help--------\n", 17);
                n = write(user[mynr].newsockfd, "who\n",4);
                n = write(user[mynr].newsockfd, "help\n",5);
                n = write(user[mynr].newsockfd, "logout\n",7);
                n = write(user[mynr].newsockfd, "history\n",8);
                n = write(user[mynr].newsockfd, "----------------\n", 17);
            }
            else
                n = write(user[mynr].newsockfd, "Server: Not a cmd\n", 18);


            printf("I got an message\n");

            //spring an ende der schleife da der rest nicht benoetigt wird
            continue;
        }

        //Privat Messages
        if ( buffer[0] == '@' )
        {
            //Suche durch alle user
            for ( i = 0 ; i < user_anz ; i++)
            {
                bzero(uname,20);
                //name zusamenbaun fuer vergleich
                uname[0] = '@';
                strcat(uname, user[i].username);


                //namen suchen
                if ( strstr(buffer, uname) )
                {
                    printf("priv strstr->\n");
                    //Zusammenbau der Message
                    bzero(private_m,256);
                    strcpy(private_m, "Message from ");
                    strcat(private_m, user[mynr].username);
                    strcat(private_m, ": ");
                    strcat(private_m, buffer);
                    //SChreiben der nachricht zu jeweiligen user
                    n = write(user[i].newsockfd, private_m, strlen(private_m));
                    if (n < 0)
                    {
                        user_anz--;
                        printf("ERROR writing to socket\n");
                        return NULL;
                    }
                    foundone=1;
                    break;
                }

            }


            if (foundone != 1)
            {
                printf("No user found\n");
                // Antwort fuer User wurde nicht gefunden
                n = write(user[mynr].newsockfd, "User Offline\n", 12);
                if (n < 0)
                {
                    user_anz--;
                    printf("ERROR writing to socket\n");
                    return NULL;
                }
            }
            //spring an ende der schleife da der rest nicht benoetigt wird
            continue;
        }


        // Messages weiterleiten an alle ausser mich
        for ( i = 0 ; i < user_anz ; i++)
        {
            if ( i != mynr)
                n = write(user[i].newsockfd, text, strlen(text));
            if (n < 0)
            {
                user_anz--;
                printf("ERROR writing to socketi\n");
                return NULL;
            }
        }

        // Für spezielle nachrichten Server antworten
        if ( strcmp(text_sp, "000") != 0 )
        {
            strcat(text_sp,"\n");
            for ( i = 0 ; i < user_anz ; i++)
            {
                n = write(user[i].newsockfd, text_sp, strlen(text_sp));
                if (n < 0)
                {
                    user_anz--;
                    printf("ERROR writing to socketi\n");
                    return NULL;
                }
            }
        }
    }

    return NULL;
}

// Error Message mit exit
void error(const char *msg)
{
    perror(msg);
    exit(1);
}



int main(int argc, char *argv[])
{
    int i,n;
    int sockfd,  portno;
    char buffer[256],buffer2[256];

    // Threads
    pthread_t pt[MAXUSER];

    // Argumentcheck
     if (argc < 2) {
         fprintf(stderr,"ERROR, no port provided\nUsage ./server portnumber\n");
         exit(1);
     }

     printf("Init Socket\n");
     sockfd = socket(AF_INET, SOCK_STREAM, 0);

     if (sockfd < 0)
        error("ERROR opening socket");

     printf("Init Server address\n");
    // nuller reinschreiben
     bzero((char *) &serv_addr, sizeof(serv_addr));
    //port
     portno = atoi(argv[1]);
     //server address koniguration
     serv_addr.sin_family = AF_INET;
     serv_addr.sin_addr.s_addr = INADDR_ANY;
     serv_addr.sin_port = htons(portno);

     printf("Bind server address\n");
     if (bind(sockfd, (struct sockaddr *) &serv_addr,
              sizeof(serv_addr)) < 0)
              error("ERROR on binding");

     printf("Init Listen\n");
     listen(sockfd,5);

//--------------------------------------------------------
// init USERS to MAXUSERS
//--------------------------------------------------------

while(user_anz != MAXUSER)
{
     printf("Waiting for Connects\n");
     user[user_anz].clilen = sizeof(user[user_anz].cli_addr);

     user[user_anz].newsockfd = accept(sockfd,
                 (struct sockaddr *) &user[user_anz].cli_addr,
                 &user[user_anz].clilen);

     if (user[user_anz].newsockfd < 0)
          error("ERROR on accept");

     printf("Waiting for User1 registration\n");
     bzero(buffer,256);

     n = read(user[user_anz].newsockfd,buffer,255);
     if (n < 0)
          error("ERROR writing to socket");

     printf("Sysmessage|| User %s connected\n", buffer);
     strcpy(buffer2,"Hello ");
     strcat(buffer2,buffer);
     strcat(buffer2," you are now registrated!\n");

     strcpy(user[user_anz].username, buffer);

     n = write(user[user_anz].newsockfd,buffer2,strlen(buffer2));
     if (n < 0)
          error("ERROR writing to socket");

     printf("Starting listener Thread\n");
     //usernr = 0;
     pthread_create (&pt[user_anz], NULL, client_scocket_reader, &user_anz);

     user_anz++;


}

//Falls MAXUSER erreicht Threads joinen lassen damit der main thread net wegsemmelt
//--------------------------------------------------------
     for ( i = 0 ; i < MAXUSER ; i++ )
        pthread_join (pt[i], NULL);


// Sockets schliessen
//--------------------------------------------------------

     for ( i = 0 ; i < MAXUSER ; i++)
        close(user[i].newsockfd);
// Serversocket schliessen
     close(sockfd);

     return 0;
}

最佳答案

I think could be a problem with threads.

没错。您的 client_scocket_reader 输掉了与 main 的比赛。 main 中容易出错的地方是

     pthread_create (&pt[user_anz], NULL, client_scocket_reader, &user_anz);

     user_anz++;

连同

void *client_scocket_reader (void *usernr)
{
    int mynr = *(int *)usernr , n, foundone=0, i= 0;
  • mainclient_scocket_reader 将其复制到 mynr
  • 之前递增 user_anz
  • 因此 client_scocket_reader 使用来自 user[1] 而不是 user[0]
  • 的错误数据
  • 因此 client_scocket_reader 尝试读取错误的文件描述符

快速修复:按值而不是引用传递 user_anz

     pthread_create(pt+user_anz, NULL, client_scocket_reader, (void *)user_anz);

    int mynr = (int)usernr, n, foundone, i;

关于c - C中的多线程TCP聊天,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24069688/

相关文章:

c++ - 为什么结构的 sizeof 不等于每个成员的 sizeof 之和?

multithreading - 为什么 Pycharm 调试器不会在主线程外的断点处停止?

java - 如何在 Java 中跟踪线程池中的 Runnables?

c - 为 Ceasar's Code 在 C 中环绕

c - 如何使用 C 获取 OS X 上所有接口(interface)的 IP 地址

c++ - 从 C 中正确使用 C++ 函数

java - ConnectionTimeout 与 SocketTimeout

java - java oracle 教程中带有锁对象的 Bow/Bower 示例

具有持久 TCP 服务器套接字的 Java 应用程序高可用性/故障转移

c# - 多少个客户端可以连接到一个 TCP 端口