關於多線程相關的學習,請參考下面的鏈接。本章主要記錄網絡socket多線程編程之服務器!
https://blog.csdn.net/makunIT/article/details/104605225
網絡socket多線程編程之服務器
1、在編寫程序之前,我們先了解一下它執行的流程圖
2、根據流程圖寫多線程服務器,代碼如下:
/*********************************************************************************
* Copyright: (C) 2020 makun<[email protected]>
* All rights reserved.
*
* Filename: djc_socket_server.c
* Description: This file
*
* Version: 1.0.0(2020年02月28日)
* Author: makun <[email protected]>
* 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系統中,一個進程包含很多東西,包括可執行程序以及一大堆的諸如文件描述符地址空間等資源。在很多情況下,完成相關任務的不同代碼間需要交換數據。如果採用多進程的方式,進程的創建所花的時間片要比線程大些,另外進程間的通信比較麻煩,需要在用戶空間和內核空間進行頻繁的切換,開銷很大。所以多線程服務器對於數據交換變得高效起來。