1.tcp.h:头文件
2.socklib.c:基本的socket模型的实现函数
3.server.c:服务器端的模型
4.client.c:客户端的模型
5.process_request.c:处理客户端的请求
6.connect_server.c:与服务端进行通信
1.tcp.h:头文件
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <netinet/in.h>
#include <time.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <strings.h>
#include <arpa/inet.h>
#include <signal.h>
#include <sys/time.h>
#include <sys/wait.h>
#include <errno.h>
#define IPSERV "127.0.0.1"
#define PORT 13000
#define BACKLOG 1
#define oops(msg) {perror(msg);exit(1);}
extern int errno;
2.socklib.c:基本的socket模型的实现函数
#include "tcp.h"
//函数声明
int make_server_socket_q(int,int);
/*
* 输入:端口号
*
*/
int make_server_socket(int port){
return make_server_socket_q(port,BACKLOG);
}
int make_server_socket_q(int port,int backlog){
// build our address
struct sockaddr_in serv;
int sockfd;
// get a socket
sockfd = socket(AF_INET,SOCK_STREAM,0);
if(sockfd<0)
oops("socket");
// build address struct
/* clear out struct */
bzero((void *)&serv,sizeof(serv));
/* fill in addr family */
serv.sin_family = AF_INET;
/* fill in ip address */
serv.sin_addr.s_addr = inet_addr(IPSERV);
/* fill in socket port */
serv.sin_port = htons(port);
// bind the address information to the socket
if(bind(sockfd,(struct sockaddr *)&serv,sizeof(serv))<0)
oops("bind");
// waiting for the client request
if(listen(sockfd,backlog)<0)
oops("listen");
return sockfd;
}
int connect_to_server(char *host,int port){
// build our address
struct sockaddr_in serv;
int sockfd;
// get a socket
sockfd = socket(AF_INET,SOCK_STREAM,0);
if(sockfd < 0)
oops("socket");
// build address struct
bzero((void *)&serv,sizeof(serv));
serv.sin_family = AF_INET;
serv.sin_addr.s_addr = inet_addr(host);
serv.sin_port = htons(port);
// connect to server
if(connect(sockfd,(struct sockaddr *)&serv,sizeof(serv))<0)
oops("connect");
return sockfd;
}
3.server.c:服务器端的模型
#include "tcp.h"
void child_waiter(int);
/*
* 标准的服务器处理模型
*
*
* 程序运行到信号处理函数跳转时中断accept。
* accept被中断时,返回-1,设置errno=EINTR,会跳出主循环
* if(fd<0){
* if(errno == EINTR)
* continue;
* else
* oops("accept");
*}
*/
int main(int ac,char *av[]){
// the socket
int sockfd;
int fd;
// get a socket which is listening
sockfd = make_server_socket(PORT);
if(sockfd == -1)
exit(1);
// deal with signal of SIGCHLD
/*
* when the child exit or be out,
* kernel will send a signal——SIGCHLD to parent
* 默认情况下SIGCHLD会被忽略
*
* 僵尸进程会在什么时候产生
* 子进程结束,但是他的父进程没有等待(wait/waitpid)
* 该子进程会变成僵尸进程
* 可是如果父进程已经提前结束
* 该子进程会变成孤儿进程,被init接管
*
* 为了防止子进程成为僵尸进程,处理SIGCHLD信号
* 调用wait(NULL)来处理
*
*/
signal(SIGCHLD,child_waiter);
// the connect request is coming
while(1){
// take the connection
fd = accept(sockfd,NULL,NULL);
if(fd<0){
if(errno == EINTR)
continue;
else
oops("accept");
}
// connect with the client
process_request(fd);
// close the socket
close(fd);
}
close(sockfd);
}
/*
* 可是呢,调用wait(NULL)来处理会出现问题
* 如果多个子进程几乎同时退出
* 最先到到达的信号会导致父进程跳转到处理函数中
* 然后父进程调用wait将子进程从进程表中删除
* 可是,其他到来的信号怎么办呢
* 我们知道,信号是没有缓存机制的
* 第二个到来的信号被阻塞
* 第三个到来的信号会丢失,以后的依次类推
* 信号处理函数只调用wait一次,
* 可是后来到达的信号没被处理,子进程会变成僵尸进程
*
* 解决方法:在处理函数中,
* 调用wait足够多的次数去清除所有的子进程
* waitpid提供了wait函数超集的功能
* 参数1:表示要等待的进程ID,-1,表示等待所有的子进程
* 参数2:指向整型值的指针,用来获取子进程状态
* 在未来的改版中,会根据这个参数来跟踪服务器信息
* 参数3:选项,WNOHANG高速waitpid若没有僵尸进程,则不必等待
*
* 该循环直到所有退出的子进程都被等待才停止。
* 即使多个子进程同时退出并产生SIGCHLD,所有信号都会被处理。
*
*/
void child_waiter(int signum){
while(waitpid(-1,NULL,WNOHANG)>0);
}
4.client.c:客户端的模型
#include "tcp.h"
int main(){
int sockfd;
sockfd = connect_to_server(IPSERV,PORT);
if(sockfd == -1)
oops("sockfd");
talk_with_server(sockfd);
close(sockfd);
}
5.process_request.c:处理客户端的请求
6.connect_server.c:与服务端进行通信
talk_with_server(int fd){
talk_with_server_v1(fd);
}
process_request(int fd){
process_request_v1(fd);
}