Linux Socket多线程实现简单的多人聊天(pend)

Server:

设置可聊天数为5,为每一个client创建一个线程,这个线程负责接收client的聊天内容并发给其他用户看。

用mutex同步各个线程修改聊天室空余聊天位。

Client:

主线程负责向server发送自己的内容,开一个线程负责接收server发过来别人聊天的内容。

client.c

#include <sys/types.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <sys/un.h>
#include <sys/time.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <pthread.h>

char recv_buf[1500],send_buf[1024];

void pthread_function(void* sock_fd){
    int sockfd=*(int*)sock_fd;
    long recvbytes;
    while(1) {
        if((recvbytes=recv(sockfd,recv_buf,1500,0))==-1){
            printf("recv error");
            exit(1);
        }
        else{
            recv_buf[recvbytes]='';
            printf("%s
",recv_buf);
        }
    }
} 
int main(void){
    pthread_t id;
    int sockfd;
    struct sockaddr_in sever_addr;

    sever_addr.sin_family=AF_INET;
    sever_addr.sin_port=htons(12363);
    sever_addr.sin_addr.s_addr=inet_addr("10.144.20.79");

    if((sockfd=socket(AF_INET,SOCK_STREAM,0))==-1){
        printf("socket error");
        exit(1);
    }
    if(connect(sockfd,(struct sockaddr*)&sever_addr,sizeof(sever_addr))==-1){
        printf("connect error");
        exit(1);
    }
    char name[20];
    printf("please input your name
");
    scanf("%s",name);
    send(sockfd,name,strlen(name),0);

    if(pthread_create(&id,NULL,(void*)pthread_function,(void*)&sockfd)!=0)
        printf("create thread error
");
    while(1){
        scanf("%s",send_buf);
        fflush(stdin);
        if(send(sockfd,send_buf,strlen(send_buf),0)==-1){
            printf("send error");
            exit(1);
        }
        sleep(1);
    }
    close(sockfd);
    pthread_cancel(id);
    return 0;
}

server.c

#include <sys/types.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <sys/un.h>
#include <sys/time.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <netinet/in.h>
#include <pthread.h>


#define COUNT 5
int socket_fd[COUNT]={0,-1,-1,-1,-1};
pthread_mutex_t emptymutex;

int empty=5;
/*
the fuction for the thread
*/

void pthread_function(void* clientfd){
    char message[1500];
    char buf[1024];
    int i;
    long recvbytes;
    char name[20];
    int client_fd=*(int*)clientfd;

    pthread_mutex_lock(&emptymutex);
    if(empty>0)
        empty--;
    else pthread_exit(NULL);
    pthread_mutex_unlock(&emptymutex);



    /*first connect,need to save the name of clients*/
    recvbytes=recv(client_fd,name,20,0);
    name[recvbytes]=':';
    name[recvbytes+1]='';
    send(client_fd,"welcome to this chatroom,enjoy chatting:",100,0);


    while(1){
        if((recvbytes=recv(client_fd,buf,1024,0))==-1){
            perror("recv");
            pthread_exit(NULL);
        }
        if(recvbytes==0){
            printf("%sbye
",name);
            break;
        }
        buf[recvbytes]='';
        for (i = 0; i < COUNT; ++i)
        {
            int tmpclient=socket_fd[i];
            if(tmpclient!=-1){
                message[0]='';
                strcat(message,name);
                strcat(message,buf);
                if(send(tmpclient,message,strlen(message),0)==-1){
                    perror("send error");
                    pthread_exit(NULL);
                }
            }
        }
    }
    //close socket and reset the fd -1 
    close(client_fd);

    pthread_mutex_lock(&emptymutex);
    if(empty<5)
        empty++;
    else pthread_exit(NULL);
    pthread_mutex_unlock(&emptymutex);
    for (i = 0; i < COUNT; ++i)
    {
        if(socket_fd[i]==client_fd)
            socket_fd[i]=-1;
    }
    pthread_exit(NULL);
}


int main(){
    int i;
    pthread_mutex_init(&emptymutex,NULL);

    pthread_t id;
    int sockfd,client_fd;
    socklen_t sin_size;
    struct sockaddr_in my_addr;
    struct sockaddr_in remote_addr;
    if((sockfd=socket(AF_INET,SOCK_STREAM,0))==-1){
        perror("socket");
        exit(1);
    }

    my_addr.sin_family=AF_INET;
    my_addr.sin_port=htons(12363);
    my_addr.sin_addr.s_addr=htonl(INADDR_ANY);
    bzero(&(my_addr.sin_zero),8);


    if(bind(sockfd,(struct sockaddr*)&my_addr,sizeof(struct sockaddr))==-1){
        perror("bind");
        exit(1);
    }

    if(listen(sockfd,10)==-1){
        perror("listen");
        exit(1);
    }

    i=0;
    while(1){
        sin_size=sizeof(struct sockaddr_in);
        if((client_fd=accept(sockfd,(struct sockaddr*)&remote_addr,&sin_size))==-1){
            perror("accept");
            exit(1);
        }
        if(empty>0){    
            while(socket_fd[i]==-1)
                i=(i+1)%COUNT;
            socket_fd[i]=client_fd;
            pthread_create(&id,NULL,(void*)pthread_function,(void*)&client_fd);}
        else break;
    }
    pthread_mutex_destroy(&emptymutex);
    return 0;    
}

注意点:

  • sockaddr和sockaddr_in的区别
  • htonl(INADDR_ANY)设置ip为0.0.0.0,这样会监听所有端口
  • server是:socket-bind-listen-accpet-send/recv-close;client是:socket-connect-send/recv-close。
  • pthread_create()第三个参数是void*,如果想用int,可以int var=*(int*)client_fd
  • bind:address in use是端口已经被打开
  • 线程里用pthread_exit(NULL)如果用exit连主线程都会退出

还要改,现在新开一个client可以接受老client的内容,但是老client接收不到新client的内容。还有就是client输入一个什么特定的字符表示结束,以close(socket).

原文地址:https://www.cnblogs.com/LUO77/p/5707511.html