linux上的socket通讯(二)多线程方案
一、框架方案
accept是主线程,不断等待客户端的连接,一旦有客户端连接过来,那么创建新的线程用于通讯
注意资源共享:栈空间不共享,全局区和堆空间是共享的;
二、多线程的并发实现
1.主进程
(1)server建立的步骤中,accept连接之后,需要创建一个新的子线程用于处理当前连接的客户端,将通讯、接受消息等放置到子线程中处理;
(2)accept建立的连接,得到了客户端的地址信息以及socket的文件描述符,这些都需要作为参数传递给子进程,因此建立一个结构体并声明一个结构体变量用于存放这些结果;
//information struct struct sockInfo { struct sockaddr_in addr;//address info int fd;//file description }; struct sockInfo infos[512]; void OpenSocketServer(){ //1.socket-listener... //2.bind... //3.listen... //初始化结构体 int max=(sizeof(infos)/sizeof(infos[0])); for (size_t i = 0; i < max; i++) { bzero(&infos[i],sizeof(infos[i])); infos[i].fd=-1; } //等待接受客户端的连接 while(1){ //从结构体数据中寻找到一个空闲的元素 struct sockInfo* pInfo; for (size_t i = 0; i < max; i++) { if(infos[i].fd==-1){ pInfo=&infos[i]; break; } } //pinfo socklen_t sock_len=sizeof(sockaddr_in); int client=accept(listener,(struct sockaddr*)&pInfo->addr,&sock_len); if(client==-1){ perror("accept error"); break; } pInfo->fd=client; //创建子线程 pthread_t tid; pthread_create(&tid,NULL,working,pInfo); pthread_detach(tid);//子线程有可能随着连接终止而会终止,但是不能影响主线程的正常运行,因此在这里将主线程与子线程分离 } close(listener); }
2.子线程
void* working(void* arg){ //参数中得到对应的结构体 struct sockInfo* ppinfo=(struct sockInfo*)arg; //打印出客户端的信息 char ip[32]; printf("client ip:%s,port:%d\n", inet_ntop(AF_INET,&ppinfo->addr.sin_addr.s_addr,ip,sizeof(ip)), ntohs(ppinfo->addr.sin_port)); while(1){ //接受数据 char buf[1024]; int len=recv(ppinfo->fd,buf,sizeof(buf),0); if(len>0){ char buf[]="hello,i am server"; send(ppinfo->fd,buf,sizeof(buf),0); } else if(len==0){ printf("client disconnect\n"); break; } else{ perror("receive error"); break; } } close(ppinfo->fd); ppinfo->fd=-1;//关闭的时候需要将文件描述符置为-1,作为空区可以为下次使用; return NULL; }
3.运行效果
注意:gcc编译的时候需要指明多线程,否则编译不通过
gcc -pthread netServer.cpp src/socketServer.cpp src/commonServer.cpp -Iinclude -o net
服务器与三个客户端建立了连接