局域网聊天工具(多线程),支持客户端与客户端间通信,服务器负责信息的接收与发送

来源:互联网 发布:惠安县招聘网络销售 编辑:程序博客网 时间:2024/05/16 14:14
//局域网聊天工具(多线程),支持客户端与客户端间通信,服务器负责信息的接收与发送//服务器端#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);} 

 
原创粉丝点击