基于linux的TCP网络聊天室

参考:

https://blog.csdn.net/qq_35260622/article/details/51643711

https://www.cnblogs.com/hambug/p/12000706.html

https://blog.csdn.net/tian_123456789/article/details/53557782

构思阶段:

  服务器端的功能:1.等待客户端的连接 2.向连接的客户发送登录成功信息 3.向已进入聊天室的用户发送新用户登录的系统消息 4.接收客户端发送的信息,发送到聊天室中的所有客户。

  客户端的功能:1.输入用户名等信息进行注册 2.连接到服务端 3.在聊天室中发送消息,接收系统消息和聊天室中的其它客户端发送的消息

实现中遇到的问题:

  为每个客户端fork一个进程,这样也无需I/O复用,但如何将消息发送给所有用户?通过查阅相关代码找到一个可行的方案是利用进程间通信的共享存储区。

代码:

 

/*client.c
*该程序有三个命令行参数:服务器ip地址,端口,昵称
*/
#include <sys/socket.h>
#include <strings.h>
#include <stdio.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <time.h>
#include <errno.h>
#define MAXLINE 1024
int main(int argc, char **argv){
    pid_t pid;
    int sockfd, connfd;
    struct sockaddr_in servaddr;
    short port;
    char* name;
    char buffer[MAXLINE], buf[MAXLINE];
    //检查参数够不够
    if(argc != 4) {
       printf("lacking essential infomation\n");
       exit(0);
    }
    port=atoi(argv[2]);
    name=argv[3];
    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if(sockfd < 0) {
        fprintf(stderr,"socket error:%s\n\a",strerror(errno));
        exit(0);
    }
    //将servaddr置0
    bzero(&servaddr, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_port = htons(port);
    inet_pton(AF_INET, argv[1], &servaddr.sin_addr);
    connfd = connect(sockfd, (struct sockaddr *) &servaddr, sizeof(servaddr));
    if(connfd < 0) {
        fprintf(stderr,"connnect error:%s\n\a",strerror(errno));
        exit(0);
    }
    //将昵称发送给服务器
    send(sockfd, name, sizeof name, 0);
    pid = fork();
    for( ; ; ){
        if(pid > 0){//父进程发送信息
            struct tm *p;
            time_t timep;
            time(&timep);
            p = localtime(&timep);
            strftime(buffer, sizeof(buffer), "%Y/%m/%d %H:%M:%S", p);
            //输出发送时间和昵称
            strcat(buffer," \n\tname ->");
            strcat(buffer,name);
            strcat(buffer,":\n\t\t  ");
            memset(buf,0,MAXLINE);
            read(fileno(stdin), buf, MAXLINE);
            //如果输入e退出
            if(strncmp("e",buf,1)==0){
                printf("该客户端下线...\n");
                strcat(buffer,"退出聊天室!");
                if((send(sockfd,buffer,MAXLINE,0)) <= 0) printf("send error");
                close(sockfd);
                sockfd = -1;
                exit(0);
            }
            else{ //将聊天内容拼接到buffer中
                strncat(buffer,buf,strlen(buf)-1);
                strcat(buffer,"\n");              
                if((send(sockfd,buffer,MAXLINE,0)) <= 0) printf("send error");
            }
        }
        else if(pid == 0){ //子进程用于接收信息
            memset(buffer, 0, MAXLINE);
            if(sockfd > 0){
                if((recv(sockfd,buffer,MAXLINE,0)) <= 0){
                    close(sockfd);
                    exit(1);
                }
                printf("%s\n",buffer);
            }
        }
        
    }
    close(sockfd);
    return 0;
}

 


//server
#include <sys/socket.h> #include <strings.h> #include <stdio.h> #include <stdlib.h> #include <arpa/inet.h> #include <unistd.h> #include <string.h> #include <errno.h> #include <signal.h> #include <sys/wait.h> #include <errno.h> #include <sys/shm.h> #include <time.h> #include <pthread.h> #define MAXLINE 1024 #define SHMADD_SIZE 2048 #define BACKLOG 3 #define SERV_PORT 4395 int sockfd, fd[BACKLOG], i = 0; int get_sockfd(){ struct sockaddr_in servaddr; if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) { fprintf(stderr,"socket error:%s\n\a",strerror(errno)); exit(0); } bzero(&servaddr, sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_port = htons(SERV_PORT); servaddr.sin_addr.s_addr = htonl(INADDR_ANY); if(bind(sockfd, (struct sockaddr *) & servaddr, sizeof servaddr) == -1){ fprintf(stderr,"bind error:%s\n\a",strerror(errno)); exit(0); } if(listen(sockfd, BACKLOG) == -1){ fprintf(stderr,"listen error:%s\n\a",strerror(errno)); exit(0); } return sockfd; } int main(int argc, char **argv) { int new_fd; socklen_t sin_size; pid_t ppid, pid; struct sockaddr_in cliaddr; char buffer[MAXLINE]; char *shmadd, *shmadd_buffer; int shmid = shmget(IPC_PRIVATE, SHMADD_SIZE, 0777); if(shmid < 0) { fprintf(stderr,"shmid error:%s\n\a",strerror(errno)); exit(0); } //启动对该共享内存的访问,并把共享内存连接到当前进程的地址空间 shmadd = shmat(shmid, 0, 0); if(shmadd < (char*)0) { fprintf(stderr,"shmat error:%s\n\a",strerror(errno)); exit(0); } int sockfd = get_sockfd(); for( ; ; ){ sin_size = sizeof(cliaddr); new_fd = accept(sockfd, (struct sockaddr*) &cliaddr, &sin_size); if(new_fd == -1) { fprintf(stderr,"accept error:%s\n\a",strerror(errno)); exit(0); } fd[i++] = new_fd; printf("\n已连接了客户端%d : %s:%d \n",i , inet_ntoa(cliaddr.sin_addr), ntohs(cliaddr.sin_port)); memset(buffer, 0, MAXLINE); strcpy(buffer,"\n——————————————————欢迎进入聊天室 ———————————————————————\n"); send(new_fd, buffer, MAXLINE, 0); ppid = fork(); if(ppid == 0){ recv(new_fd, buffer, MAXLINE, 0);//昵称 strcat( buffer," 进入了聊天室...."); for(i=0;i<BACKLOG;i++) { if(fd[i]!=-1) send(fd[i],buffer,strlen(buffer),0); } pid = fork();for( ; ; ){ if(pid > 0){//父进程接收信息 memset(buffer, 0, MAXLINE); if((recv(new_fd,buffer,MAXLINE,0)) <= 0){ close(new_fd); exit(1); } memset(shmadd, 0, SHMADD_SIZE); strncpy(shmadd, buffer, SHMADD_SIZE); printf("%s\n", buffer); } if(pid == 0){ //子进程发送信息 sleep(1); if(strcmp(buffer,shmadd) != 0){ strcpy(buffer,shmadd); if(new_fd > 0){ if(send(new_fd,shmadd,strlen(shmadd),0) == -1) fprintf(stderr,"send error:%s\n\a",strerror(errno)); memset(shmadd, 0, SHMADD_SIZE); strcpy(shmadd,buffer); } } } } } } free(buffer); close(new_fd); close(sockfd); return 0; }

 

posted @ 2020-12-24 21:46  clara-zhang  阅读(458)  评论(0)    收藏  举报