您的位置:首页 > 编程语言

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

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

客户端主程序:

view
plainprint?

#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。

view
plainprint?

#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_ */

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

view
plainprint?

#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的定义。

view
plainprint?

#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_ */

上面代码中,用到了一些结构体及函数,这里给出一个简单的介绍。

view
plainprint?

/* <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)

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