支持多协议多服务的服务器设计和 实现和实现
运行服务器时如果出现权限问题,记得在前面加sudo命令,赋予管理员权限执行
实验目的
熟练掌握多协议多服务服务器的设计和实现方法。
实验原理
多协议的设计方案允许单个服务器线程同时管理针对同一个服务的 UDP 套接字和
TCP 套接字。在多服务的情况下,服务器可以为它所提供的一些甚至全部的服务管理 UDP 和 TCP 套接字。
实验步骤
服务器端:
1.passiveTCP.c
#include <stdio.h>
int passivesock( const char *service, const char *transport, const int qlen);
int passiveTCP(const char *service, int qlen){
return passivesock( service, "tcp", qlen);
}
int passiveUDP(const char *service, int qlen){
return passivesock( service, "udp", 0);
}
2.passivesock.c
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <stdio.h>
unsigned short portbase = 0;
int errexit(const char *format, ...);
int passivesock( const char *service, const char * transport, int qlen){
struct hostent *phe;
struct servent *pse;
struct protoent *ppe;
struct sockaddr_in sin;
int s, type;
memset(&sin, 0, sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = INADDR_ANY;
if( pse = getservbyname(service, transport)){
sin.sin_port = htons(ntohs((unsigned short)pse->s_port) +portbase);
}
else if( ( sin.sin_port = htons((unsigned short)atoi(service))) == 0 ){
errexit("can't get \"%s\" service entry\n", service);
}
if( (ppe = getprotobyname( transport )) == 0 ){
errexit("can't get \"%s\" protocol entry\n", transport);
}
if( strcmp(transport, "udp") == 0){
type = SOCK_DGRAM;
}
else{
type = SOCK_STREAM;
}
s = socket(AF_INET, type, ppe->p_proto);
printf("s=%d\n", s);
printf("%d\n", ppe->p_proto);
printf("%d\n", sin.sin_port);
if( s < 0 ){
errexit("can't create socket: %s\n", strerror(errno));
}
if( bind( s, (struct sockaddr *)&sin, sizeof(sin)) < 0 ){
errexit("can't connect to %s:%s\n", service, strerror(errno));
}
if( type == SOCK_STREAM && listen(s, qlen) < 0){
errexit("cann't listen on %s port\n", service);
}
return s;
}
3.errexit.c
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
int errexit(const char *format, ...){
va_list args;
va_start(args, format);
vfprintf(stderr, format, args);
va_end(args);
exit(1);
}
4.superd.c
#define _USE_BSD
#include <sys/types.h>
#include <sys/param.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <sys/errno.h>
#include <sys/signal.h>
#include <sys/wait.h>
#include <netinet/in.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#define UDP_SERV 0
#define TCP_SERV 1
#define NOSOCK -1 /* an invalid socket descriptor */
struct service {
char *sv_name;
char sv_useTCP;
int sv_sock;
void (*sv_func)(int);
};
void TCPechod(int), TCPchargend(int), TCPdaytimed(int), TCPtimed(int), UDPechod(int), UDPtimed(int) ;
int passiveTCP(const char *service, int qlen);
int passiveUDP(const char *service);
int errexit(const char *format, ...);
void doTCP(struct service *psv);
void reaper(int sig);
struct service svent[] =
{
{ "echo", TCP_SERV, NOSOCK, TCPechod },
{ "chargen", TCP_SERV, NOSOCK, TCPchargend },
{ "daytime", TCP_SERV, NOSOCK, TCPdaytimed },
{ "time", TCP_SERV, NOSOCK, TCPtimed },
{ "echo", UDP_SERV, NOSOCK, UDPechod },
{ "time", UDP_SERV, NOSOCK, UDPtimed },
{ 0, 0, 0, 0 },
};
#ifndef MAX
#define MAX(x, y) ((x) > (y) ? (x) : (y))
#endif /* MAX */
#define QLEN 32
#define LINELEN 128
unsigned short portbase; /* from passivesock()*/
int main(int argc, char *argv[]){
struct service *psv, /* service table pointer */
*fd2sv[NOFILE]; /* map fd to service pointer */
int fd, nfds;
fd_set afds, rfds; /* readable file descriptors */
switch (argc) {
case 1: break;
case 2:
portbase = (unsigned short) atoi(argv[1]);
break;
default:
errexit("usage: superd [portbase]\n");
}
nfds = 0;
FD_ZERO(&afds);
for (psv = &svent[0]; psv->sv_name; ++psv) {
if (psv->sv_useTCP){
psv->sv_sock = passiveTCP(psv->sv_name, QLEN);
}
else{
psv->sv_sock = passiveUDP(psv->sv_name);
}
fd2sv[psv->sv_sock] = psv;
nfds = MAX(psv->sv_sock+1, nfds);
FD_SET(psv->sv_sock, &afds);
}
(void) signal(SIGCHLD, reaper);
while (1) {
memcpy(&rfds, &afds, sizeof(rfds));
if (select(nfds, &rfds, (fd_set *)0, (fd_set *)0, (struct timeval *)0) < 0) {
if (errno == EINTR) {
continue;
}
errexit("select error: %s\n", strerror(errno));
}
for (fd=0; fd<nfds; ++fd)
if (FD_ISSET(fd, &rfds)) {
psv = fd2sv[fd];
if (psv->sv_useTCP) {
doTCP(psv);
}
else{
psv->sv_func(psv->sv_sock);
}
}
}
}
void doTCP(struct service *psv)
{
struct sockaddr_in fsin; /* the request from address */
unsigned int alen; /* from-address length */
int fd, ssock;
alen = sizeof(fsin);
ssock = accept(psv->sv_sock, (struct sockaddr *)&fsin, &alen);
if (ssock < 0){
errexit("accept: %s\n", strerror(errno));
}
switch (fork()) {
case 0:
break;
case -1:
errexit("fork: %s\n", strerror(errno));
default:
(void) close(ssock);
return; /* parent */
}
/* child */
for (fd = NOFILE; fd >= 0; --fd) {
if (fd != ssock){
(void) close(fd);
}
}
psv->sv_func(ssock);
exit(0);
}
/*------------------------------------------------------------------------
* reaper - clean up zombie children
*------------------------------------------------------------------------
*/
void reaper(int sig)
{
int status;
while (wait3(&status, WNOHANG, (struct rusage *)0) >= 0)
/* empty */;
}
5.vs_funcs.c
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <time.h>
#include <string.h>
#include <errno.h>
#include <sys/socket.h>
#include <netinet/in.h>
#define BUFFERSIZE 4096 /* max read buffer size */
void TCPechod(int), TCPchargend(int), TCPdaytimed(int), TCPtimed(int), UDPechod(int), UDPtimed(int);
int errexit(const char *format, ...);
/*------------------------------------------------------------------------
* TCPecho - do TCP ECHO on the given socket
*------------------------------------------------------------------------
*/
void TCPechod(int fd)
{
char buf[BUFFERSIZE];
int cc;
while (cc = read(fd, buf, sizeof buf)) {
if (cc < 0){
errexit("echo read: %s\n", strerror(errno));
}
if (write(fd, buf, cc) < 0){
errexit("echo write: %s\n", strerror(errno));
}
}
}
void UDPechod(int sock)
{
struct sockaddr_in fsin; /* the from address of a client */
char buf[128]; /* "input" buffer; any size > 0 */
time_t now; /* current time */
unsigned int alen; /* from-address length */
int n; /*接收数据长度*/
alen = sizeof(fsin);
n = recvfrom(sock, buf, sizeof(buf), 0, (struct sockaddr *)&fsin, &alen);
(void) sendto(sock, buf, n, 0, (struct sockaddr *)&fsin, sizeof(fsin));
}
#define LINELEN 72
/*------------------------------------------------------------------------
* TCPchargend - do TCP CHARGEN on the given socket
*------------------------------------------------------------------------
*/
void TCPchargend(int fd)
{
char c, buf[LINELEN+2]; /* print LINELEN chars + \r\n */
c = ' ';
buf[LINELEN] = '\r';
buf[LINELEN+1] = '\n';
while (1) {
int i;
for (i=0; i<LINELEN; ++i) {
buf[i] = c++;
if (c > '~'){
c = ' ';
}
}
if (write(fd, buf, LINELEN+2) < 0) {
break;
}
}
}
/*------------------------------------------------------------------------
* TCPdaytimed - do TCP DAYTIME protocol
*------------------------------------------------------------------------
*/
void TCPdaytimed(int fd)
{
char buf[LINELEN], *ctime();
time_t now;
(void) time(&now);
sprintf(buf, "%s", ctime(&now));
(void) write(fd, buf, strlen(buf));
}
#define UNIXEPOCH 2208988800UL /* UNIX epoch, in UCT secs */
/*------------------------------------------------------------------------
* TCPtimed - do TCP TIME protocol
*------------------------------------------------------------------------
*/ void TCPtimed(int fd)
{
time_t now;
(void) time(&now);
now = htonl((unsigned long)(now + UNIXEPOCH));
(void) write(fd, (char *)&now, sizeof(now));
}
void UDPtimed(int sock)
{
struct sockaddr_in fsin; /* the from address of a client */
char buf[1]; /* "input" buffer; any size > 0 */
time_t now; /* current time */
unsigned int alen; /* from-address length */
alen = sizeof(fsin);
if (recvfrom(sock, buf, sizeof(buf), 0, (struct sockaddr *)&fsin, &alen) < 0) {
errexit("recvfrom: %s\n", strerror(errno));
}
(void) time(&now);
now = htonl((unsigned long)(now + UNIXEPOCH));
(void) sendto(sock, (char *)&now, sizeof(now), 0, (struct sockaddr *)&fsin, sizeof(fsin));
}
实验结果: