代码开源(3)——UNIX中CS简单实现

     主要摘自《深入理解计算机系统》一书,略作整理,加了些备注。可以简单了解一下UNIX网络编程。下面程序已在Ubuntu9.10下测试通过。

     客户端主程序:

#include "client.h"

int main(int argc, char **argv)
{
	int clientfd; //客户端套接字描述符
	if(argc != 3) //参数必须是3个
	{
		fprintf(stderr, "usage: %s <host> <port>\n", argv[0]);
		return 0;
	}

	clientfd = open_clientfd(argv[1], atoi(argv[2]));  //打开连接
	exchange_data(clientfd);                           //与服务器交换数据
	close(clientfd);                                   //关闭客户端套接字
	return 0;
}

      主要包含了一个头文件,里面有具体的实现。下面是client.h的定义。其中所用的IO输入输出,就是代码开源(2)——UNIX 健壮I/O函数介绍的健壮IO,包含进来即可。这里就不重复给出了。

      对客户端程序,当标准输入遇到EOF,或者因为用户在键盘键入ctrl-d,或者因为在一个重定向的输入文件中用尽了所有的文本行,循环就结束。这时客户端关闭套接字描述符,并发送一个EOF通知服务器,服务器就可以收到一个为0的返回码,从而确定客户端已经关闭。也就是说,当服务器和客户端都运行时,客户端要想退出,最简单的就是按ctrl-d。

#ifndef CLIENT_H_
#define CLIENT_H_

#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <string.h>
#include <netdb.h>
#include "rio.h"

#define MAX_LINE 1024

void unix_error(char *msg);                    //错误处理程序
void exchange_data(int fd);                    //与服务器交换数据
int open_clientfd(char *hostname, int port);   //打开连接,外部调用
int _open_clientfd(char *hostname, int port);  //打开连接,内部调用

//错误处理程序,打印错误信息
void unix_error(char *msg)
{
	fprintf(stderr, "%s: %s\n", msg, strerror(errno));
	exit(0);
}
//与服务器交换数据
void exchange_data(int fd)
{
	char buf[MAX_LINE];
	rio_t rio;
	rio_readinitb(&rio, fd); //初始化rio_t

	while(fgets(buf, MAX_LINE, stdin) != NULL)   //从标准输入读入数据
	{
		rio_writen(fd, buf, strlen(buf));       //发往服务器端
		rio_readlineb(&rio, buf, MAX_LINE);     //从服务器读一行
		fputs(buf, stdout);                     //写到标准输出
	}
}
//打开连接,外部调用
int open_clientfd(char *hostname, int port)
{
	int rc;
	if((rc = _open_clientfd(hostname, port)) < 0)
	{
		if(rc == -1)
			unix_error("Open client UNIX error");
		else
			unix_error("Open client DNS error");
	}
	return rc;
}
//打开连接,内部调用
int _open_clientfd(char *hostname, int port)
{
	int clientfd;                  //客户端套接字描述符
	struct hostent *hp;            //主机条目
	struct sockaddr_in serveraddr; //服务器地址

	if((clientfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) //返回-1表示出错
		return -1;
	if((hp = gethostbyname(hostname)) == NULL)      //返回空表示出错
		return -2;

	bzero((char *)&serveraddr, sizeof(serveraddr)); //清零
	serveraddr.sin_family = AF_INET;
	bcopy((char *)hp->h_addr, (char *)&serveraddr.sin_addr.s_addr, hp->h_length); //服务器IP地址
	serveraddr.sin_port = htons(port); //转换为网络序

	if(connect(clientfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr)) < 0) //连接
		return -1;
	printf("Connect to server success\n");
	return clientfd;  //连接成功
}

#endif /* CLIENT_H_ */

         下面给出服务器的主程序:

#include "server.h"

int main(int argc, char **argv)
{
	int listenfd, connfd;
	unsigned int clientlen;         //地址长度
	struct sockaddr_in clientaddr;  //客户端地址
	struct hostent *hp;
	char *haddrp;                   //客户端域名

	if(argc != 2)                   //参数必须是2个
	{
		fprintf(stderr, "usage: %s <port>\n",argv[0]);
		return 0;
	}
	listenfd = open_listenfd(atoi(argv[1])); //进入监听状态
	clientlen = sizeof(clientaddr);
	while(1) //只支持单个连接
	{
		if((connfd = accept(listenfd, (struct sockaddr *)&clientaddr, &clientlen)) < 0) //建立连接
		{
			fprintf(stderr, "Accept error");
			exit(0);
		}

		hp = gethostbyaddr((const char *)&clientaddr.sin_addr.s_addr,
				sizeof(clientaddr.sin_addr.s_addr), AF_INET);  //客户端域名
		haddrp = inet_ntoa(clientaddr.sin_addr);               //客户端IP地址

		printf("%s (%s) connect to server\n", hp->h_name, haddrp);
		exchange_data(connfd);   //与客户端交换数据
		close(connfd);           //客户端断开连接
		printf("%s (%s) close\n", hp->h_name, haddrp);
	}
	return 0;
}

           主要包含了一个头文件,里面有具体的实现。下面是server.h的定义。

#ifndef SERVER_H_
#define SERVER_H_

#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <string.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <errno.h>
#include <unistd.h>
#include "rio.h"

#define MAX_LINE 1024
#define MAX_LISTEN 1024          //最大连接数

void unix_error(char *msg);      //错误处理
void exchange_data(int connfd);  //与客户端交换数据
int open_listenfd(int port);     //打开监听,外部调用
int _open_listenfd(int port);    //打开监听,内部调用

//错误处理程序,打印错误信息
void unix_error(char *msg)
{
	fprintf(stderr, "%s: %s\n", msg, strerror(errno));
	exit(0);
}
//与客户端交换数据
void exchange_data(int connfd)
{
	size_t n;
	char buf[MAX_LINE];
	rio_t rio;
	rio_readinitb(&rio, connfd);
	while((n = rio_readlineb(&rio, buf, MAX_LINE)) != 0) //读到末尾结束
	{
		printf("server received %d bytes\n", n); //收到的字节数
		rio_writen(connfd, buf, n);              //反显到客户端
	}
}
//打开监听
int open_listenfd(int port)
{
	int rc;
	if((rc = _open_listenfd(port)) < 0)
		unix_error("Open server listen error");
	return rc;
}
//建立监听函数,内部调用
int _open_listenfd(int port)
{
	int listenfd;   //服务器套接字描述符
	int optval = 1;
	struct sockaddr_in serveraddr;

	if((listenfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) //返回-1表示出错
		return -1;

	if(setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR,
			(const void *)&optval, sizeof(int)) < 0)  //忽略地址正在使用的错误
	{
		close(listenfd);
		return -1;
	}

	bzero((char *)&serveraddr, sizeof(serveraddr));
	serveraddr.sin_family = AF_INET;
	serveraddr.sin_addr.s_addr = htonl(INADDR_ANY);    //接受来自主机的任何IP地址
	serveraddr.sin_port = htons((unsigned short)port); //网络字节顺序

	if(bind(listenfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr)) < 0) //绑定
	{
		close(listenfd);
		return -1;
	}
	if(listen(listenfd, MAX_LISTEN) < 0) //开始监听
	{
		close(listenfd);
		return -1;
	}
	return listenfd;
}
#endif /* SERVER_H_ */
       上面代码中,用到了一些结构体及函数,这里给出一个简单的介绍。

/* <netdb.h>
 * DNS主机条目结构
 *  struct hostent{
 *  	char *h_name;        官方名字
 *  	char **h_aliases;    一组别名
 *  	int h_addrtype;      地址类型
 *  	int h_length;        地址字节长度
 *  	char **h_addr_list;  一组IP地址
 * };
 */

/* <sys/socket.h>
 * 套接字地址结构
 * struct sockaddr{
 * 		unsigned short sa_family;
 * 		sa_data[14];
 * };
 * struct in_addr{
 * 		unsigned int s_addr;  网络字节顺序,大端法
 * };
 * struct sockaddr_in{ 后缀_in表示internet
 * 		unsigned short sin_family;
 * 		unsigned short sin_port;    端口号
 * 		struct in_addr sin_addr;    IP地址
 * 		unsigned char sin_zero[8];  补齐
 * };
 */
/*
 * <string.h>
 * extern void bzero(void *s, int n)
 * extern void bcopy(const void *src, void *dest, int n)
 */

         

你可能感兴趣的:(struct,server,socket,unix,服务器,Exchange)