套接字多线程实现C

时间:2013-02-10 03:31:30

标签: c multithreading sockets pthreads client-server

我正在研究在C中实现多线程多客户端单服务器套接字。但是无论出于何种原因,当前程序使用pthread_create()创建新线程时,它都不会超过该行代码。我已经在这行代码之前和之后放置了打印行,所有打印行在手工打印之前都很好,但是在打印之后都没有。这让我相信pthread_create()在某种程度上是错误的。这个奇怪的是我可以有1个客户端连接并成功从服务器发送/接收数据但是因为listen()命令所在的循环没有推进我不能承担额外的客户端。感谢你对此事的帮助。

服务器代码

#include <stdio.h>
#include <stdlib.h> //for IOs
#include <string.h>
#include <unistd.h>
#include <sys/types.h> //for system calls
#include <sys/socket.h> //for sockets
#include <netinet/in.h> //for internet
#include <pthread.h>

void error(const char *msg)
{
    perror(msg);
    exit(1);
}
void *threadFunc(int mySockFd)
{
    int n;
    char buffer[256];
    do
    {
        bzero(buffer,256);
        n = read(mySockFd,buffer,255);
        if (n < 0) 
        {
            error("ERROR reading from socket");
        }
        else if(strcmp(buffer, "EXIT\n") == 0)
    {
        printf("Exit by user\n");
        pthread_exit(NULL);
    }
    else
    {
        printf("Here is the message: %s\n",buffer);
            n = write(mySockFd,"I got your message",18);            
            if (n < 0) 
            {
                error("ERROR writing to socket");
            }
        }
    }while(n >= 0);


}    

int main(int argc, char *argv[])
{
    int sockfd;
    int newsockfd;
    int portno;
    pthread_t pth;
    int n; /*n is the return value for the read() and write() calls; i.e. it contains     the number of characters read or written.*/
    int i = 0;
    printf("after var decl");
socklen_t clilen; /*clilen stores the size of the address of the client. This is     needed for the accept system call.*/
    char buffer[256]; /*The server reads characters from the socket connection into     this buffer.*/
    struct sockaddr_in serv_addr;
    struct sockaddr_in cli_addr;
if (argc < 2) 
    {
        fprintf(stderr,"ERROR, no port provided\n");
        exit(1);
    }
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0)
    {
        error("ERROR opening socket");
    }
bzero((char *) &serv_addr, sizeof(serv_addr));
portno = atoi(argv[1]);
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = INADDR_ANY;
serv_addr.sin_port = htons(portno);
if (bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0)
    {
        error("ERROR on binding");
    }
do
    {       
        printf("before listen");
        listen(sockfd,5);
        printf("after listen");
                clilen = sizeof(cli_addr);
        printf("before accept");
        newsockfd = accept(sockfd,(struct sockaddr *) &cli_addr,&clilen);
        printf("after accept");
        pthread_create(&pth,NULL,threadFunc(newsockfd),(void*) &i);
        printf("after pthread create");
        if (newsockfd < 0)
        {
            error("ERROR on accept");
        }
    }while(1 == 1);
bzero(buffer,256);
    n = read(newsockfd,buffer,255);
    if (n < 0) 
    {
        error("ERROR reading from socket");
    }
    printf("Here is the message: %s\n",buffer);
if (n < 0) error("ERROR writing to socket");
close(newsockfd);
    close(sockfd);
    return 0;

这是客户端代码

#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> /*The file netdb.h defines the structure hostent, which will be     used below.*/
void error(const char *msg)
{
    perror(msg);
    exit(0);
}

int main(int argc, char *argv[])
{
int sockfd;
int portno;
int n;
struct sockaddr_in serv_addr;
struct hostent *server;
char buffer[256];
if (argc < 3) 
{
    fprintf(stderr,"usage %s hostname port\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");
}
do
{
    printf("Please enter the message: ");
    bzero(buffer,256);
    fgets(buffer,255,stdin);
    n = write(sockfd,buffer,strlen(buffer));
    if(strcmp(buffer,"EXIT\n") == 0)
    {
        printf("Connection Terminated\n");
        break;
    }
    if (n < 0)
    {
        error("ERROR writing to socket");
    }
    bzero(buffer,256);
    n = read(sockfd,buffer,255);
    printf("%s\n",buffer);
    if (n < 0)
    {
    error("ERROR reading from socket");
    printf("%s\n",buffer);
    }

}while(1 == 1);
close(sockfd);
return 0;
}

2 个答案:

答案 0 :(得分:4)

两个错误:

  1. 你投的太多了,这里唯一的地方应该是inaddr的东西。
  2. 您没有收听编译器,提高了警告级别。
  3. 现在,问题(或者只是一个?)实际上是这样的:

    pthread_create(&pth,NULL,threadFunc(newsockfd),(void*) &i);
    

    这将调用threadFunc(newsockfd)并将结果传递给pthread_create。第二部分永远不会发生,因为该函数调用pthread_exit或在不返回任何内容的情况下掉线,这可能导致任何事情发生。

答案 1 :(得分:0)

您的服务器代码未正确显示printf语句是因为您没有以“\ n”结束传递给printf的字符串。

更改所有printf语句以包含尾随\ n,以便立即“刷新”输出。 E.g。

而不是:

printf("after pthread create");

这样做:

printf("after pthread create\n");

对printf语句的所有重复此修复。然后,当客户端连接到程序流时,程序流将更容易看到。

您的代码中可能还有大约5或6个其他错误。我想要调用的主要原因是客户端发送了4个字节的“EXIT”,并不意味着TCP流不会在两个单独的读取调用中将其分段为“EX”和“IT”,具体取决于intertubes的状态。始终编​​写协议代码,就好像read / recv一次只返回一个char。 OR 只需将MSG_WAITALL与recv()一起使用,以便始终读取块大小。