服务器端在监听客户端的请求采用了一个线程,在为每个客户请求时又安排了一个线程。
下面是用来监听客户端请求的线程所执行的函数:
int listen_fd,conn_fd; //监听socket,连接socket
struct sockaddr_in server,cli_addr;
int ret,buffer_index,i,cli_len;
unsigned long ip_addr;
int flag = 1;
pthread_t listentid,servicetid; //监听线程ID,服务线程ID
/*监听线程*/
void* listen_thread(void *p)
{
char msg[512];
while(1){
/*接受远端的TCP连接请求*/
cli_len = sizeof(cli_addr);
conn_fd = accept(listen_fd,(struct sockaddr *)&cli_addr,&cli_len);
if(conn_fd<0)
continue;
ip_addr = ntohl(cli_addr.sin_addr.s_addr);
/*检测重复连接*/
check_connection(ip_addr);
/*分配线程缓冲区*/
buffer_index = get_free_buff();
if(buffer_index<0){
sprintf(msg,"没用空闲的线程缓冲区。\n");
add_info(msg);
close(conn_fd);
continue;
}
/*填写服务线程需要的信息*/
pthread_mutex_lock(&buff_mutex);
thread_buff[buffer_index].buff_index = buffer_index;
thread_buff[buffer_index].ip_addr = ip_addr;
thread_buff[buffer_index].conn_fd = conn_fd;
thread_buff[buffer_index].buff_status = BUFF_OCCUPIED;
pthread_mutex_unlock(&buff_mutex);
/*创建新底服务线程*/
ret = pthread_create(&servicetid,NULL,service_thread,&thread_buff[buffer_index]);
if(ret == -1){
sprintf(msg,"创建服务器线程出错!\n");
add_info(msg);
close(conn_fd);
/*释放线程缓冲区*/
pthread_mutex_lock(&buff_mutex);
thread_buff[buffer_index].tid = 0;
thread_buff[buffer_index].buff_status = BUFF_FREED;
pthread_mutex_unlock(&buff_mutex);
}
}
}
为客户端安排的服务线程调用的函数是:void* service_thread(void *p)
#ifndef _SERVICE_THREAD_H
#define _SERVICE_THREAD_H
#include "ticket.h"
#include "threadbuff.h"
static void thread_err(char *s, int index)
{
int i;
char msg[512];
sprintf(msg,"线程%d发生致命错误:%s\n",(unsigned int)pthread_self(),s);
add_info(msg);
free_buff(index);
pthread_exit(NULL);
}
void* service_thread(void *p)
{
int conn_fd;
/*线程缓冲区标号*/
int buff_index;
char send_buf[1024],recv_buf[512];
int ret,i,cnt;
uint16_t nbyte;
struct sockaddr_in peer_name;
int peer_name_len;
unsigned int required_ticket_num;
int pos;
thread_buff_struct *pstruct;
char msg[512];
/*获取线程使用底线程缓冲区的序号*/
pstruct = (thread_buff_struct *)p;
buff_index = pstruct->buff_index;
/*从线程缓冲区中获取通信使用底套接子描述符,通过accept获取*/
conn_fd = pstruct->conn_fd;
/*打印远程主机地址*/
peer_name_len = sizeof(peer_name);
ret = getpeername(conn_fd,(struct sockaddr*)&peer_name,&peer_name_len);
if(ret==-1)
thread_err("获取远端主机地址出错",buff_index);
sprintf(msg,"新链接--->线程ID:%d,接连ID:%d,线程缓冲区索引号:%d,远端地址:%s,端口号:%d\n", (unsigned short)pstruct->tid,conn_fd,buff_index,inet_ntoa(peer_name.sin_addr),ntohs(peer_name.sin_port));
add_info(msg);
while(1){
/*从网络中获取数据记录*/
ret = recv(conn_fd,recv_buf,sizeof(message),0);
/*接受出错*/
if(ret == -1){
sprintf(msg,"线程:%d 在链接:%d接收出错。链接将关闭\n",(unsigned short)pstruct->tid, conn_fd);
add_info(msg);
thread_err(msg, buff_index);
}
/*ret==0说明客户端链接已关闭*/
if(ret == 0){
sprintf(msg,"线程 %d 的链接(ID:%d) 客户端已关闭。服务器端链接也将关闭。\n",(unsigned short)pstruct->tid,conn_fd);
add_info(msg);
close(conn_fd);
free_buff(buff_index);
pthread_exit(NULL);
}
/*ret为其他值说明接受到了客户端底请求消息*/
init_message();
memcpy(&message,recv_buf,sizeof(message));
switch(message.msg_type){
case DISCONNECT:
sprintf(msg,"线程 %d 的链接(ID: %d)客户端已关闭。服务器链接也将关闭。\n",(unsigned short)pstruct->tid,conn_fd);
add_info(msg);
close(conn_fd);
free_buff(buff_index);
pthread_exit(NULL);
break;
case BUY_TICKET:
for(i=0;i<FLIGHT_NUM;i++){
pthread_mutex_lock(&ticket_list[i].ticket_mutex);
if(ticket_list[i].flight_ID == message.flight_ID){
if(ticket_list[i].ticket_num >= message.ticket_num){
//剩余票数大于请求票数
message.msg_type = BUY_SUCCEED;
message.ticket_total_price=message.ticket_num*ticket_list[i].ticket_price;
ticket_list[i].ticket_num -= message.ticket_num;
pthread_mutex_unlock(&ticket_list[i].ticket_mutex);
sprintf(msg,"购买成功!航班号:%d,票数:%d,总票价:%d\n",message.flight_ID,message.ticket_num,message.ticket_total_price);
add_info(msg);
memcpy(send_buf,&message,sizeof(message));
ret = send(conn_fd,send_buf,sizeof(message),0);
if(ret<0)
thread_err("发送数据出错\n",buff_index);
break;
}else{
//剩余票数不足,购买失败
message.msg_type = BUY_FAILED;
required_ticket_num = message.ticket_num;
message.ticket_num = ticket_list[i].ticket_num;
pthread_mutex_unlock(&ticket_list[i].ticket_mutex);
sprintf(msg,"购买失败!航班号:%d,剩余票数:%d,请求票数:%d\n",message.flight_ID,message.ticket_num,required_ticket_num);
add_info(msg);
memcpy(send_buf,&message,sizeof(message));
ret=send(conn_fd,send_buf,sizeof(message),0);
if(ret<0)
thread_err("发送数据出错\n",buff_index);
break;
}
}
pthread_mutex_unlock(&ticket_list[i].ticket_mutex);
}
break;
case INQUIRE_ONE:
for(i=0;i<FLIGHT_NUM;i++){
pthread_mutex_lock(&ticket_list[i].ticket_mutex);
if(ticket_list[i].flight_ID == message.flight_ID){
message.msg_type = INQUIRE_SUCCEED;
message.ticket_num = ticket_list[i].ticket_num;
message.ticket_total_price = ticket_list[i].ticket_price;
pthread_mutex_unlock(&ticket_list[i].ticket_mutex);
printf("查询成功!航班号:%d,票数:%d,票价:%d\n",message.flight_ID,message.ticket_num,message.ticket_total_price);
sprintf(msg,"查询成功!航班号:%d,票数:%d,票价:%d\n",message.flight_ID,message.ticket_num,message.ticket_total_price);
add_info(msg);
memcpy(send_buf,&message,sizeof(message));
ret = send(conn_fd,send_buf,sizeof(message),0);
if(ret<0)
thread_err("发送数据出错\n",buff_index);
break;
}
pthread_mutex_unlock(&ticket_list[i].ticket_mutex);
}
break;
case INQUIRE_ALL:
pos = 0;
for(i=0;i<FLIGHT_NUM;i++){
pthread_mutex_lock(&ticket_list[i].ticket_mutex);
message.msg_type = INQUIRE_SUCCEED;
message.flight_ID = ticket_list[i].flight_ID;
message.ticket_num = ticket_list[i].ticket_num;
message.ticket_total_price = ticket_list[i].ticket_price;
pthread_mutex_unlock(&ticket_list[i].ticket_mutex);
/*经过一圈又回来了*/
if(i==0){
sprintf(msg,"查询所有航班号成功!\n");
add_info(msg);
}
sprintf(msg,"航班号:%d,票数:%d,票价:%d\n",message.flight_ID,message.ticket_num,message.ticket_total_price);
add_info(msg);
memcpy(send_buf+pos,&message,sizeof(message));
pos+=sizeof(message);
}
ret = send(conn_fd,send_buf,pos,0);
if(ret<0)
thread_err("发送数据出错\n",buff_index);
break;
default:
message.msg_type = UNKNOWN_CODE;
memcpy(send_buf,&message,sizeof(message));
ret=send(conn_fd,send_buf,sizeof(message),0);
if(ret<0)
thread_err("发送数据出错\n",buff_index);
}
}
}
#endif
项目代码下载地址: 航空售票项目源码。有一些小BUG,还在继续改进。