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]='\0'; printf("%s\n",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\n"); scanf("%s",name); send(sockfd,name,strlen(name),0); if(pthread_create(&id,NULL,(void*)pthread_function,(void*)&sockfd)!=0) printf("create thread error\n"); 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]='\0'; 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\n",name); break; } buf[recvbytes]='\0'; for (i = 0; i < COUNT; ++i) { int tmpclient=socket_fd[i]; if(tmpclient!=-1){ message[0]='\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).