zoukankan      html  css  js  c++  java
  • linux C/S 有用户名的聊天程序 only one cilent —— using socket

    服务器端源代码如下:
    #include <stdio.h>
    #include <stdlib.h>
    #include <errno.h>
    #include <string.h>
    #include <sys/types.h>
    #include <netinet/in.h>
    #include <sys/socket.h>
    #include <sys/wait.h>
    #include <unistd.h>
    #include <arpa/inet.h>
    #include <sys/time.h>
    #include <sys/types.h>

    #define MAXBUF 1024
    /************关于本文档********************************************
    *filename: async-server.c
    *purpose: 演示网络异步通讯,这是服务器端程序
    *wrote by: zhoulifa(zhoulifa@163.com) 周立发(http://zhoulifa.bokee.com)
    Linux爱好者 Linux知识传播者 SOHO族 开发者 最擅长C语言
    *date time:2007-01-25 21:22
    *Note: 任何人可以任意复制代码并运用这些文档,当然包括你的商业用途
    * 但请遵循GPL
    *Thanks to: Google.com
    *Hope:希望越来越多的人贡献自己的力量,为科学技术发展出力
    * 科技站在巨人的肩膀上进步更快!感谢有开源前辈的贡献!
    *修改、添加注释 by: nickolas 
    *********************************************************************/

    int main(int argc, char **argv)
    {
        int sockfd, new_fd;
        socklen_t len;
        struct sockaddr_in my_addr, their_addr;   //服务器绑定地址,客户端协议地址
        unsigned int myport, lisnum;
        char buf[MAXBUF + 1];
        char usrname[MAXBUF + 1], hostname[MAXBUF + 1];
        int flag = 1 ;
        fd_set rfds;
        struct timeval tv;
        int retval, maxfd = -1;

        printf("请输入您的用户名:");
        bzero(hostname, MAXBUF + 1);
        fgets(hostname, MAXBUF, stdin);

        if (argv[1])
            myport = atoi(argv[1]);
        else
            myport = 7838;

        if (argv[2])
            lisnum = atoi(argv[2]);
        else
            lisnum = 2;

        //创建套接字,指定通信类型,返回套接字描述字
        if ((sockfd = socket(PF_INET, SOCK_STREAM, 0)) == -1) {   //协议簇:ipv4,套接字类型:字节流(使用TCP),协议类型:缺省0
            perror("socket");
            exit(1);
        }
    /*
    使用sockaddr_in这个结构来设置/获取地址信息。
    struct   sockaddr_in {
         short                    sin_family;    
         u_short                sin_port
         struct  in_addr    sin_addr;
         char                      sin_zero[8];
     };
    sin_family 指代协议族,在socket编程中只能是AF_INET
    sin_port 存储端口号(使用网络字节顺序)
    sin_addr 存储IP地址,使用in_addr这个数据结构
        struct in_addr {
            unsigned long s_addr;
        };
        这个数据结构是由于历史原因保留下来的,主要用作与以前的格式兼容。
      s_addr 按照网络字节顺序存储IP地址
    sin_zero是为了让sockaddr与sockaddr_in两个数据结构保持大小相同而保留的空字节。
    */
        bzero(&my_addr, sizeof(my_addr));  //extern void bzero(void *s, int n);置字节字符串s的前n个字节为零。
        my_addr.sin_family = PF_INET;          //协议簇
        my_addr.sin_port = htons(myport);   //将一个16位数(端口号)从主机字节顺序转换成网络字节顺序。
        if (argv[3])   //按照网络字节顺序存储IP地址
            my_addr.sin_addr.s_addr = inet_addr(argv[3]);
        else
            my_addr.sin_addr.s_addr = INADDR_ANY;//INADDR_ANY 通配符地址告诉系统假如有多个接口,本服务器进程将接受任意一个Internet 接口的连接。

    //int bind( int sockfd, const struct sockaddr *myaddr, socket_t   addrlen );给socket分配本地协议地址(ipaddr和port).
        if (bind(sockfd, (struct sockaddr *) &my_addr, sizeof(struct sockaddr))
            == -1) {
            perror("bind");
            exit(1);
        }
    //int listen(int sockfd, int backlog);表明服务器进程愿意接收客户进程的连接请求,为套接字建立一个连接请求侦听队列。处理可能同时出现的几个连接请求。
        if (listen(sockfd, lisnum) == -1) {
            perror("listen");
            exit(1);
        }

        while (1) {
        /*需要再次接收用户名*/
        flag = 1;
       
            printf("\n----等待新的连接到来开始新一轮聊天……\n");
            len = sizeof(struct sockaddr);  //协议地址长度
    //int accept (int sockfd, struct sockaddr *cliaddr, socklen_t *addrlen);用于服务器进程接收客户进程的连接请求。函数返回值为新生成的用于连接的套接字(四元组形成)。
            if ((new_fd =
                 accept(sockfd, (struct sockaddr *) &their_addr,
                        &len)) == -1) {
                perror("accept");
                exit(errno);
            } else
                printf("server: got connection from %s, port %d, socket %d\n",
                       inet_ntoa(their_addr.sin_addr),
                       ntohs(their_addr.sin_port), new_fd);//ntohs 将一个无符号短整形数从网络字节顺序转换为主机字节顺序。

         send(new_fd, hostname, strlen(hostname) - 1, 0);
            /* 开始处理每个新连接上的数据收发 */
            printf("\n准备就绪,可以开始聊天了……直接输入消息回车即可发信息给对方\n");
            while (1) {
                /* 把集合清空 */
                FD_ZERO(&rfds);
                /* 把标准输入句柄0加入到集合中 */
                FD_SET(0, &rfds);
                maxfd = 0;
                /* 把当前连接句柄new_fd加入到集合中 */
                FD_SET(new_fd, &rfds);
                if (new_fd > maxfd)
                    maxfd = new_fd;
                /* 设置最大等待时间 */
                tv.tv_sec = 1;
                tv.tv_usec = 0;
                /* 开始等待 */
                retval = select(maxfd + 1, &rfds, NULL, NULL, &tv);
                if (retval == -1) {
                    printf("将退出,select出错! %s", strerror(errno));
                    break;
                } else if (retval == 0) {
                    /* printf
                       ("没有任何消息到来,用户也没有按键,继续等待……\n"); */
                    continue;
                } else {
                    if (FD_ISSET(0, &rfds)) {
                        /* 用户按键了,则读取用户输入的内容发送出去 */
                        bzero(buf, MAXBUF + 1);
    //标准输入 char *fgets(char *buf, int n, FILE  *fp);   从文件流读入一行。
                        fgets(buf, MAXBUF, stdin);
    //strncasecmp()用来比较参数s1和s2字符串前n个字符,比较时会自动忽略大小写的差异,若参数s1和s2字符串相同则返回0 s1若大于s2则返回大于0的值 s1若小于s2则返回小于0的值
                        if (!strncasecmp(buf, "quit", 4)) {
                            printf("自己请求终止聊天!\n");
                            break;
                        }
    //int send ( int sd, const void *msg, size_t  len, int flags );msg:指向用于发送/接收数据的缓冲区的指针。len: 是缓冲区的字节数。
                        len = send(new_fd, buf, strlen(buf) - 1, 0);
    //            hostname[len]='\0';
    //            slen = strlen(hostname);
    //            hostname[slen-1] = '\0';
                        if (len > 0)
                            printf
                                ("%s的消息: \'%s\'发送成功,共发送了%d个字节!\n",
                                 hostname, buf, len);
                        else {
                            printf
                                ("消息'%s'发送失败!错误代码是%d,错误信息是'%s'\n",
                                 buf, errno, strerror(errno));
                            break;
                        }
                    }
                    if (FD_ISSET(new_fd, &rfds)) {
                if(flag == 1) {
                 /*接收客户端用户名*/
                bzero(usrname, MAXBUF + 1);
                recv(new_fd, usrname, MAXBUF, 0);
                flag = 0;
                }
                else {
                        /* 当前连接的socket上有消息到来则接收对方发过来的消息并显示 */
                        bzero(buf, MAXBUF + 1);
                        /* 接收客户端的消息 */
    //int recv  ( int sd,  void *msg, size_t  len, int flags );
                        len = recv(new_fd, buf, MAXBUF, 0);
                        if (len > 0)
                            printf
                                ("收到来自%s的消息:'%s',共%d个字节的数据\n",
                                 usrname, buf, len);
                        else {
                            if (len < 0)
                                printf
                                    ("消息接收失败!错误代码是%d,错误信息是'%s'\n",
                                     errno, strerror(errno));
                            else
                                printf("对方退出了,聊天终止\n");
                            break;
                        }
                }
                    }
                }
            }
            close(new_fd);
            /* 处理每个新连接上的数据收发结束 */
            printf("还要和其它连接聊天吗?(no->退出)");
            fflush(stdout);//清除文件缓冲区,文件以写方式打开时将缓冲区内容写入文件.原型:int fflush(FILE *stream)
            bzero(buf, MAXBUF + 1);
            fgets(buf, MAXBUF, stdin);
            if (!strncasecmp(buf, "no", 2)) {
            flag = 0;
                printf("终止聊天!\n");
                break;
            }
        }

        close(sockfd);//关闭套接字, 发送队列中的余留数据, 终止TCP连接。
        return 0;
    }
    客户端源代码如下:
    #include <stdio.h>
    #include <string.h>
    #include <errno.h>
    #include <sys/socket.h>
    #include <resolv.h>
    #include <stdlib.h>
    #include <netinet/in.h>
    #include <arpa/inet.h>
    #include <unistd.h>
    #include <sys/time.h>
    #include <sys/types.h>

    #define MAXBUF 1024
    //int select(int maxfd, fd_set *readfds, fd_set *writefds, fe_set *exceptfds, const struct timeval *timeout);
    /*select的第一个参数是文件描述符集中要被检测的比特数,这个值必须至少比待检测的最大文件描述符大1;
    参数readfds指定了被读监控的文件描述符集;
    参数 writefds指定了被写监控的文件描述符集;
    参数exceptfds指定了被例外条件监控的文件描述符集。
    参数timeout起了定时器的作用:到了指定的时间,无论是否有设备准备好,都返回调用。*/
    /************关于本文档********************************************
    // *filename: ssync-client.c
    *purpose: 演示网络异步通讯,这是客户端程序
    *wrote by: zhoulifa(zhoulifa@163.com) 周立发(http://zhoulifa.bokee.com)
    Linux爱好者 Linux知识传播者 SOHO族 开发者 最擅长C语言
    *date time:2007-01-25 21:32
    *Note: 任何人可以任意复制代码并运用这些文档,当然包括你的商业用途
    * 但请遵循GPL
    *Thanks to: Google.com
    *Hope:希望越来越多的人贡献自己的力量,为科学技术发展出力
    * 科技站在巨人的肩膀上进步更快!感谢有开源前辈的贡献!
    *修改、添加注释 by: nickolas 
    *********************************************************************/
    int main(int argc, char **argv)
    {
        int sockfd, len;
        struct sockaddr_in dest;
        char buffer[MAXBUF + 1];
        fd_set rfds;
        struct timeval tv;
        int retval, maxfd = -1;
        char usrname[MAXBUF + 1],hostname[MAXBUF + 1];
        int flag = 1;
     
        printf("请输入您的用户名:");
        bzero(hostname, MAXBUF + 1);
        fgets(hostname, MAXBUF, stdin);

        if (argc != 3) {
            printf
                ("参数格式错误!正确用法如下:\n\t\t%s IP地址 端口\n\t比如:\t%s 127.0.0.1 80\n此程序用来从某个 IP 地址的服务器某个端口接收最多 MAXBUF 个字节的消息",
                 argv[0], argv[0]);
            exit(0);
        }
        /* 创建一个 socket 用于 tcp 通信 */
        if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
            perror("Socket");
            exit(errno);
        }

        /* 初始化服务器端(对方)的地址和端口信息 */
        bzero(&dest, sizeof(dest));
        dest.sin_family = AF_INET;
        dest.sin_port = htons(atoi(argv[2]));
    //int inet_aton(const char *cp, struct in_addr *inp);
    //inet_aton() 转换网络主机地址cp为二进制数值,并存储在struct in_addr结构中,即第二个参数*inp,函数返回非0表示cp主机有地有效,返回0表示主机地址无效。
        if (inet_aton(argv[1], (struct in_addr *) &dest.sin_addr.s_addr) == 0) {
            perror(argv[1]);
            exit(errno);
        }

        /* 连接服务器 */
        if (connect(sockfd, (struct sockaddr *) &dest, sizeof(dest)) != 0) {
            perror("Connect ");
            exit(errno);
        }
        /*发用户名给服务器*/
        send(sockfd, hostname, strlen(hostname) - 1, 0);
       
        printf ("\n准备就绪,可以开始聊天了……直接输入消息回车即可发信息给对方\n");
        while (1) {
            /* 把集合清空 */
            FD_ZERO(&rfds);
            /* 把标准输入句柄0加入到集合中 */
            FD_SET(0, &rfds);
            maxfd = 0;
            /* 把当前连接句柄sockfd加入到集合中 */
            FD_SET(sockfd, &rfds);
            if (sockfd > maxfd)
                maxfd = sockfd;
            /* 设置最大等待时间 */
            tv.tv_sec = 1;
            tv.tv_usec = 0;
            /* 开始等待 */

            retval = select(maxfd + 1, &rfds, NULL, NULL, &tv);
            if (retval == -1) {
                printf("将退出,select出错! %s", strerror(errno));
                break;
            } else if (retval == 0) {
                /* printf
                   ("没有任何消息到来,用户也没有按键,继续等待……\n"); */
                continue;
            } else {
                if (FD_ISSET(sockfd, &rfds)) {
            if(flag == 1) {
            /*接收服务器端用户名*/
            bzero(usrname, MAXBUF + 1);
            recv(sockfd, usrname, MAXBUF, 0);
            flag = 0;
            }
            else {
                    /* 连接的socket上有消息到来则接收对方发过来的消息并显示 */
                    bzero(buffer, MAXBUF + 1);
                    /* 接收对方发过来的消息,最多接收 MAXBUF 个字节 */
                    len = recv(sockfd, buffer, MAXBUF, 0);
                    if (len > 0)
                        printf("收到来自%s的消息:'%s',共%d个字节的数据\n",
                             usrname, buffer, len);
                    else {
                        if (len < 0)
                            printf("消息接收失败!错误代码是%d,错误信息是'%s'\n",
                                 errno, strerror(errno));
                        else
                            printf("对方退出了,聊天终止!\n");
                        break;
                         }
                    }
                }
                if (FD_ISSET(0, &rfds)) {
                    /* 用户按键了,则读取用户输入的内容发送出去 */
                    bzero(buffer, MAXBUF + 1);
                    fgets(buffer, MAXBUF, stdin);
                    if (!strncasecmp(buffer, "quit", 4)) {
                        printf("自己请求终止聊天!\n");
                        break;
                    }
                    /* 发消息给服务器 */
                    len = send(sockfd, buffer, strlen(buffer) - 1, 0);
            buffer[len]='\0';
    //        slen = strlen(hostname);
    //        hostname[slen-1] = '\0';
                    if (len < 0) {
                        printf
                            ("消息'%s'发送失败!错误代码是%d,错误信息是'%s'\n",
                             buffer, errno, strerror(errno));
                        break;
                    } else
                        printf("%s的消息:%s发送成功,共发送了%d个字节!\n",
                             hostname, buffer, len);
               
                }
            }
        }
        /* 关闭连接 */
        close(sockfd);
        return 0;
    }
    编译用如下命令:
    gcc -Wall async-server.c -o server
    gcc -Wall async-client.c -o client
    运行用如下命令:
    ./server 7838 1
    ./client 127.0.0.1 7838
  • 相关阅读:
    vue+axios实现文件下载
    处理 Vue 单页面应用 SEO
    JSON.parse()与JSON.stringify()的区别
    JS设置Cookie过期时间
    <div>标签仿<textarea>。contentEditable=‘true’,赋予非表单标签内容可以编辑
    CSS上下左右居中的几种方法
    jqGrid 修改单元格值或者替换图片及其他
    对于Web性能优化, 了解和经验
    初步了解反射案列
    JS IndexOf移除符合规则的一项
  • 原文地址:https://www.cnblogs.com/hucn/p/1597001.html
Copyright © 2011-2022 走看看