//局域网聊天工具(多线程),支持客户端与客户端间通信,服务器负责信息的接收与发送//服务器端#include <stdio.h>#include <stdlib.h>#include <errno.h>#include <strings.h>#include <unistd.h>#include <string.h>#include <signal.h>#include <sys/types.h> #include <sys/socket.h>#include <netinet/in.h>#include <arpa/inet.h>#include <pthread.h>#include <semaphore.h>#define SERV_PORT 5432#define SERV_ADDR "192.168.7.118"#define BACKLOG 10#define CLIENT_NUM 50void *add_client(void *args); //当有一个client联上server时,新建一线程.void *send_to(void *args); //发送线程.void sys_clean(int newfd); //善后工作.void ctrl_c(int sig);sem_t sem;int id = 0;int fd = -1;struct sockaddr_in cin;int newfd[CLIENT_NUM];static char clientip[CLIENT_NUM][32];static char sendto_ip[CLIENT_NUM][32];static char readbuf[CLIENT_NUM][BUFSIZ];static char sendbuf[CLIENT_NUM][BUFSIZ];static int lenofip[CLIENT_NUM];void usage(char *s){printf("usage:\n");printf("%s ipaddr\n",s);exit(0);}void getsignal(int sig){}/*server.c*/int main(int argc, char **argv) {int tmp = 0;//初始化for(tmp = 0; tmp < CLIENT_NUM; tmp++){newfd[tmp] = -1;memset(clientip[tmp],0,32);}signal(SIGINT,ctrl_c);int ID = -1;int ret = -1;socklen_t clen = -1;struct sockaddr_in sin;sem_init(&sem,0,1); //设置信号量,使有多个写线程时可以同步fd = socket(AF_INET, SOCK_STREAM, 0); //创建socket套接字if(fd < 0) {perror("socket");exit(1);}sin.sin_family = AF_INET; //使用的协议sin.sin_port = htons(SERV_PORT);// 端口bzero(sin.sin_zero,8); //清空填充位int breuse = -1; /*允许地址快速重用. */setsockopt(fd,SOL_SOCKET,SO_REUSEADDR,&breuse,sizeof(int));switch (argc) {case 1:sin.sin_addr.s_addr = inet_addr(SERV_ADDR);break;case 2:sin.sin_addr.s_addr = inet_addr(argv[1]);if(sin.sin_addr.s_addr < 0) {usage(argv[0]);} break;default:usage(argv[0]);break;}if (bind(fd, (struct sockaddr *)&sin, sizeof(sin)) < 0) { //绑定套接字perror("bind");exit(1);} /* accept*/while(1){while(ID == id); //确保id已经加1;ID = id; listen(fd, BACKLOG); //变成被动套接字do {clen = sizeof(cin);newfd[ID] = accept(fd, (struct sockaddr *)&cin, &clen); //读写操作都是针对这个文件描述符} while(newfd[ID] < 0 && EINTR == errno);strcpy(clientip[ID],inet_ntoa(cin.sin_addr));printf("\n客户端联上主机 ip为: %s\n", clientip[ID]);printf("此客户端的ID =%d accept描述符为newfd[%d] = %d\n\n",ID,ID,newfd[ID]);pthread_t tid; //有一个用户联上时,newfd>0 此时就创建一个新的线程.if(newfd[ID] > 0){ pthread_create(&tid, NULL,add_client,(void *)&ID);}}sys_clean(newfd[ID]);}void *add_client(void *args) //增加用户线程{id++;pthread_detach(pthread_self()); //设置分离int ID = *((int *)args);int ret = -1;printf("\n此线程负责客户端ip = %s \n",clientip[ID]);printf("ID = %d 的消息发送与接收 accept 的描述符为newfd[%d] = %d\n",ID,ID,newfd[ID]);pthread_t tid;pthread_create(&tid,NULL,send_to,(void *)&ID); while (1) { bzero(readbuf[ID], BUFSIZ); do { ret = read(newfd[ID],readbuf[ID], BUFSIZ); if (!ret) break; if(ret < 0) { perror("read"); exit(1); } int t = 0; while(readbuf[ID][t] != ':' && readbuf[ID][t] != '\0' && t < 32) { t++; } if(t >= 32 || readbuf[ID][0]>'9'|| readbuf[ID][0]<'0'){ continue; } strncpy(sendto_ip[ID],readbuf[ID],t);//取出要发送的ip存于sendtoip中 strcpy(sendbuf[ID],readbuf[ID]); lenofip[ID] = t; printf("消息发往的ip = %s\n",sendto_ip[ID]); int t2 = 0; while(strncmp(clientip[t2],readbuf[ID],t) && t2 < CLIENT_NUM){ t2++; } if(t2 >= CLIENT_NUM){ strcpy(readbuf[ID],"sorry,对方不在线!!\n"); write(newfd[ID], readbuf[ID], BUFSIZ); } else{ strcpy(readbuf[ID],"对方在线,消息正在发送...\n"); write(newfd[ID], readbuf[ID], BUFSIZ); pthread_kill(tid, SIGUSR1);//向发送线程发送信号,数据处理完毕. printf("消息正发送至: %s\n",sendto_ip[ID]); } memset(readbuf[ID],0,BUFSIZ); if (!strncmp(readbuf[ID], "quit",4)){ close(newfd[ID]); break; } } while(ret <0 && EINTR == errno);}sys_clean(newfd[ID]);}void *send_to(void *args){pthread_detach(pthread_self()); //设置分离 signal(SIGUSR1,getsignal);int ret,ID,sendfd; char success[20] = {"消息发送成功\n"}; char tou[50] = "消息来自:";while(1) { /*写数据*/ pause(); ret = -1; ID = *((int *)args); sendfd = -1;printf("sendto 中确认ID与描述符 newfd[%d] = %d\n",ID,newfd[ID]);int t3 = 0;while((strcmp(sendto_ip[ID], clientip[t3])) && t3 < CLIENT_NUM) //sendto_ip[ID]即为要发送到的客户端的ip 与此对应的clientip中的newfd[ID]即为要发送的客户端的文件描述符t3++;sendfd = newfd[t3];printf("要发送到的客户端ID为:%d 描述符为sendfd = %d\n",ID,sendfd);printf("要发送到的 ip = %s\n",clientip[t3]); strcat(tou,clientip[ID]); sem_wait(&sem); do { ret = write(sendfd,tou,sizeof(tou)); ret = write(sendfd, sendbuf[ID]+lenofip[ID], BUFSIZ); write(newfd[ID],success,20); }while(ret < 0 && EINTR == errno); if(ret < 0) continue; if(!strncmp(readbuf[ID], "quit",4)){ break; } bzero(sendbuf[ID], BUFSIZ); //清空缓存 sem_post(&sem); }sys_clean(newfd[ID]);}void sys_clean(int newfd){int ret = -1;char buf[BUFSIZ];bzero(buf, BUFSIZ); strncpy(buf,"quit",4);if(ret = write(newfd,buf,BUFSIZ) < 0){perror("write");}close(newfd);close(fd);exit(0);}void ctrl_c(int sig){sys_clean(fd);}