多进程架构设计
第一篇 网络服务
现实生活中,网络通信的例子很多,例如,我们用浏览器打开一个网页,这个浏览器程序就向服务器发送一个HTTP请求,服务器收到这个请求后,经一番内部的逻辑处理,就将结果(网页)返回给浏览器。这实质上是一个TCP通信过程,这里的浏览器是客户端,服务器是服务端。本篇将用实例来讲述TCP通信过程,主要是服务端的实现。
第一章 完全的单进程服务
一、TCP网络通信基础
TCP通信是网络通信中最常用的一种,开篇中提到的大名鼎鼎的HTTP协议就是建立在TCP之上的。为了简化网络通信编程,系统设计人员设计了一种叫做套接字(socket)的东西。下面就如何利用socket实现TCP网络通信编程。
TCP网络通信分为客户端和服务端。客户端编程过程如下:
1、 产生一个套接字-socket;
2、 连接到服务器-connect;
3、 往对方发送数据-send/write;
4、 接收数据-recv/read;
5、 关闭套接字-close;
服务端编程:
1、 产生一个套接字-socket;
2、 bind一个端口;
3、 listen这个端口;
4、 accept等待客户端连接;
5、 传送数据:write/read或send/recv;
6、 关闭套接字;
(以上所讲是socket的同步通信,这是最简单的TCP编程模式,我们就采纳这种模式。至于异步通信,需要使用select函数,在本篇中完全忽略掉。)
二、服务端实例程序设计
下面是我设计的一个完全单进程服务程序:
#include<unistd.h>
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<sys/stat.h>
#include<errno.h>
#include<stdarg.h>
#include<sys/resource.h>
#include<sys/socket.h>
#include<fxapi.h>
#include<appdef.h>
#defineLOG_FILE "test1.log"
#defineBUF_SIZE 1024*4
static int log_id = 0;
staticint BUSIService(const char * _info, char * _imsg_data, int _ilen, char **_omsg_data, int * _olen);
intfd_svr;
intfd_cli;
intmain(int argc, char ** argv)
{
int ret = 0;
int len = 0;
unsigned int count = 0;
struct sockaddr_in sa;
char * obuf = NULL;
char ibuf[BUF_SIZE];
char szTmp[128];
fx_log(log_id, LOG_FILE, _FL_, "启动服务...");
fd_svr = fx_server(NULL, 12345);
if (fd_svr < 0)
{
fx_log(log_id, LOG_FILE, _FL_, "启动服务失败");
exit(-1);
}
while(1)
{
fx_log(log_id, LOG_FILE, _FL_, "等待客户端连接...");
memset(&sa, 0x00, sizeof(sa));
fd_cli = fx_accept(fd_svr, &sa);
if (fd_cli > 0)
{
count++;
}
else
{
fx_log(log_id, LOG_FILE, _FL_,"客户端连接失败");
continue;
}
/* 输出客户端信息 */
fx_log(log_id, LOG_FILE, _FL_, "客户端%u:%s:%d", count,inet_ntop(AF_INET, &sa.sin_addr, szTmp, sizeof(szTmp)),ntohs(sa.sin_port));
/* 读取客户端请求信息 */
ret = read(fd_cli, ibuf, BUF_SIZE);
if (ret < 0)
{
fx_log(log_id, LOG_FILE, _FL_,"接收数据失败");
close(fd_cli);
continue ;
}
ibuf[ret] = 0;
fx_log(log_id, "tot.log",_FL_, "接收数据[%d]", ret);
/* 业务逻辑处理 */
if (0 != BUSIService(NULL, ibuf, ret,&obuf, &len))
{
fx_log(log_id, LOG_FILE, _FL_,"BUSIService error");
}
if (obuf) free(obuf);
}
return 0;
}
staticint BUSIService(const char * _info, char * _imsg_data, int _ilen, char **_omsg_data, int * _olen)
{
/* 简化处理,只是返回 SUCCESS */
char szMsg[32];
/* 模拟业务逻辑处理过程, 延时20ms */
usleep(20000);
sprintf(szMsg, "SUCCESS bytest1");
write(fd_cli, szMsg, 16);
close(fd_cli);
fd_cli = -1;
return 0;
}
其中fx_server 和 fx_accept的函数实现如下:
intfx_server( const char * _ip, const int _port )
{
struct sockaddr_in sa;
int on = 1;
int fd;
fd = socket( AF_INET, SOCK_STREAM, 0 );
if( fd < 0 )
{
return -1;
}
setsockopt( fd, SOL_SOCKET, SO_REUSEADDR, (char* ) &on , sizeof( on ) );
memset( &sa, 0x00, sizeof( sa ) ); /* Probably unecessary */
sa.sin_family = AF_INET ;
if (NULL == _ip || 0 == strlen(_ip))
{
sa.sin_addr.s_addr = htonl(INADDR_ANY);
}
else
{
sa.sin_addr.s_addr = inet_addr(_ip);
}
sa.sin_port = htons( _port );
if( bind( fd, ( struct sockaddr * ) &sa,sizeof( sa ) ) < 0 )
{
close( fd );
return -1;
}
listen( fd, 500 ); /* max # of queued connects */
return fd;
}
intfx_accept( const int fd, struct sockaddr_in * cli_adr )
{
int newfd;
socklen_t dummy = sizeof( *cli_adr );
newfd = accept( fd, ( struct sockaddr * )cli_adr, &dummy );
return newfd;
}
程序很简单,基本的TCP服务程序,其中日志函数fx_log我封装在自己的实用库(libfx.a)中。其中业务逻辑部分也很简单,延时20ms模拟业务处理过程,然后返回”SUCCESS by test1”,见上面的BUSIService函数。(顺便说一下,本人曾使用英文名Felix,所以函数名/文件名前使用fx_前缀)
Makefile文件如下:
include${WORKDIR}/etc/Custom.defines
NAME=TEST1
CUSTOMLIB=./main.o${WORKDIR}/lib/libfx.a
EXECOBJ=${WORKDIR}/bin/${NAME}
LINKRULE=${CC}-o ${EXECOBJ} ${REGISTC} ${CUSTOMLIB} ${LIBOPTS}
REGISTC=
TARGETS=${EXECOBJ}
all:${TARGETS}
clean:
@- rm -f ${CLEANFILES} ${TARGETS}
${EXECOBJ}:${CUSTOMLIB}
${LINKRULE}
编译一下:
[platdev@localhostTEST1]$ make
gcc -g -D_ALL_SOURCE -DAREV_PINSERT-I/home/platdev/include -o main.o -cmain.c
gcc-o /home/platdev/bin/TEST1 ./main.o/home/platdev/lib/libfx.a -lrt -ldl-lm -lc
[platdev@localhostTEST1]$
启动服务:
[platdev@localhostTEST1]$ echo $PATH
/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/home/platdev/bin:/home/platdev/bin:/bin:.:/home/platdev/bin:/home/platdev/scripts:/home/