网络socket多线程编程之服务器

关于多线程相关的学习,请参考下面的链接。本章主要记录网络socket多线程编程之服务器!
https://blog.youkuaiyun.com/makunIT/article/details/104605225

网络socket多线程编程之服务器

1、在编写程序之前,我们先了解一下它执行的流程图
在这里插入图片描述
2、根据流程图写多线程服务器,代码如下:

/*********************************************************************************
 *      Copyright:  (C) 2020 makun<1394987689@qq.com>
 *                  All rights reserved.
 *
 *       Filename:  djc_socket_server.c
 *    Description:  This file 
 *                 
 *        Version:  1.0.0(2020年02月28日)
 *         Author:  makun <1394987689@qq.com>
 *      ChangeLog:  1, Release initial version on "2020年02月28日 13时59分51秒"
 *                 
 ********************************************************************************/
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <getopt.h>
#include <stdlib.h>
#include <ctype.h>
#include <pthread.h>
typedef void *(THREAD_BODY)  (void *thread_arg);//将void *(THREAD_BODY)定义一种新的类型(void * thread_arg)

void *thread_worker(void *ctx);
int thread_start(pthread_t *thread_id, THREAD_BODY *thread_workbody,void *thread_arg);//自己定义了一个新的函数,一个函数就是一个功能,用来设置线程属性

void print_usage(char *progname)
{
     printf("%s usage: \n", progname);
     printf("-p(--port): sepcify server listen port.\n");
     printf("-h(--Help): print this help information.\n");
    
     return ;
}

int main (int argc, char **argv)
{

    int                 sockfd = -1;
    int                 clifd;
    struct sockaddr_in  servaddr;
    struct sockaddr_in  cliaddr;
    socklen_t           len;
    int                 port = 0;
    int                 ch;
    int                 rv = -1;
    int                 on = 1;
    pthread_t           tid;//定义一个线程
struct option opts[] = {
     {"port", required_argument, NULL, 'p'},
     {"help", no_argument, NULL, 'h'},
     {NULL, 0, NULL, 0}
      };
 while( (ch=getopt_long(argc, argv, "p:h", opts, NULL)) != -1 )
      {
           switch(ch)
                {
                    case 'p':
                        port=atoi(optarg);
                        break;
                    case 'h':
                        print_usage(argv[0]);
                        return 0;
                }
    }
  if( !port )
  {
      print_usage(argv[0]);
      return 0;
  }

    sockfd=socket(AF_INET,SOCK_STREAM ,0);
        
    if( sockfd < 0)
    {
        printf("create socket failure:%s\n",strerror(errno));
        return -1;
    }
    printf("create socket successfuly: %d\n", sockfd);

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

    memset(&servaddr,0,sizeof(servaddr));
    servaddr.sin_family=AF_INET;
    servaddr.sin_port = htons(port);
    servaddr.sin_addr.s_addr=htonl(INADDR_ANY);
    rv = bind(sockfd,(struct sockaddr *)&servaddr,sizeof(servaddr));
    if(rv < 0)
    {
        printf("socket[%d] bind to port[%d] failure:%s\n",sockfd,port,strerror(errno));
        return -2;
    }
      
    listen(sockfd,13);
    printf("start to listen on port [%d]\n", port);
    while(1)
    {
        printf("start accept new client incoming..\n");
        clifd=accept(sockfd,(struct sockaddr *)&cliaddr,&len);
        if(clifd < 0)
    {
        printf("accept client failure:%s\n", strerror(errno));
        continue;
    }

         printf("accept new clienr[%s:%d] successfully\n", inet_ntoa(cliaddr.sin_addr),ntohs(cliaddr.sin_port));
    

        thread_start(&tid,thread_worker,(void *)clifd );//调用thread_start
    }
    close(sockfd);
    return 0;
}
    int thread_start(pthread_t * thread_id,THREAD_BODY *thread_workbody,void *thread_arg)
{
        int                rv = -1;
        pthread_attr_t     thread_attr;

        if(pthread_attr_init(&thread_attr))//对线程属性初始化
        {
            printf("pthread_attr_init failure:%s\n",strerror(errno));
            goto cleanup;
        }
        if( pthread_attr_setstacksize(&thread_attr,120*1024))
        {
            printf("pthread_attr_setstacksize failure:%s\n",strerror(errno));
            goto cleanup;
        }
        if( pthread_attr_setdetachstate(&thread_attr,PTHREAD_CREATE_DETACHED))
        {
            printf("pthread_attr_setdetachstate failure:%s\n",strerror(errno));
            goto cleanup;
        }
        if(pthread_create(thread_id,&thread_attr,thread_worker,thread_arg))//为什么会创建这个线程?
        {
            printf("create thread failure:%s\n",strerror(errno));
            goto cleanup;
        }
        rv = 0;
cleanup:
        pthread_attr_destroy(&thread_attr);//用完摧毁
        return rv;
}
void *thread_worker(void *ctx)
{
    int clifd;
    int rv;
    char buf[1024];
    int i;
    if(!ctx)
    {
        printf("invalid input argument in %s\n",__FUNCTION__);
        pthread_exit(NULL);
    }
    clifd = (int)ctx;

    printf("child thread start to commuicate with socket client...\n");

    while(1)
    {
        memset(buf , 0, sizeof(buf));
    
        rv=read(clifd,buf,sizeof(buf));
        if(rv < 0)
        {
             printf("Read data from client sockfd[%d] failure:%s\n",clifd,strerror(errno));
             close(clifd);
             pthread_exit(NULL);
    
        }
        else if( rv == 0)
        {
             printf("socket[%d]get disconnected\n", clifd);
             close(clifd);
             pthread_exit(NULL);
        }
        else if( rv > 0)
        {
            printf("read %d bytss data from server:%s\n",rv,buf);
        }
        for(i=0;i<rv; i++)
        {
            buf[i]=toupper(buf[i]);//小写字母转大写字母
        }
        rv=write(clifd, buf, rv);
       if(rv < 0)
       {
           printf("Write to client by sockfd[%d] failure: %s\n", clifd, strerror(errno));
           close(clifd);
           pthread_exit(NULL);
       }
    }
}

linux下运行结果如下:在这里插入图片描述
在这里插入图片描述
结果分析:使用多线程可以实现服务器和多个客户端得通信,如上图,我用两个Tcp Test Tool建立与服务器得通信,使用多线程的方式,因为可以使用共享的全局变量,所以线程间的通信(数据交换)变得非常高效。但是在Unix系统中,一个进程包含很多东西,包括可执行程序以及一大堆的诸如文件描述符地址空间等资源。在很多情况下,完成相关任务的不同代码间需要交换数据。如果采用多进程的方式,进程的创建所花的时间片要比线程大些,另外进程间的通信比较麻烦,需要在用户空间和内核空间进行频繁的切换,开销很大。所以多线程服务器对于数据交换变得高效起来。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值