一 问题来源

     2014腾讯研发工程师笔试题:请设计一个排队系统,能够让每个进入队伍的用户都能看到自己在 中所处的位置和变化。队伍可能随时有人加入和退出,当有人退出影响到用户的位置排名时需要即时反馈到用户。

二 思路

    排队即队列,要保证先进先出原则,队尾进,队头出。我把问题划分问两部分来解决:客户端和服务器端。客户端留给用户输入命令行,如“add x”,表示将用户x加入队列中,输入“rm y”表示将用户y从队列中删除,但是必须强调得是,加入某个用户必须在队尾实施插入操作,删除某个用户,此用户必须是队头用户,这是为了遵守队列“FIFO”原则,输入“show xx”表示显示用户xx的在队列中排名(位置)。

三 队列结构

基于C/S架构的排队系统_服务器端

四 运行效果

客户端:

基于C/S架构的排队系统_服务器端_02

服务器端:

基于C/S架构的排队系统_客户端_03

五 编程体会

     程序分为客户端与服务器端两部分,每部分都需要用到套接字编程,常用的操作有建立套接字、连接服务器、读写套接字等等。在服务器端,原本使用了“fork”创建子进程,在生成的子进程中对队列进行操作,如插入、删除结点,但是不断"fork"出的子进程有独立的地址空间,会使得队列无法连续生成,每个子进程都只能生成一个结点。所以回避了“fork”。这也是我调试了半天的经验。本系统使用了命令行的形式来对队列进行操作,能够完成题目的入队、出队和查看排名的要求,但实现的时候也有些不妥。比如如果某个用户离开队列,那么其他的用户怎样立即感知,这就不得不用“show XXX”这样的命令。所以还是有缺陷的,另外,gets函数比较危险,sscanf和sprintf函数不常用,但确实很有用。

六  代码

客户端:

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <signal.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#define LEN 20
/*
* The client program which user can input a command line to query the result
* */
int main() {
/*
* the socket fd is same the file descriptor
* */
int sockfd = -1;
int len = 0;
/*
* to define socket address
* */
struct sockaddr_in address;
int result;

char cmdline[LEN];
char resultstring[2 * LEN];

// while(1) {
/*
* to create a socket
* a socket contains domain, type and protocal
* */
sockfd = socket(AF_INET, SOCK_STREAM, 0);
/*
* to initialize the socket address
* */
address.sin_family = AF_INET;
address.sin_addr.s_addr = inet_addr("127.0.0.1");
address.sin_port = htons(9736);
len = sizeof(address);
/*
* to connect the server
* */
result = connect(sockfd, (struct sockaddr *)&address, len);
if(result == -1) {
perror("ops:client!\n");
exit(1);
}

printf("input a cmdline:");
gets(cmdline);
/*
* The format of cmdline is:
* add username -- to add a user to the queue system
* rm username -- to remove a user from the queue system
* show username -- to show the rank of the user from the queue system
* */
/*
* to check whether the cmdline is legal
* */
if(!strstr(cmdline, "add") && !strstr(cmdline, "rm") && !strstr(cmdline, "show")
&& !strstr(cmdline, " ")) {
fprintf(stderr, "cmdline error!\n");
exit(EXIT_FAILURE);
}
write(sockfd, &cmdline, sizeof(cmdline));
read(sockfd, &resultstring, sizeof(resultstring));
printf("result from server = %s\n", resultstring);

close(sockfd);
// }
exit(0);

return 0;
}



服务器端:

/*
* Copyright (C) 2013 wangzhicheng <2363702560@qq.com>
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Gernal Public License as published by
* the Free software Foundation, version1.0
* Author:
* wangzhicheng <2363702560@qq.com>
* 2013/10/6
* */
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <pthread.h>
#include <sys/socket.h>
#include <signal.h>
#include <netinet/in.h>
#include <ctype.h>
#define LEN 20
/*
* to define a Person structure and initialize
* */
typedef struct User {
char name[LEN];
unsigned int pos;
}User;
/*
* the linked queue structure
* */
typedef struct QueueNode {
User user;
struct QueueNode *next;
}QueueNode;
typedef struct LinkedQueue {
QueueNode *front;
QueueNode *rear;
}LinkedQueue;

pthread_mutex_t mutext;
/*
* to parse the command line
* @type: the type argument as add, rm and show
* @val: the value argument as username
* */
int parse_cmdline(char *cmdline, char *type, char *val) {
int err = 0;
char *cp;
/*
* change the first '\n' into '\0'
* */
cp = strchr(cmdline, '\n');
if(cp) {
*cp = '\0';
}
if(sscanf(cmdline, "%5s %12s", type, val) != 2) err = 1;
if(!strcmp(type, "add") && !strcmp(type, "rm") && !strcmp(type, "show")) {
err = 1;
}
return err;
}
/*
* a function of queue system can add, remove or query
* @type: the type of cmdline as add, rm, show
* @val: the username
* @resultstring: the result string which should return to the client socket
* */
void Do(LinkedQueue **Q, char *type, char *val, char *resultstring) {
QueueNode *p; // point to the current node
QueueNode *pre; // point to the previous of current node
QueueNode *r; // point to the node should be deleted
int find; // whether the node is located
if(strcmp(type, "add") == 0) {
/*
* to check whether check repeatedly
* */
find = 0;
p = (*Q)->front;
while(p) {
if(strcmp(p->user.name, val) == 0) {
find = 1;
break;
}
p = p->next;
}
if(find) {
strcpy(resultstring, "add repeatedly!\n");
return;
}
/*
* to add a user to the queue system
* */
/*
* to create a new queue node
* */
p = (QueueNode *)malloc(sizeof(QueueNode));
if(!p) { // Maybe this will hardly happen
fprintf(stderr, "memory error!\n");
exit(EXIT_FAILURE);
}
p->next = 0;
User user;
strncpy(user.name, val, 16);
user.pos = 0;
p->user = user;
/*
* to add the new queue node to the queue system
* */
if((*Q)->rear == NULL) { // the queue system is empty
p->user.pos = 1;
(*Q)->front = p;
(*Q)->rear = p;
}
else { // insert into the rear of queue system
p->user.pos = (*Q)->rear->user.pos + 1;
(*Q)->rear->next = p;
(*Q)->rear = p;
}
strcpy(resultstring, "add a new user to the queue system!\n");
}
/*
* to remove a node from the queue system
* */
else if(strcmp(type, "rm") == 0) {
if(!(*Q)->front) { // the queue system is empty
strcpy(resultstring, "remove failed, the queue system is empty!\n");
return;
}
else {
if(strcmp((*Q)->front->user.name, val)) {
strcpy(resultstring, "You are not the head of queue!\n");
return;
}
else {
r = (*Q)->front;
(*Q)->front = r->next;
free(r);
if(!(*Q)->front) (*Q)->rear = NULL;
}
strcpy(resultstring, "the head of queue has been deleted!\n");
}
/*
* change the rank
* */
p = (*Q)->front;
while(p) {
p->user.pos--;
p = p->next;
}
}
/*
* to show the rank
* */
else {
if(!(*Q)->front) { // the queue system is empty
strcpy(resultstring, "show failed, the queue system is empty!\n");
}
find = 0;
p = (*Q)->front;
while(p) {
if(strcmp(p->user.name, val) == 0) {
sprintf(resultstring,"the rank is:%d", p->user.pos);
find = 1;
break;
}
else p = p->next;
}
if(!find) strcpy(resultstring, "show failed, no user!\n");
}
}
int main() {
/*
* create a empty queue system
* */
LinkedQueue *Q = (LinkedQueue *)malloc(sizeof(LinkedQueue));
Q->front = NULL;
Q->rear = NULL;
/*
* the socket fd is same the file descriptor
* */
int server_sockfd = -1;
int client_sockfd = -1;
int client_len = 0;
/*
* to define socket address
* */
struct sockaddr_in server_addr;
struct sockaddr_in client_addr;
/*
* to create a socket
* a socket contains domain, type and protocal
* */
server_sockfd = socket(AF_INET, SOCK_STREAM, 0);
// bzero(&server_addr, sizeof(server_addr)); // to clear the server_addr
/*
* to initialize the socket address
* */
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
/*
* pay attention
* the port should be same with the port from client
* */
server_addr.sin_port = htons(9736);
/*
* to bind the sockect and its address
* */
bind(server_sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr));
/*
* to listen the port
* */
listen(server_sockfd, 5);
/*
* to ignore child processes which stop
* */
signal(SIGCHLD, SIG_IGN);

char msg[LEN];
char cmdline[LEN];
char type[5];
char val[12];
char resultstring[2 * LEN];
while(1) {
client_len = sizeof(client_addr);
printf("server is waiting...\n");
/*
* to accept a connect from a client socket
* */
client_sockfd = accept(server_sockfd, (struct sockaddr *)&client_addr, &client_len);
/*
* let the child process to deal with
* */
/*
* read data from socket
* */
read(client_sockfd, &cmdline, sizeof(cmdline));
if(parse_cmdline(cmdline, type, val)) {
strcpy(resultstring, "cmdline contains error!\n");
}
else {
Do(&Q, type, val, resultstring);
/*
* write the data to socket
* */
write(client_sockfd, &resultstring, sizeof(resultstring));
}
close(client_sockfd);
}

close(server_sockfd);
return 0;
}