Skip to content

Instantly share code, notes, and snippets.

@luchenqun
Created December 13, 2017 14:50
Show Gist options
  • Save luchenqun/d63b9f571ca3d2d6e8a8da2f9b925973 to your computer and use it in GitHub Desktop.
Save luchenqun/d63b9f571ca3d2d6e8a8da2f9b925973 to your computer and use it in GitHub Desktop.
/*************************************************
* File name : server.c
* Description : 单进程并发服务器
* Author : 745917886@@qq.com
* Version : V1.0
* Date :
* Compiler : arm-linux-gcc-4.4.3
* Target : mini2440(Linux-2.6.32)
* History :
* <author> <time> <version > <desc>
*************************************************/
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/time.h>
#include <stdlib.h>
#define PORT 1234 // 服务器端口
#define BACKLOG 5 //listen 队列中等待的连接数
#define MAXDATASIZE 1024 // 缓冲区大小
typedef struct _CLIENT
{
int fd; // 客户端 socket 描述符
char name[20]; // 客户端名称
struct sockaddr_in addr; // 客户端地址信息结构体
char data[MAXDATASIZE]; // 客户端私有数据指针
} CLIENT;
void process_client(CLIENT * client, char *recvbuf, int len); // 客户请求处理函数
/*************************************************
* Function : main()
* Description :
* Calls : process_client()
* Called By :
* Input :
* Output :
* Return :
*************************************************/
void main(int argc ,char **argv)
{
int i, maxi, maxfd, sockfd;
int nready;
ssize_t n;
fd_set rset, allset; //select 所需的文件描述符集合
int listenfd, connectfd; //socket 文件描述符
struct sockaddr_in server; // 服务器地址信息结构体
CLIENT client[FD_SETSIZE]; //FD_SETSIZE 为 select 函数支持的最大描述符个数
char recvbuf[MAXDATASIZE]; // 缓冲区
int sin_size; // 地址信息结构体大小
if ((listenfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
{ // 调用 socket 创建用于监听客户端的 socket
perror("Creating socket failed.");
exit(1);
}
int opt = SO_REUSEADDR;
setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)); // 设置 socket 属性
bzero(&server, sizeof(server));
server.sin_family = AF_INET;
server.sin_port = htons(PORT);
server.sin_addr.s_addr = htonl(INADDR_ANY);
if (bind(listenfd, (struct sockaddr *)&server, sizeof(struct sockaddr)) == -1)
{ // 调用 bind 绑定地址
perror("Bind error.");
exit(1);
}
if (listen(listenfd, BACKLOG) == -1)
{ // 调用 listen 开始监听
perror("listen() error\n");
exit(1);
}
// 初始化 select
maxfd = listenfd;
maxi = -1;
for (i = 0; i < FD_SETSIZE; i++)
{
client[i].fd = -1;
}
FD_ZERO(&allset); // 清空
FD_SET(listenfd, &allset); // 将监听 socket 加入 select 检测的描述符集合
while (1)
{
struct sockaddr_in addr;
rset = allset;
nready = select(maxfd + 1, &rset, NULL, NULL, NULL); // 调用 select
printf("Select() break and the return num is %d. \n", nready);
if (FD_ISSET(listenfd, &rset))
{ // 检测是否有新客户端请求
printf("Accept a connection.\n");
// 调用 accept,返回服务器与客户端连接的 socket 描述符
sin_size = sizeof(struct sockaddr_in);
if ((connectfd =
accept(listenfd, (struct sockaddr *)&addr, (socklen_t *) & sin_size)) == -1)
{
perror("Accept() error\n");
continue;
}
// 将新客户端的加入数组
for (i = 0; i < FD_SETSIZE; i++)
{
if (client[i].fd < 0)
{
char buffer[20];
client[i].fd = connectfd; // 保存客户端描述符
memset(buffer, '0', sizeof(buffer));
sprintf(buffer, "Client[%.2d]", i);
memcpy(client[i].name, buffer, strlen(buffer));
client[i].addr = addr;
memset(buffer, '0', sizeof(buffer));
sprintf(buffer, "Only For Test!");
memcpy(client[i].data, buffer, strlen(buffer));
printf("You got a connection from %s:%d.\n", inet_ntoa(client[i].addr.sin_addr),ntohs(client[i].addr.sin_port));
printf("Add a new connection:%s\n",client[i].name);
break;
}
}
if (i == FD_SETSIZE)
printf("Too many clients\n");
FD_SET(connectfd, &allset); // 将新 socket 连接放入 select 监听集合
if (connectfd> maxfd)
maxfd = connectfd; // 确认 maxfd 是最大描述符
if (i> maxi) // 数组最大元素值
maxi = i;
if (--nready <= 0)
continue; // 如果没有新客户端连接,继续循环
}
for (i = 0; i <= maxi; i++)
{
if ((sockfd = client[i].fd) < 0) // 如果客户端描述符小于 0,则没有客户端连接,检测下一个
continue;
// 有客户连接,检测是否有数据
if (FD_ISSET(sockfd, &rset))
{
printf("Receive from connect fd[%d].\n", i);
if ((n = recv(sockfd, recvbuf, MAXDATASIZE, 0)) == 0)
{ // 从客户端 socket 读数据,等于 0 表示网络中断
close(sockfd); // 关闭 socket 连接
printf("%s closed. User's data: %s\n", client[i].name, client[i].data);
FD_CLR(sockfd, &allset); // 从监听集合中删除此 socket 连接
client[i].fd = -1; // 数组元素设初始值,表示没客户端连接
}
else
process_client(&client[i], recvbuf, n); // 接收到客户数据,开始处理
if (--nready <= 0)
break; // 如果没有新客户端有数据,跳出 for 循环回到 while 循环
}
}
}
close(listenfd); // 关闭服务器监听 socket
}
/*************************************************
* Function : process_client()
* Description : 处理客户端连接函数
* Calls :
* Called By : main()
* Input :
* Output :
* Return :
*************************************************/
void process_client(CLIENT * client, char *recvbuf, int len)
{
char sendbuf[MAXDATASIZE];
int i;
printf("Received client(%s) message: %s\n", client->name, recvbuf);
for (i = 0; i < len - 1; i++)
{
sendbuf[i] = recvbuf[len - i - 2];
}
sendbuf[len - 1] = '\0';
send(client->fd, sendbuf, strlen(sendbuf), 0);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment