您的位置:首页 > 理论基础 > 计算机网络

Linux网络编程:TCP服务器(单进程多用户),使用select方法实现

2012-08-08 11:02 1121 查看
Linux下的单进程多用户TCP服务器,采用select方法实现。

[cpp] view
plaincopy

/*************************************************

* File name : server.c

* Description : 单进程并发服务器

* Author : sg131971@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);

}

测试结果:

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐