前述: 多线程网络服务类似于多进程网络服务模式, 不同之处是多线程是为新到连接启动一个服务线程,而多进程模式是为新到连接启动一个服务进程。
多线程网络服务模式的实现原理:
1> 在主服务线程里进行阻塞式等待,在绑定的端口进行侦听;
2> 若当前有连接到来,则启动一个新线程为其服务,服务结束后,释放线程资源;
多线程网络服务模式的流程图如下:
多线程网络服务模式的优点: 便捷, 高效;
多线程网络服务模式的缺点:
仍存在动态线程申请于释放,有一定的开销,若存在大量用户在线,可能会带来很大的线程间切换开销。
以下是一个多线程网络服务的例子:
//服务器端的代码
#include "../unp.h"
//子进程处理通信过程
void handler(int sock)
{
int sockconn = sock; //得到客户端的套接字
char sendbuf[100];
Oper op;
int result;
while(1)
{
int res = recv(sockconn, (char *)&op, sizeof(op), 0); //接收客户的消息
if(res == -1){
perror("recv");
}
//对客户端的服务请求进行处理
if(op.oper == ADD){ //得到两数的和
result = op.op1 + op.op2;
}else if(op.oper == SUB){ //得到两数的差
result = op.op1 - op.op2;
}else if(op.oper == MUL){ //得到两个数的乘积
result = op.op1 * op.op2;
}else if(op.oper == DIV){ //两个数的除
result = op.op1 / op.op2;
}else if(op.oper == QUIT){ //客户端发出退出服务请求
struct sockaddr_in addrcli;
socklen_t addrlen = sizeof(struct sockaddr); //客户端的地址结构大小
getpeername(sockconn, (struct sockaddr*)&addrcli, &addrlen); //得到对端即客户端的端口号(本例中是由系统自动分配的)
printf("client[%d] quit.\n", addrcli.sin_port);
break;
}
send(sockconn, (char *)&result, sizeof(int), 0); //将服务后得到的结果返回给客户端
}
}
int main(int ac, char *av[])
{
int sockser = socket(AF_INET, SOCK_STREAM, 0); //得到服务器端的套接字
if(sockser == -1){
perror("socket");
}
struct sockaddr_in addrser, addrcli; //服务器或客户端的地址结构
addrser.sin_family = AF_INET; //服务器端所用的协议家族
addrser.sin_port = htons(SERVER_PORT); //设置服务器的端口号
addrser.sin_addr.s_addr = inet_addr(SERVER_IP); //设置服务器端的IP地址
socklen_t addrlen = sizeof(struct sockaddr); //得到服务器的地址结构的大小
int res = bind(sockser, (struct sockaddr*)&addrser, addrlen); //绑定服务器的套接字和服务器的地址结构
if(res == -1){
perror("bind");
}
listen(sockser, QUEUE_SIZE); //监听
int sockconn;
while(1){
sockconn = accept(sockser, (struct sockaddr*)&addrcli, &addrlen); //服务器接受客户端的连接
if(sockconn == -1){
perror("accept");
}
printf("client[%d] connect server ok.\n", addrcli.sin_port);
pthread_create(&tid, NULL, handler, &sockconn); //创建线程,每来一个客户,就启动一个线程
}
close(sockser); //关闭服务器端的套接字
return 0;
}
//客户端的代码
#include "unp.h"
void Input(Oper *op);
void help();
int main(int ac, char *av[])
{
int sockcli = socket(AF_INET, SOCK_STREAM, 0); //得到客户端的套接字
if(sockcli == -1){
perror("socket");
}
struct sockaddr_in addrser; //服务器的地址结构
addrser.sin_family = AF_INET; //设置服务器的协议家族
addrser.sin_port = htons(SERVER_PORT); //设置服务器的端口号
addrser.sin_addr.s_addr = inet_addr(SERVER_IP); //设置服务器的IP地址
socklen_t addrlen = sizeof(struct sockaddr); //得到服务器的地址结构大小
int res = connect(sockcli, (struct sockaddr*)&addrser, addrlen); //连接服务器
if(res == -1){
perror("connect");
}else{
printf("client connect server ok.\n");
}
char cmd[20]; //用于存储客户所请求的服务名称
Oper op; //客户的请求的结构体(包括客户请求的服务类型,输入的数据)
int result; //由服务器发回的请求的服务结果
//客户发送请求并接收请求结果
while(1){
printf("please input cmd:>");
scanf("%s", cmd);
if(strcmp(cmd, "add") == 0){
op.oper = ADD;
Input(&op);
}else if(strcmp(cmd, "sub") == 0){
op.oper = SUB;
Input(&op);
}else if(strcmp(cmd, "mul") == 0){
op.oper = MUL;
Input(&op);
}else if(strcmp(cmd, "div") == 0){
op.oper = DIV;
while(1){
Input(&op);
if(op.op2 == 0){ //除数为0时重新输入
perror("op2 == 0 error");
printf("please input again.\n");
continue;
}
break;
}
}else if(strcmp(cmd, "help") == 0){
help();
continue;
}else if(strcmp(cmd, "quit") == 0){
op.oper = QUIT;
}else{
printf("your input is invalid.\n");
}
int res = send(sockcli, (char *)&op, sizeof(op), 0); //发送客户的请求给服务器
if(res == -1){
perror("send");
}
if(op.oper == QUIT){
break;
}
recv(sockcli, &result, sizeof(int), 0); //接收服务器发送回来的结果
printf("result = %d\n", result);
}
close(sockcli); //关闭客户端的套接字
return 0;
}
void Input(Oper *op) //得到进行操作的两个数
{
printf("please input op1 add op2.\n");
scanf("%d, %d", &op->op1, &op->op2);
}
void help() //提示信息
{
printf("#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*\n");
printf("^^^^^^^^^version^^^^^^^^^ : ^^^^^v1.0^^^^^^^^^^\n");
printf("^^^^^^^^^author^^^^^^^^^^ : ^^^^^haha^^^^^^^^^^\n");
printf("~*~*~*~*~cmd~*~*~*~*~*~*~ ~*~*~describe~*~*~\n");
printf("~*~*~*~*~add~*~*~*~*~*~*~ ~*~*~op1 + op2~*~*\n");
printf("~*~*~*~*~sub~*~*~*~*~*~*~ ~*~*~op1 - op2~*~*\n");
printf("~*~*~*~*~mul~*~*~*~*~*~*~ ~*~*~op1 * op2~*~*\n");
printf("~*~*~*~*~div~*~*~*~*~*~*~ ~*~*~op1 / op2~*~*\n");
printf("#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*\n");
}
//头文件
#include <iostream>
#include <unistd.h>
#include <signal.h>
#include <stdio.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <string.h>
#include <stdlib.h>
#include <sys/socket.h>
#define SERVER_IP "127.0.0.1"
#define SERVER_PORT 7070
#define QUEUE_SIZE 5
typedef enum{ADD, SUB, MUL, DIV, QUIT}OPER_STATE;
typedef struct Oper{
OPER_STATE oper;
int op1;
int op2;
}Oper;