流式(TCP)套接字客户端/服务器编程 (多线程并发服务器)

实现一个基本的流式套接字客户端/服务器通信程序,客户端和服务器按如下步骤交互:

(1)客户端向服务器发出日期时间请求字符串,如:%D %Y %A %T等。

(2)服务器从网络接收到日期时间请求字符串后,根据字符串格式生成对应的日期时间值返回给客户端。



  1. //TCP服务器

    /*

     用法:./server ip port

     说明:该流式套接字服务器程序工作于多线程模式,根据客户端发来的请求格式字符串,服务器回应对应的日期和时间

     */

    #include <stdio.h>

    #include <unistd.h>

    #include <stdlib.h>

    #include <errno.h>

    #include <string.h>

    #include <time.h>

    #include <sys/socket.h>

    #include <sys/types.h>

    #include <arpa/inet.h>

    #include <pthread.h>

    #define BUFSIZE 1024

    #define BACKLOG 128

    static void bail(const char *on_what)

    {

        fputs(strerror(errno),stderr);

        fputs(": ",stderr);

        fputs(on_what, stderr);

        fputc('\n',stderr);

        exit(1);

    }

    time_t td;

    struct tm tm;

    struct TmpSockt

    {

        int connfd;

        sockaddr_in client;

    };

    void *start_routine(void *arg)

    {

        char reqBuf[BUFSIZE];

        char dtfmt[BUFSIZE];

        struct TmpSockt *temp=(TmpSockt*)arg;

        while (1)

        {

           //读取客户端发来的日期时间请求,若客户端没有发送请求,则服务器将阻塞

            long  bytes=read(temp->connfd, reqBuf,sizeof(reqBuf));

            if(bytes<0)

                bail("read()");

            

            

           //服务器检查客户端是否关闭了套接字,此时read操作返回0(EOF)

           //如果客户端关闭了其套接字,则服务器将执行close结束此连接,然后开始接收下一个客户端的连接请求

            if(bytes==0)

            {

                printf("The %ld thread exit...\n",(long)pthread_self());

                close(temp->connfd);

                delete temp;

                pthread_exit(NULL);

                break;

            }

           //向请求字符串尾添加NULL字符构成完整的请求日期时间字符串

            reqBuf[bytes]=0;

            

            

           //获取服务器当前日期和时间

            time(&td);

            tm=*localtime(&td);

            

            

           //根据请求日期字符串的格式串生成应答字符串,不清楚的可以查一下这个函数

            strftime(dtfmt, sizeof(dtfmt), reqBuf, &tm);

           

            //将格式化结果发送给客户端

            bytes=write(temp->connfd, dtfmt,strlen(dtfmt));

            if (bytes<0)

                bail("write()");

        }

        return NULL;

    }

    int main(int argc,char **argv)

    {

        int sockfd,connectfd; //描述符

        pthread_t thread;

        TmpSockt *sockt;

        int portnumber; //端口号

        struct sockaddr_in server;//服务器地址信息

        struct sockaddr_in client;//客户端地址信息

        socklen_t sin_size;

        if ((portnumber=atoi(argv[2]))==-1)

        {

            fprintf(stderr,"格式错误");

            exit(1);

        }

        if ((sockfd=socket(AF_INET,SOCK_STREAM,0))==-1)

        {

            fprintf(stderr,"socket error %s",strerror(errno));

            exit(1);

        }

        

        //设置套接字选项为SO_REUSEADDR

        int opt=SO_REUSEADDR;

        setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));

        bzero(&server, sizeof(server));

        server.sin_family=AF_INET;

        server.sin_port=htons(portnumber);

        if (!inet_aton(argv[1], &server.sin_addr))

        {

            bail("bad address");

        }

       //绑定套接字到相应地址

        if (bind(sockfd, (struct sockaddr*)&server,sizeof(server))==-1)

        {

            fprintf(stderr,"bind error:%s\a\n",strerror(errno));

            exit(1);

        }

       //监听网络连接

        if (listen(sockfd,BACKLOG)==-1)

        {

            fprintf(stderr,"Listen error:%s\a\n",strerror(errno));

            exit(1);

        }

        sin_size=sizeof(struct sockaddr_in);

        printf("waiting for the client's request...\n");

        while (1)

        {

            

            if ((connectfd=accept(sockfd, (structsockaddr*)&client, &sin_size))==-1)

            {

                fprintf(stderr,"accept error:%s\a\n",strerror(errno));

                exit(1);

            }

            fprintf(stdout,"Server got connection from %s\n",inet_ntoa(client.sin_addr));

            

            sockt=new TmpSockt;

            sockt->connfd=connectfd;

            memcpy((void *)&sockt->client, &client,sizeof(client));

           //产生线程,并执行线程函数,处理客服端请求

            if (pthread_create(&thread,NULL,start_routine, (void*)sockt)==-1)

            {

                fprintf(stderr,"pthread_creat() error:%s\a\n",strerror(errno));

                exit(1);

            }

        }

        close(sockfd); //关闭监听

        

        return 0;

    }




客户端:



  1. //TCP客户端

    /*

     

     用法:./client hostname port

     

     说明:本程序使用TCP连接和TCP服务器通信,当连接建立后,向服务器发送如下格式字符串

     

     格式字符串示例:

     (1) %D

     (2) %A %D %H:%M:%S

     (3) %A

     (4) %H:%M:%S

     (5)...

     

     */

    #include <stdio.h>

    #include <stdlib.h>

    #include <unistd.h>

    #include <errno.h>

    #include <string.h>

    #include <netdb.h>

    #include <sys/types.h>

    #include <time.h>

    #include <sys/socket.h>

    #include <arpa/inet.h>

    #define BUFSIZE 1024

    #define backlog 128 //等待队列大小

    static void bail(const char *on_what)

    {

        fputs(strerror(errno),stderr);

        fputs(": ",stderr);

        fputs(on_what, stderr);

        fputc('\n',stderr);

        exit(1);

    }

    int main(int argc,char *argv[])

    {

        int sockfd;//客户端套接字

        char buf[BUFSIZE];

        struct sockaddr_in server_addr;

        int portnumber;

        long nbytes;

        long z;

        char reqBuf[BUFSIZE];

        if (argc!=3)

        {

            printf("输入格式错误\n");

            exit(1);

        }

        if ((portnumber=atoi(argv[2]))<0)

        {

            fprintf(stderr,"Usage:%s hostname portnumber\a\n",argv[0]);

            exit(1);

        }

       //创建客户端套接字

        

        if ((sockfd=socket(PF_INET,SOCK_STREAM,0))==-1)

        {

            fprintf(stderr,"Socket error:%s\a\n",strerror(errno));

            exit(1);

        }

       //创建服务器地址

        memset(&server_addr, 0, sizeof(server_addr));

        server_addr.sin_family=AF_INET;

        server_addr.sin_port=htons(portnumber);

        if (!inet_aton(argv[1], &server_addr.sin_addr))

        {

            bail("bad address");

        }

       //连接服务器

        if (connect(sockfd, (struct sockaddr*)(&server_addr),sizeof(server_addr))==-1)

        {

            fprintf(stderr,"connect error:%s\a\n",strerror(errno));

            exit(1);

        }

        printf("connected to server %s\n",inet_ntoa(server_addr.sin_addr));

       //客户端主循环输入 “quit”退出

        for (; ; )

        {

           //提示输入日期请求格式字符串

            

            fputs("\nEnter fotmat string(^D or 'quit' to exit):",stdout);

            if (!fgets(reqBuf,sizeof(reqBuf),stdin))

            {

                printf("\n");

                break;

            }

           //为日期时间请求字符串添加NULL字符作为结尾,另外同时去掉末尾的换行符

            

            z=strlen(reqBuf);

            if (z>0 && reqBuf[--z]=='\n')

                reqBuf[z]=0;

            

            

            if (z==0)//客户端仅键入Enter

                continue;

            

            

            //输入‘quit’退出

            if(!strcasecmp(reqBuf,"QUIT"))//忽略大小写比较

            {

                printf("press any key to end client.\n");

                getchar();

                break;

            }

           //发送日期时间请求字符串到服务器,注意请求信息中去掉了NULL字符

            

            z=write(sockfd, reqBuf, sizeof(reqBuf));

            printf("client has sent '%s' to the sever\n",reqBuf);

            if (z<0)

                bail("write()");

            

            

            

           //从客户端套接字中读取服务器发回的应答

            if ((nbytes=read(sockfd,buf,sizeof(buf)))==-1)

            {

                fprintf(stderr,"read error:%s\n",strerror(errno));

                exit(1);

            }

           //若服务器由于某种原因关闭了连接,则客户端需要处理此事件

            if(nbytes==0)

            {

                printf("server hs closed the socket.\n");

                printf("press any key to exit...\n");

                getchar();

                break;

            }

            buf[nbytes]='\0';

            

            

            //输出日期时间结果

            printf("result from %s port %u:\n\t'%s'\n",inet_ntoa(server_addr.sin_addr),(unsigned)ntohs(server_addr.sin_port),buf);

            

        }

        close(sockfd);

        return 0;

    }




评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值