首先《UNIX网络编程》对这部分代码做了足够的错误检查,但是个人觉得这会分散注意力,他不能够让我们集中注意力在假设所有的情况都是正常的情况下,如何编写一个可通信的网络程序?他的基本框架应该是个什么样的?网络通信代码是不是所有代码中最重要的部分?下面我给出我的一个非常简化的版本,稍后我会对我这个版本做一个简单的说明。
时间服务器代码
#include <iostream>
#include <string.h>
#include <netinet/in.h>
using namespace std;
void serve_main(int sockfd)
{
const int BUFLEN = 1024;
char buf[BUFLEN];
bzero(buf, BUFLEN);
time_t nTime = time(NULL);
memcpy(buf, ctime(&nTime), BUFLEN-1);
write(sockfd, buf, BUFLEN);
}
int main(int argc, char ** argv) {
int listenfd;
listenfd = socket(AF_INET, SOCK_STREAM, 0);
struct sockaddr_in serveaddr;
serveaddr.sin_family = AF_INET;
serveaddr.sin_port = htons(8088);
serveaddr.sin_addr.s_addr = htonl(INADDR_ANY);
bind(listenfd, (struct sockaddr *)&serveaddr, sizeof(serveaddr));
listen(listenfd, 5);
for ( ; ; ) {
int connfd = accept(listenfd, NULL, NULL);
int pid = fork();
if (pid == 0) {
close(listenfd);
serve_main(connfd);
close(connfd);
exit(0);
} else {
close(connfd);
}
}
return 0;
}
时间客户端代码
#include <iostream>
#include <netinet/in.h>
#include <string.h>
#include <arpa/inet.h>
using namespace std;
void client_main(int sockfd)
{
const int BUFLEN = 1024;
char buf[BUFLEN];
read(sockfd, buf, BUFLEN-1);
cout << buf << endl;
}
int main(int argc, char ** argv) {
string strServeIP = "127.0.0.1";
int sockfd;
struct sockaddr_in serveaddr;
bzero(&serveaddr, sizeof(serveaddr));
serveaddr.sin_family = AF_INET;
serveaddr.sin_port = htons(8088);
inet_pton(AF_INET, strServeIP.c_str(), &(serveaddr.sin_addr));
sockfd = socket(AF_INET, SOCK_STREAM, 0);
connect(sockfd, (struct sockaddr *)&serveaddr, sizeof(serveaddr));
client_main(sockfd);
close(sockfd);
return 0;
}
你可以看到这组代码没有任何错误处理,一个健壮的网络程序应该加上所有的可能的网络错误!
你还会看到我的网络输入输出代码紧紧使用了系统的read和write,但是,我并没有展示当程序混合使用标准输入输出时的情况!
你还会发现,我是用了并发服务器的框架,所有的业务都是子进程处理!
从上述的三条描述我们至少知道了我们需要学习的方向:第一,API函数在什么情况下会出错,当API函数出错了我们应该如何处理?第二,网络的输入输出和系统的输入输出混合时,整个系统和网络会出现什么情况?第三,网络编程并不是最重要的编程部分,但是也不是一个可忽视的编程的地方。你会发现实际的业务都是有client_main和serve_main函数处理的!