网络编程实验3-并发多线程服务器设计
来源:互联网 发布:淘宝网机械水压开关 编辑:程序博客网 时间:2024/06/05 17:23
网络编程实验3-并发多线程服务器设计
实验目的
本次实验的主要目的是使用多线程实现并发的,面向连接的服务器设计。
实验内容
1、设计多线程的,面向连接的并发服务器
2、改造客户端为面向连接的多线程客户端
基本概念
1、将线程用于并发的、面向连接服务器的算法
主1 创建套接字并将其绑定到所提供服务的熟知地址上。让该套接字保持非连接。
主2 将该端口设置为被动模式,使其准备为服务器所用。
主3 反复调用accept以便接受来自客户的下一个连接请求,并创建新的从线程来处理响应。
从1 由主线程传递来的连接请求(即针对连接的套接字)开始。
从2 用该连接与客户进行交互:读取请求并发回响应。
从3 关闭连接并退出。在处理完来自客户的所有请求后,从线程就退出。
2、线程
线程是一个进程内部的一个控制序列,是一次独立的计算。
(1)创建线程
#include <pthread.h>int pthread_create(pthread_t thread, pthread_attr_t *attr, void *(*start_routine)(void*), void *arg);
返回值: 0 成功 错误号表示失败。
thread: 指向pthread_t类型的变量,新创建的线程标识符;
attr:指向pthread_attr_t线程属性类型的变量,
start_routine: 指向线程函数的指针,线程要执行的代码。
arg: 指向线程参数的指针。
(2)终止线程
线程退出方式:
① 从线程函数中返回,返回值为线程的退出码;
return(retu_val);
② 被同一进程的其他线程终止,即被取消;
pthread_cancel;
③ 执行线程退出调用;
pthread_exit;
exit
线程退出:
#include <pthread.h>void pthread_exit(void *retval);
*retval: void类型的指针。
线程终止:
#include <pthread.h>int pthread_cancel(pthread_t th);
返回值: 0 成功 错误号表示失败
(3)等待线程
等待线程的终止。
#include <pthread.h>int pthread_join(pthread_t th, void **thread_return);
返回值:0 成功 错误码表示失败
(4)线程协调和同步
Linux的同步机制:
① 互斥( mutex)
② 信号量( semaphore)
③ 条件变量( condition variable)
(5)线程的属性
属性: 脱离线程(detached thread)
初始化属性对象
#include <pthread.h>int pthread_attr_init(pthread_attr_t *attr);
返回码:成功 0
失败 错误代码
回收函数 Pthread_attr_destroy(pthread_attr_t *attr);
实验步骤
1.编写头文件sockutil.h
#ifndef SOCKUTIL_H#define SOCKUTIL_H#include<sys/types.h>#include<sys/socket.h>#include<arpa/inet.h>#include<netinet/in.h>#include<netdb.h>#ifndef INADDR_NONE#define INADDR_NONE 0xFFFFFFFF#endifint connectSock(char* host,char* service,char* protocol);int passiveSock(char* service,char* protocol,int qlen);void errexit(char* fmt,...);#endif
2.创建sockutil.c文件,编写通用过程connectSock及passiveSock代码
#include "sockutil.h"#include <stdio.h>#include <stdlib.h>#include <string.h>#include <stdarg.h>#include <unistd.h>#include <errno.h>int connectSock(char* host,char* service,char* protocol){ struct hostent* pHost; struct servent* pServ; struct protoent* pProto; struct sockaddr_in addr; int s,type; memset(&addr,0,sizeof(addr)); addr.sin_family=AF_INET; if((pHost=gethostbyname(host))!=NULL) memcpy(&addr.sin_addr,pHost->h_addr,pHost->h_length); else if((addr.sin_addr.s_addr=inet_addr(host))==INADDR_NONE) errexit("can't get \"%s\" host entry",host); if((pServ=getservbyname(service,protocol))!=NULL) addr.sin_port=pServ->s_port; else if((addr.sin_port=htons((unsigned short)atoi(service)))==0) errexit("can't get \"%s\" service entry",service); if((pProto=getprotobyname(protocol))==0) errexit("can't get \"%s\" protocol entry",protocol); if(strcmp(protocol,"tcp")==0) type=SOCK_STREAM; else type=SOCK_DGRAM; s=socket(PF_INET,type,pProto->p_proto); if(s<0) errexit("can't create socket"); if(connect(s,(struct sockaddr*)&addr,sizeof(addr))<0) errexit("can't connect to %s:%s",host,service); return s;}int passiveSock(char* service,char* protocol,int qlen){ struct servent* pServ; struct protoent* pProto; struct sockaddr_in addr; int s,type,reuse=1; memset(&addr,0,sizeof(addr)); addr.sin_family=AF_INET; addr.sin_addr.s_addr=INADDR_ANY; if((pServ=getservbyname(service,protocol))!=NULL) addr.sin_port=pServ->s_port; else if((addr.sin_port=htons((unsigned short)atoi(service)))==0) errexit("can't get \"%s\" service entry",service); if((pProto=getprotobyname(protocol))==0) errexit("can't get \"%s\" protocol entry",protocol); if(strcmp(protocol,"tcp")==0) type=SOCK_STREAM; else type=SOCK_DGRAM; s=socket(PF_INET,type,pProto->p_proto); if(s<0) errexit("can't create socket"); if(setsockopt(s,SOL_SOCKET,SO_REUSEADDR,&reuse,sizeof(int))<0) errexit("setsockopt err"); if(bind(s,(struct sockaddr*)&addr,sizeof(addr))<0) errexit("can't bind to %s port",service); if(type==SOCK_STREAM&&listen(s,qlen)<0) errexit("can't to listen on %s port",service); return s;}void errexit(char* fmt,...){ va_list arg_ptr; va_start(arg_ptr,fmt); vfprintf(stderr,fmt,arg_ptr); fprintf(stderr,":%s.\n",strerror(errno)); va_end(arg_ptr); exit(errno);}
3.编写头文件socklink.h
#ifndef SOCKLINK_H #define SOCKLINK_H#include<pthread.h> typedef struct SockEle{ void* next; int sock; pthread_t tid; }*pSockEle; extern pSockEle linkHead,linkTail; void linkInsert(int sock,pthread_t tid); void linkDelete(int sock); void linkPrint(void); int linkGetSize(void); #endif//SOCKLINK_H
4.编写socklink.c文件,用来存储发送的消息内容。
#include"socklink.h" #include"sockutil.h" #include<stdio.h> #include<stdlib.h> pSockEle linkHead,linkTail; static int linkSize; static pSockEle Linkmalloc(int sock,pthread_t tid){ pSockEle p=(pSockEle)malloc(sizeof(struct SockEle)); p->sock=sock; p->tid=tid; p->next=NULL; }void linkInsert(int sock,pthread_t tid){ pSockEle p; if(sock<0) errexit("linkInsert err"); p=Linkmalloc(sock,tid); if(linkHead==NULL) linkHead=linkTail=p; else{ linkTail->next=p; linkTail=p; } linkSize++; } void linkDelete(int sock){ pSockEle p,it; if(sock<0) errexit("linkDelete err"); for(p=linkHead;p!=NULL;p=p->next){ if(p->sock==sock){ if(linkHead==linkTail) linkHead=linkTail=NULL; else if(p==linkHead) linkHead=linkHead->next; else if(p==linkTail){ //找出尾部元素的前一个对象 for(it=linkHead;it->next!=linkTail;it=it->next); it->next=NULL; linkTail=it; } else{ //找出待删除元素的前一个对象 for(it=linkHead;it->next!=p;it=it->next); it->next=p->next; } break; } } if(p!=NULL){ free(p); linkSize--; } }void linkPrint(void) { pSockEle p; for(p=linkHead;p!=NULL;p=p->next) printf("%d ",p->sock); printf("\n"); } int linkGetSize(void) { return linkSize; }
5.编写头文件server.h
#ifndef SERVER_H #define SERVER_H #define QLEN 20 #define BUFSIZE 100 #endif
6.编写server.c文件,实现TCP多线程并发服务器
#include"server.h" #include"sockutil.h" #include"socklink.h" #include<stdio.h> #include<string.h> #include<stdlib.h> #include<unistd.h> #include<pthread.h> #include<errno.h> #include<sys/signal.h> #include<sys/select.h> int sockFd; pthread_mutex_t linkLock=PTHREAD_MUTEX_INITIALIZER; void sigint_handler(int sig){ close(sockFd); printf("\nConnection has closed.\n"); exit(0); } void* commWithClient(void* arg){ char buf[BUFSIZE]={0}; char* strWelcome="Welcome To ChatRoom\n"; int n,ret,sock=(int)arg; pSockEle p,tmp=0; //向客户端发送欢迎消息 send(sock,strWelcome,strlen(strWelcome),0); while(1){ while((n=recv(sock,buf,BUFSIZE,0))>0){ //线程加锁 pthread_mutex_lock(&linkLock); //循环处理消息发送任务 for(p=linkHead;p!=NULL;p=p->next){ printf("send to %d\n",p->sock); if((ret=send(p->sock,buf,n,0))<n) tmp=p; } if(tmp!=0){ printf("error:%s.\n",strerror(errno)); linkDelete(tmp->sock); pthread_cancel(tmp->tid); tmp=0; } //线程取消枷锁 pthread_mutex_unlock(&linkLock); } if(n<0) errexit("recv err"); } } int main(int argc,char* argv[]) { int newFd,err; unsigned int alen; struct sockaddr_in addr; pthread_t tid; char IPBuf[16]; if(argc!=2) errexit("Usage:%s port.\n",argv[0]); sockFd=passiveSock(argv[1],"tcp",QLEN); signal(SIGPIPE,SIG_IGN); signal(SIGINT,sigint_handler); while(1) { //多线程处理客户端请求 newFd=accept(sockFd,(struct sockaddr*)&addr,&alen); err=pthread_create(&tid,NULL,commWithClient,(void*)newFd); pthread_mutex_lock(&linkLock); linkInsert(newFd,tid); pthread_mutex_unlock(&linkLock); if(err!=0) errexit("pthread create err"); printf("New Connection to %s\n",inet_ntop(AF_INET,&(addr.sin_addr.s_addr),IPBuf,16)); } }
7.编写头文件client.h
#ifndef CLIENT_H #define CLIENT_H #define BUFSIZE 100#endif
8.编写client.c文件,设计面向连接的多线程客户端
#include"client.h" #include"sockutil.h" #include<stdio.h> #include<string.h> #include<stdlib.h> #include<unistd.h> #include<errno.h> #include<sys/signal.h> #include<sys/select.h> int sockFd; void sigint_handler(int sig) { close(sockFd); printf("\nConnection has closed.\n"); exit(0); } int main(int argc,char* argv[]) { int n; fd_set oset,nset; char buf[BUFSIZE]; if(argc!=3) errexit("Usage:%s hostname port.\n",argv[0]); sockFd=connectSock(argv[1],argv[2],"tcp"); FD_ZERO(&oset); FD_SET(STDIN_FILENO,&oset); FD_SET(sockFd,&oset); nset=oset; signal(SIGPIPE,SIG_IGN); signal(SIGINT,sigint_handler); while(1) { //利用select进行数据收发 if(select(4,&nset,NULL,NULL,0)<0) errexit("select err"); if(FD_ISSET(sockFd,&nset)){ n=recv(sockFd,buf,BUFSIZE,0); if(n<0) errexit("recv err"); if(n>0 && write(STDOUT_FILENO,buf,n)!=n) errexit("write err"); } if(FD_ISSET(STDIN_FILENO,&nset)){ fgets(buf,BUFSIZE,stdin); if((n=send(sockFd,buf,strlen(buf),0))<0) errexit("send err"); } nset=oset; } }
9.编写makefile文件,对已编写完成的代码进行编译运行。
SERVOBJ=sockutil.o server.o socklink.o CLITOBJ=sockutil.o client.o CFLAGS=-lpthread server:${SERVOBJ} gcc -o $@ ${SERVOBJ} ${CFLAGS} client:${CLITOBJ} gcc -o $@ ${CLITOBJ} ${CFLAGS} clean: rm -rf ${CLITOBJ} ${SERVOBJ}
简要说明:Makefile 文件描述了整个工程的编译、连接等规则。为工程编写Makefile 的好处是能够使用一行命令来完成“自动化编译”。SERVEROBJ、CLIENTOBJ为定义的一个变量,gcc命令为编译变量中的文件,-o用来设置编译后的输出文件名称。
rm命令用来删除指定的文件或目录 -r表明同时删除目录下的所有子目录,-f表明强行删除文件或目录,不提示任何信息。
10.显示实验结果
开启终端,执行以下命令对文件进行编译和运行。
执行命令:make clean server client 对文件进行编译。
执行命令:./server 7777运行服务器端。
重新开启三个新终端,执行命令:./client localhost 7777运行客户端。
三个客户端能互相交互,则实验成功。(端口号可以自行设定)
- 网络编程实验3-并发多线程服务器设计
- 网络编程中设计并发服务器,使用多进程与多线程有什么区别?
- 多线程并发服务器编程
- 多线程并发服务器编程
- 多线程并发服务器编程
- 网络并发服务器设计
- 网络编程(6)单进程多线程并发服务器实现
- Linux网络编程——tcp并发服务器(多线程)
- UNIX网络编程——并发服务器(多线程)
- Linux网络编程——tcp并发服务器(多线程)
- Linux网络编程——tcp并发服务器(多线程)
- Linux网络编程(3):并发服务器程序设计
- 【Unix 网络编程】服务器网络编程模型——多线程并发模型
- Liunx网络编程---并发服务器
- 网络编程之并发服务器,分布服务器
- 【Linux的高级应用编程】网络编程中并发服务器的设计模式
- 多线程与并发服务器设计(23-1)
- 多线程与并发服务器设计(23 - 2 )
- Chrome dev tool issue
- HTML用到的小标签
- docker run的--rm选项详解
- minmax()函数
- (一) Spring Boot学习笔记之第一个Spring Boot程序HelloWorld
- 网络编程实验3-并发多线程服务器设计
- Spring Boot项目利用Redis实现session管理
- android动态加载资源
- 五分钟读懂TCP 协议——TCP协议简介
- lua热更新
- pandas <十分钟掌握pandas常用操作>
- Integer Break
- NEUQ网络赛小结
- (4.3)uboot详解——异常和异常向量