第13章 基于数据报的编程:编写许可证服务器

1.许可证管理

第13章 基于数据报的编程:编写许可证服务器

第13章 基于数据报的编程:编写许可证服务器

2.流和数据报的比较

第13章 基于数据报的编程:编写许可证服务器

3.数据报编程

(1)接收数据报

/************************************************************************
 * dgrecv.c  - datagram receiver
 * 	         usage: dgrecv portnum 
 * 	        action: listens at the specfied port and reports messages
 */

#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<stdlib.h>

#define oops(m,x)  { perror(m);exit(x);}

int  make_dgram_server_socket(int);
int  get_internet_address(char *, int,  int *, struct sockaddr_in *);
void say_who_called(struct sockaddr_in *);

int main(int ac, char *av[])
{
	int	port;			/* use this port		*/
	int	sock;			/* for this socket		*/
	char	buf[BUFSIZ];		/* to receive data here		*/
	size_t	msglen;			/* store its length here	*/
	struct  sockaddr_in   saddr;	/* put sender's address here	*/
	socklen_t saddrlen;		/* and its length here		*/
	
	if ( ac == 1 || (port = atoi(av[1])) <= 0 ){
		fprintf(stderr,"usage: dgrecv portnumber\n");
		exit(1);
	}

    /*  get a socket and assign it a port number */

	if( (sock = make_dgram_server_socket(port)) == -1 )
		oops("cannot make socket",2);

    /* receive messaages on that socket */

	saddrlen = sizeof(saddr);
	while( (msglen = recvfrom(sock,buf,BUFSIZ,0,
				(struct sockaddr *) &saddr,&saddrlen))>0 ) {
		buf[msglen] = '\0';
		printf("dgrecv: got a message: %s\n", buf);
		say_who_called(&saddr);
	}
	return 0;
}
void say_who_called(struct sockaddr_in *addrp)
{
	char	host[BUFSIZ];
	int	port;

	get_internet_address(host,BUFSIZ,&port,addrp);
	printf("  from: %s:%d\n", host, port);
}

(2)发送数据报

/*********************************************************************
 * dgsend.c  - datagram sender
 * 	         usage: dgsend hostname portnum "message"
 * 	        action: sends message to hostname:portnum
 */

#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<stdlib.h>
#include<string.h>

#define oops(m,x)  { perror(m);exit(x);}

int make_dgram_client_socket();
int make_internet_address(char *,int, struct sockaddr_in *);

int main(int ac, char *av[])
{
	int	sock;			/* use this socket to send	*/
	char	*msg;			/* send this messag		*/
	struct  sockaddr_in   saddr;	/* put sender's address here	*/

	if ( ac != 4 ){
		fprintf(stderr,"usage: dgsend host port 'message'\n");
		exit(1);
	}
	msg = av[3];

    /* get a datagram socket */

	if( (sock = make_dgram_client_socket()) == -1 )
		oops("cannot make socket",2);

    /* combine hostname and portnumber of destination into an address */

	if ( make_internet_address(av[1], atoi(av[2]), &saddr) == -1 )
		oops("make addr",4);

    /* send a string through the socket to that address */

	if ( sendto(sock, msg, strlen(msg), 0, 
				(struct sockaddr *)&saddr,sizeof(saddr)) == -1)
		oops("sendto failed", 3);
	return 0;
}

(3)辅助函数

/***************************************************************
 *	dgram.c
 *	support functions for datagram based programs
 */ 

#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<netdb.h>
#include<string.h>

#define   HOSTLEN  256

int make_internet_address();

int make_dgram_server_socket(int portnum)
{
	struct  sockaddr_in   saddr;   /* build our address here */
	char	hostname[HOSTLEN];     /* address 	         */
	int	sock_id;	       /* the socket             */

	sock_id = socket(PF_INET, SOCK_DGRAM, 0);  /* get a socket */
	if ( sock_id == -1 ) return -1;

	/** build address and bind it to socket **/

	gethostname(hostname, HOSTLEN);         /* where am I ?         */
	make_internet_address(hostname, portnum, &saddr);

	if ( bind(sock_id, (struct sockaddr *)&saddr, sizeof(saddr)) != 0 )
	       return -1;

	return sock_id;
}
int make_dgram_client_socket()
{
	return socket(PF_INET, SOCK_DGRAM, 0);
}

int make_internet_address(char *hostname, int port, struct sockaddr_in *addrp)
/*
 * constructor for an Internet socket address, uses hostname and port
 *   (host,port) -> *addrp
 */
{
	struct hostent	*hp;

	bzero((void *)addrp, sizeof(struct sockaddr_in));
	hp = gethostbyname(hostname);
	if ( hp == NULL ) return -1;
	bcopy((void *)hp->h_addr, (void *)&addrp->sin_addr, hp->h_length);
	addrp->sin_port = htons(port);
	addrp->sin_family = AF_INET;
	return 0;
}

int get_internet_address(char *host, int len, int *portp, struct sockaddr_in *addrp)
/*
 * extracts host and port from an internet socket address
 *   *addrp -> (host,port)
 */
{
	strncpy(host, inet_ntoa(addrp->sin_addr), len );
	*portp = ntohs(addrp->sin_port);
	return 0;
}

(4)运行结果

第13章 基于数据报的编程:编写许可证服务器

第13章 基于数据报的编程:编写许可证服务器

启动服务器使得它监听端口4444,客户发送字符串到端口4444,客户socket拥有主机地址和端口号,内核随机给它分配了一个端口号56923。

(5)程序函数

sendto和recvfrom函数

第13章 基于数据报的编程:编写许可证服务器

第13章 基于数据报的编程:编写许可证服务器

地址转换函数:inet_aton & inet_ntoa & inet_addr和inet_pton & inet_ntop

在Unix网络编程中,我们常用到地址转换函数,它将ASCII字符串(如"206.62.226.33")与网络字节序的二进制值(这个值保存在套接口地址结构中)间进行地址的转换。

  1、inet_aton、inet_addr和inet_ntoa在点分十进制数串(例如"206.62.226.33")与它的32位网络字节序二进制值间转换IPv4地址。

  2、两个较新的函数:inet_pton和inet_ntop对IPv4和IPv6地址都能进行处理。

#include<arpa/inet.h>

/* 返回1:串有效,返回0:串出错 */
int inet_aton(const char *strptr, struct in_addr *addrptr);

/* 若成功,返回32位二进制的网络字节序地址;若出错,返回INADDR_NONE */
in_addr_t inet_addr(const char *strptr);

/* 返回指向点分十进制数串的指针 */
char* inet_ntoa(struct in_addr inaddr);
#include<arpa/inet.h>

/* 若函数成功,则返回1;若输入不是有效的格式,则函数返回0;若处理失败,函数返回-1 */
int inet_pton(int family, const char *strptr, void *addrptr);

/* 若函数处理成功,返回指向结果的指针;若函数处理失败,返回NULL */
const char* inet_ntop(int family, const void *addrptr, char *strptr, size_t len);

4.许可证服务器版本1.0

第13章 基于数据报的编程:编写许可证服务器

(1)客户端版本1

/****************************************************************************
 * lclnt1.c
 * License server client version 1
 *  link with lclnt_funcs1.o dgram.o
 */

#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>

void do_regular_work();

int main(int ac, char *av[])
{
	setup();
	if (get_ticket() != 0 )
		exit(0);	

	do_regular_work();

	release_ticket();
	shut_down();

}
/****************************************************************************
 * do_regular_work  the main work of the application goes here
 */
void do_regular_work()
{
	printf("SuperSleep version 1.0 Running - Licensed Software\n");
	sleep(10);	/* our patented sleep algorithm */
}
/***************************************************************************
 * lclnt_funcs1.c: functions for the client of the license server
 */

#include<stdio.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<netdb.h>
#include<string.h>
#include<stdlib.h>

/*
 * Important variables used throughout
 */
static int pid = -1;			/* Our PID */
static int sd = -1;			/* Our communications socket */
static struct sockaddr serv_addr;	/* Server address */	
static socklen_t serv_alen;		/* length of address */
static char ticket_buf[128];		/* Buffer to hold our ticket */
static have_ticket = 0;			/* Set when we have a ticket */

#define MSGLEN		128		/* Size of our datagrams */
#define SERVER_PORTNUM	2020		/* Our server's port number */
#define	HOSTLEN		512
#define oops(p) { perror(p); exit(1) ; }

char *do_transaction(char *msg);
/****************************************************************************
 * narrate:  print messages to stderr for debugging and demo purposes
 * IN msg1, msg2 : strings to print along with pid and title
 * RET     nothing, dies on error
 */
void narrate(char *msg1, char *msg2)
{
	fprintf(stderr,"CLIENT [%d]: %s %s\n", pid, msg1, msg2);
}

void syserr(char *msg1)
{
	char	buf[MSGLEN];
	sprintf(buf,"CLIENT [%d]: %s", pid, msg1);
	perror(buf);
}

/*
 * setup:  get pid, socket, and address of license server
 * IN      no args
 * RET     nothing, dies on error
 * notes:  assumes server is on same host as client
 */
void setup()
{
	char	hostname[BUFSIZ];

	pid = getpid();				/* for ticks and msgs	*/
	sd  = make_dgram_client_socket();	/* to talk to server	*/
	if ( sd == -1 ) 
		oops("Cannot create socket");
	gethostname(hostname, HOSTLEN);         /* server on same host	*/
	make_internet_address(hostname, SERVER_PORTNUM, &serv_addr);
	serv_alen = sizeof(serv_addr);
}

void shut_down()
{
	close(sd);
}
/****************************************************************************
 * get_ticket
 * get a ticket from the license server
 * Results: 0 for success, -1 for failure
 */
int get_ticket()
{
	char *response;
	char buf[MSGLEN];

	if(have_ticket)				/* don't be greedy 	*/
		return(0);

	sprintf(buf, "HELO %d", pid);		/* compose request	*/

	if ( (response = do_transaction(buf)) == NULL )
		return(-1);

	/* parse the response and see if we got a ticket.  
	 *   on success, the message is: TICK ticket-string
	 *   on failure, the message is: FAIL failure-msg
	 */
	if ( strncmp(response, "TICK", 4) == 0 ){
		strcpy(ticket_buf, response + 5);	/* grab ticket-id */
		have_ticket = 1;			/* set this flag  */
		narrate("got ticket", ticket_buf);
		return(0);
	}

	if ( strncmp(response,"FAIL",4) == 0)
		narrate("Could not get ticket",response);
	else
		narrate("Unknown message:", response);

	return(-1);
} /* get_ticket */
  
/****************************************************************************
 * release_ticket
 * Give a ticket back to the server
 * Results: 0 for success, -1 for failure
 */
int release_ticket()
{
	char buf[MSGLEN];
	char *response;

	if(!have_ticket)			/* don't have a ticket	*/
		return(0);			/* nothing to release	*/

	sprintf(buf, "GBYE %s", ticket_buf);	/* compose message	*/
	if ( (response = do_transaction(buf)) == NULL )
		return(-1);

	/* examine response
	 * success: THNX info-string
	 * failure: FAIL error-string
	 */
	if ( strncmp(response, "THNX", 4) == 0 ){
		narrate("released ticket OK","");
		return 0;
	}
	if ( strncmp(response, "FAIL", 4) == 0)
		narrate("release failed", response+5);
	else
		narrate("Unknown message:", response);
	return(-1);
} /* release_ticket */

/****************************************************************************
 * do_transaction
 * Send a request to the server and get a response back
 * IN  msg_p		message to send
 * Results: pointer to message string, or NULL for error
 *			NOTE: pointer returned is to static storage 
 *			overwritten by each successive call.
 * note: for extra security, compare retaddr to serv_addr (why?)
 */
char *do_transaction(char *msg)
{
	static char buf[MSGLEN];
	struct sockaddr retaddr;
	socklen_t       addrlen;
	int ret;
	
	ret = sendto(sd, msg, strlen(msg), 0, &serv_addr, serv_alen);
	if ( ret == -1 ){
		syserr("sendto");
		return(NULL);
	}

	/* Get the response back */
	ret = recvfrom(sd, buf, MSGLEN, 0, &retaddr, &addrlen);
	if ( ret == -1 ){
		syserr("recvfrom");
		return(NULL);
	}

	/* Now return the message itself */
	return(buf);
} /* do_transaction */


第13章 基于数据报的编程:编写许可证服务器

(2)服务器版本1

/****************************************************************************
 * lsrv1.c  
 * License server server program version 1
 */

#include<stdio.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<signal.h>
#include<sys/errno.h>
#define MSGLEN		128		/* Size of our datagrams */

int main(int ac, char *av[])
{
	struct sockaddr_in client_addr;
	socklen_t addrlen;
	char      buf[MSGLEN];
	int	  ret;
	int       sock;

	sock = setup();

	while(1) {
		addrlen = sizeof(client_addr);
		ret = recvfrom(sock,buf,MSGLEN,0,
				(struct sockaddr*)&client_addr,&addrlen);
		if ( ret != -1 ){
			buf[ret] = '\0';
			narrate("GOT:", buf, &client_addr);
			handle_request(buf,&client_addr,addrlen);
		}
		else if ( errno != EINTR )
			perror("recvfrom");
	}
}
/****************************************************************************
 * lsrv_funcs1.c
 * functions for the license server
 */

#include<stdio.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<netdb.h>
#include<signal.h>
#include<sys/errno.h>
#include<stdlib.h>
#include<string.h>
#include<arpa/inet.h>

#define SERVER_PORTNUM	2020		/* Our server's port number */
#define MSGLEN		128		/* Size of our datagrams */
#define TICKET_AVAIL	0		/* Slot is available for use */
#define MAXUSERS	3		/* Only 3 users for us */
#define	oops(x)	{ perror(x); exit(-1); }

/****************************************************************************
 * Important variables
 */
int ticket_array[MAXUSERS];	/* Our ticket array */
int sd = -1;			/* Our socket */
int num_tickets_out = 0;	/* Number of tickets outstanding */

char *do_hello();
char *do_goodbye();
void narrate(char *msg1, char *msg2, struct sockaddr_in *clientp);
void free_all_tickets();

/****************************************************************************
 * setup() - initialize license server
 */
int setup()
{
	sd = make_dgram_server_socket(SERVER_PORTNUM);
	if ( sd == -1 )
		oops("make socket");
	free_all_tickets();
	return sd;
}
void free_all_tickets()
{
	int	i;

	for(i=0; i<MAXUSERS; i++)
		ticket_array[i] = TICKET_AVAIL;
}

/****************************************************************************
 * shut_down() - close down license server
 */
void shut_down()
{
	close(sd);
}

/****************************************************************************
 * handle_request(request, clientaddr, addrlen)
 *   branch on code in request
 */
void handle_request(char *req,struct sockaddr_in *client, socklen_t addlen)
{
	char	*response;
	int	ret;

	/* act and compose a response */
	if ( strncmp(req, "HELO", 4) == 0 )
		response = do_hello(req);
	else if ( strncmp(req, "GBYE", 4) == 0 )
		response = do_goodbye(req);
	else
		response = "FAIL invalid request";

	/* send the response to the client */
	narrate("SAID:", response, client);
	ret = sendto(sd, response, strlen(response),0, 
			(struct sockaddr *) client, addlen);
	if ( ret == -1 )
		perror("SERVER sendto failed");
}

/****************************************************************************
 * do_hello
 * Give out a ticket if any are available
 * IN  msg_p			message received from client
 * Results: ptr to response
 *    NOTE: return is in static buffer overwritten by each call
 */
char *do_hello(char *msg_p)
{
	int x;
	static char replybuf[MSGLEN];

	if(num_tickets_out >= MAXUSERS) 
		return("FAIL no tickets available");

	/* else find a free ticket and give it to client */

	for(x = 0; x<MAXUSERS && ticket_array[x] != TICKET_AVAIL; x++) 
		;

	/* A sanity check - should never happen */
	if(x == MAXUSERS) {
		narrate("database corrupt","",NULL);
		return("FAIL database corrupt");
	}

	/* Found a free ticket.  Record "name" of user (pid) in array.
	 * 	generate ticket of form: pid.slot
	 */
	ticket_array[x] = atoi(msg_p + 5); /* get pid in msg */
	sprintf(replybuf, "TICK %d.%d", ticket_array[x], x);
	num_tickets_out++;
	return(replybuf);
} /* do_hello */

/****************************************************************************
 * do_goodbye
 * Take back ticket client is returning
 * IN  msg_p			message received from client
 * Results: ptr to response
 */
char *do_goodbye(char *msg_p)
{
	int pid, slot;		/* components of ticket	*/

	/* The user's giving us back a ticket.  First we need to get
 	 * the ticket out of the message, which looks like:
	 *
	 * 	GBYE pid.slot
	 */
	if((sscanf((msg_p + 5), "%d.%d", &pid, &slot) != 2) ||
	   (ticket_array[slot] != pid)) {
		narrate("Bogus ticket", msg_p+5, NULL);
		return("FAIL invalid ticket");
	}

	/* The ticket is valid.  Release it. */
	ticket_array[slot] = TICKET_AVAIL;
	num_tickets_out--;

	/* Return response */
	return("THNX See ya!");
} /* do_goodbye */

/****************************************************************************
 * narrate() - chatty news for debugging and logging purposes
 */
void narrate(char *msg1, char *msg2, struct sockaddr_in *clientp)
{
	fprintf(stderr,"\t\tSERVER: %s %s ", msg1, msg2);
	if ( clientp )
		fprintf(stderr,"(%s : %d)", inet_ntoa(clientp->sin_addr),
						ntohs(clientp->sin_port));
	putc('\n', stderr);
}

第13章 基于数据报的编程:编写许可证服务器

第13章 基于数据报的编程:编写许可证服务器

程序运行结果

第13章 基于数据报的编程:编写许可证服务器

第13章 基于数据报的编程:编写许可证服务器

因为进程运行是随机的,所以拿钥匙时结果不是按顺序的。

5.许可证服务器版本2

(1)处理客户端崩溃

服务器必须实现两个独立的操作:等待客户的请求,同时周期性回收丢失的票据。调度行为是简单的,只要使用alarm和signal技术来周期性地调用一个函数。

服务器要收回已经不存在进程的票据,如何判断一个进程是否存在。可以使用popen来运行ps,然后从ps的输出中查找pid是否存在。二是通过给进程发送编号为0的信号以确定它是否存在,如果进程不存在,内核将不会发送信号,而是返回错误并设置errno为ESRCH。

在lserv2.c文件中

/****************************************************************************
 * lsrv2.c  
 * License server server program version 2 - features ticket recycling
 */

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <signal.h>
#include <sys/errno.h>

#define	MSGLEN		128

#define RECLAIM_INTERVAL 5		/* reclaim every 60 seconds */
int main(int ac, char *av[])
{
	struct sockaddr client_addr;
	socklen_t addrlen;
	char      buf[MSGLEN];
	int	  ret, sock;
	void	  ticket_reclaim();	/* version 2 addition */
	unsigned  time_left;

	sock = setup();
	signal(SIGALRM, ticket_reclaim); /* run ticket reclaimer */
	alarm(RECLAIM_INTERVAL);	 /* after this delay     */

	while(1) {
		addrlen = sizeof(client_addr);
		ret = recvfrom(sock,buf,MSGLEN,0,
				(struct sockaddr *)&client_addr,&addrlen);
		if ( ret != -1 ){
			buf[ret] = '\0';
			narrate("GOT:", buf, &client_addr);
			time_left = alarm(0);
			handle_request(buf,&client_addr,addrlen);
			alarm(time_left);
		}
		else if ( errno != EINTR )
			perror("recvfrom");
	}
}

alarm(0)返回剩余的时间,此时闹钟会暂停,alarm(time_left)继续计时剩下的时间,这样做是为了防止客户请求和SIGALAM处理函数对共享数据的处理发生冲突。

lserv_funcs2.c文件的修改

/****************************************************************************
 * ticket_reclaim
 * go through all tickets and reclaim ones belonging to dead processes
 * Results: none
 */
#define RECLAIM_INTERVAL 60		/* Expire every 60 seconds */
void ticket_reclaim()
{
	int	i;
	char	tick[BUFSIZ];

	for(i = 0; i < MAXUSERS; i++) {
		if((ticket_array[i] != TICKET_AVAIL) &&
		   (kill(ticket_array[i], 0) == -1) && (errno == ESRCH)) {
			/* Process is gone - free up slot */
			sprintf(tick, "%d.%d", ticket_array[i],i);
			narrate("freeing", tick, NULL);
			ticket_array[i] = TICKET_AVAIL;
			num_tickets_out--;
		}
	}
	alarm(RECLAIM_INTERVAL);	/* reset alarm clock */
} 

(2)处理服务器崩溃

客户和服务器必须增加票据验证

第13章 基于数据报的编程:编写许可证服务器

lclnt2.c程序

/****************************************************************************
 * lclnt2.c
 * License server client version 2
 *  link with lclnt_funcs2.o dgram.o
 */

#include<stdio.h>
#include<stdlib.h>
void do_regular_work();

int main(int ac, char *av[])
{
	setup();
	if (get_ticket() != 0 )
		exit(0);	

	do_regular_work();

	release_ticket();
	shut_down();

}
/****************************************************************************
 * do_regular_work  the main work of the application goes here
 */
void do_regular_work()
{
	printf("SuperSleep version 1.0 Running - Licensed Software\n");
	sleep(15);	/* our patented sleep algorithm */

	if ( validate_ticket() != 0 ){
		printf("Server errors. Please Try later.\n");
		return;
	}
	sleep(15);
}

lclnt_funcs2.c客户端的验证请求

int validate_ticket()
{
	char *response;
	char buf[MSGLEN];

	if(!have_ticket)			/* bizarre */
		return(0);

	sprintf(buf, "VALD %s", ticket_buf);	/* compose request	*/

	if ( (response = do_transaction(buf)) == NULL )
		return(-1);

	narrate("Validated ticket: ", response);

	if ( strncmp(response, "GOOD", 4) == 0 )
		return(0);

	if ( strncmp(response,"FAIL",4) == 0){
		have_ticket = 0;
		return(-1);
	}
	narrate("Unknown message:", response);

	return(-1);
} 

lserv_funcs2.c服务器端验证回应

/****************************************************************************
 * handle_request(request, clientaddr, addrlen)
 *   branch on code in request
 */
void handle_request(char *req,struct sockaddr *client, socklen_t addlen)
{
	char	*response;
	int	ret;

	/* act and compose a response */
	if ( strncmp(req, "HELO", 4) == 0 )
		response = do_hello(req);
	else if ( strncmp(req, "GBYE", 4) == 0 )
		response = do_goodbye(req);
	else if ( strncmp(req, "VALD", 4) == 0 )
		response = do_validate(req);
	else
		response = "FAIL invalid request";

	/* send the response to the client */
	narrate("SAID:", response, client);
	ret = sendto(sd, response, strlen(response),0, client, addlen);
	if ( ret == -1 )
		perror("SERVER sendto failed");
}

/****************************************************************************
 * do_validate
 * Validate client's ticket
 * IN  msg_p			message received from client
 * Results: ptr to response
 */
static char *do_validate(char *msg)
{
	int pid, slot;          /* components of ticket */

	/* msg looks like VAD pid.slot - parse it and validate */
	 
	if (sscanf(msg+5,"%d.%d",&pid,&slot)==2 && ticket_array[slot] == pid) 
		return("GOOD Valid ticket");

	/* bad ticket */
	narrate("Bogus ticket", msg+5, NULL);
	return("FAIL invalid ticket");
} 

6.Unix域socket

本地地址通常叫做Unix域地址,它是一个文件名,没有主机和端口号。

为了学习Unix域socket的客户/服务器编程,这里编写了一个日志系统。这里的日志系统使用unix域socket地址。只有同一台主机上的客户才能发消息给它。

/**********************************************************************
 * logfilec.c - logfile client - send messages to the logfile server
 *              usage: logfilec "a message here"
 */

#include<stdio.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<sys/un.h>
#include<stdlib.h>

#define	SOCKET	"/tmp/logfilesock"
#define	oops(m,x) { perror(m); exit(x); }

main(int ac, char *av[])
{
	int	           sock;
	struct sockaddr_un addr;
	socklen_t          addrlen;
	char	           sockname[] = SOCKET ;
	char               *msg = av[1];

	if ( ac != 2 ){
		fprintf(stderr,"usage: logfilec 'message'\n");
		exit(1);
	}
	sock = socket(PF_UNIX, SOCK_DGRAM, 0);
	if ( sock == -1 )
		oops("socket",2);

	addr.sun_family = AF_UNIX;
	strcpy(addr.sun_path, sockname);
	addrlen = strlen(sockname) + sizeof(addr.sun_family) ;

	if ( sendto(sock,msg, strlen(msg), 0, 
				(struct sockaddr *)&addr, addrlen) == -1 )
		oops("sendto",3);
}
/*************************************************************************
 * logfiled.c  - a simple logfile server using Unix Domain Datagram Sockets
 * 	         usage: logfiled >>logfilename
 */

#include<stdio.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<sys/un.h>
#include<time.h>
#include<stdlib.h>

#define	MSGLEN	512
#define oops(m,x) { perror(m); exit(x); }
#define	SOCKNAME "/tmp/logfilesock"

int main(int ac, char *av[])
{
	int	sock;			/* read messages here	*/
	struct sockaddr_un addr;	/* this is its address	*/
	socklen_t addrlen;
	char	msg[MSGLEN];
	int	l;
	char	sockname[] = SOCKNAME ;
	time_t	now;
	int	msgnum = 0;
	char	*timestr;

	/* build an address */
	addr.sun_family = AF_UNIX;		/* note AF_UNIX */
	strcpy(addr.sun_path, sockname);	/* filename is address */
	addrlen = strlen(sockname) + sizeof(addr.sun_family);

	sock = socket(PF_UNIX, SOCK_DGRAM, 0);	/* note PF_UNIX  */
	if ( sock == -1 )
		oops("socket",2);

	/* bind the address */
	if ( bind(sock, (struct sockaddr *) &addr, addrlen) == -1 )
		oops("bind", 3);

	/* read and write */
	while(1)
	{
		l = read(sock, msg, MSGLEN);	/* read works for DGRAM	*/
		msg[l] = '\0';			/* make it a string 	*/
		time(&now);
		timestr = ctime(&now);
		timestr[strlen(timestr)-1] = '\0';	/* chop newline */

		printf("[%5d] %s %s\n", msgnum++, timestr, msg);
		fflush(stdout);
	}
}

程序输出结果

第13章 基于数据报的编程:编写许可证服务器

第13章 基于数据报的编程:编写许可证服务器

小结,两种socket和两种socket地址

第13章 基于数据报的编程:编写许可证服务器