下面的代码是一个简单的C/S程序,程序源于《UNIX网络编程》第五章,程序实现如下功能:
在客户端的标准输入上输入一个字符串,到达服务端之后,服务端原样返回字符串给客户端,在客户端回显该字符串。
首先是客户端和服务端的代码描述:
server.c代码:
int main()
{
int listenfd;
int connfd;
pid_t child_pid;
size_t len;
struct sockaddr_in clientaddr = {0};
struct sockaddr_in serveraddr = {0};
listenfd = socket( AF_INET, SOCK_STREAM, 0);
serveraddr.sin_family = AF_INET;
serveraddr.sin_addr.S_addr = htonl(INADDR_ANY);
serveraddr.sin_port = htons(9999);
bind(listenfd,(struct sockaddr*)&serveraddr, sizeof(serveraddr));
listen(listenfd, 5);
while (1)
{
len = sizeof(clientaddr);
connfd = accept(listenfd,(struct sockaddr*)&clientaddr, &len);
if ( (child_pid = fork()) == 0)
{
close(listenfd);
str_echo(connfd);
exit(0);
}
close(connfd);
}
}
void str_echo( int sockfd )
{
size_t n ;
char buf[1024] = {0};
again:
while ( (n = read(sockfd, buf,1024)) > 0 )
{
write(sockfd, buf, n );
}
if ( n < 0 && errno == EINTR )
{
goto again:
}
else if(n < 0)
{
err_sys("read err!");
}
}
client.c的代码
int main()
{
int sockfd;
struct sockaddr_in serveraddr = {0};
sockfd = socket( AF_INET, SOCK_STREAM, 0);
serveraddr.sin_family = AF_INET;
serveraddr.sin_addr.S_addr = htonl(INADDR_ANY);
serveraddr.sin_port = htons(9999);
connect(sockfd, (struct sockaddr*)&serveraddr, sizeof(serveraddr));
str_cli(stdin,sockfd);
exit(0);
}
void str_cli( FILE* fp, int sockfd )
{
char sendline[1024] = {0};
char recvline[1024] = {0};
while( NULL != fgets(sendline, 1024, fp))
{
write(sockfd, sendline, strlen(sendline));
if ( 0 == read(sockfd, recvline, 1024))
{
err_quit("str_cli:sever terminated prematurely");
}
fputs(recvline,stdout);
}
}
程序非常的简单,但是程序折射出来的问题需要我们细细的去琢磨,有如下的问题需要去思考:
(1) 客户端和服务端的建链过程是什么?需要了解三次握手建链,及建链过程中的状态转移
(2) 客户端和服务端分别正常终止的时候,程序会发生什么样的情况?
(3) 客户端和服务端分别异常终止的时候,程序会发生什么样的情况?
程序最后的总结:
(1) 如何处理僵尸子进程;---捕获SIGCHILD信号
(2) 当服务器进程终止的时候,客户端进程没有感知到,这需要怎样处理?---IO复用
(3) 服务器主机崩溃,需要客户端向服务端发送了数据才能检测到?---SO_KEEPALIVE