20155321 第八周课堂实践3
多进程实现daytime服务器
-
知识储备
- daytime协议:daytime服务其实是一个好用的调试工具,它的返回值是当前的日期和时间(字符串格式)。而基于TCP的daytime服务,一旦有连接建立就返回ASCII形式的日期和时间,在传送完后关闭连接,接收到的数据则被忽略。
- 因为是多进程实现daytime服务器,因此需要用到fork()函数创建进程,而课堂上所实现的daytime服务器每次只是接收一个客户端的请求,这会导致服务器的效率比较低下。因此可以采用多进程并发技术提高服务器的处理效率。
- 关于fork()函数:调用了fork()函数后,子进程和父进程会继续执行fork()函数后的代码。因为子进程是父进程的副本,所以子进享有父进程的数据空间、堆栈,但是父进程和子进程并不共享这些内存的存储空间。
-
在基于socket编程的基础上,以下为服务端代码部分
#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<string.h>
#include<ctype.h>
#include<arpa/inet.h>
#include<unistd.h>
#include<errno.h>
#include <time.h>
#define SERV_PORT 13321
int std_err(const char* name)
{
perror(name);
exit(1);
}
int main(void)
{
int sfd, cfd, ret;
int len;
pid_t pid;
socklen_t clie_len;
char buf[BUFSIZ], clibuf[32];
//创建服务器套节字
sfd = socket(AF_INET, SOCK_STREAM, 0);
if(sfd == -1)
std_err("socket");
//定义地址类型
struct sockaddr_in serv_addr, clie_addr;
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(SERV_PORT);
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
//绑定服务器的IP、端口;
ret = bind(sfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr));
if(ret == -1)
std_err("bind");
//监听链接服务器的客户数量
ret = listen(sfd, 3);
if(ret == -1)
std_err("listen");
clie_len = sizeof(clie_addr);
while(1)
{
//阻塞等待客户端发起链接请求
cfd = accept(sfd, (struct sockaddr*)&clie_addr, &clie_len);
printf("服务器实现者20155321 ");
printf("客户端IP:%s
",inet_ntoa(clie_addr.sin_addr));
time_t t = time(0);
char tmp[64];
strftime( tmp, sizeof(tmp), "%Y/%m/%d %X %A
", localtime(&t) );
//这里无需打印当前时间,应该是客户端打印当前时间
if(cfd == -1)
std_err("accept");
pid = fork();
if(pid < 0)
std_err("fork:");
else if(pid == 0)
{
close(sfd);
break;
}
else //1.回收子进程,2,关闭不必要的文件描述父 3,继续等待客户端链接,如果有,则继续创建子进程
{
send(cfd,tmp,strlen(tmp),0);
close(cfd);
}
}
return 0;
}
- 在基于socket编程的基础上,以下为客户端代码部分
#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<string.h>
#include<ctype.h>
#include<arpa/inet.h>
#include<unistd.h>
#include<sys/wait.h>
#include<signal.h>
#include<errno.h>
#include<pthread.h>
#define SERV_PORT 13321
#define SERV_IP "127.0.0.1"
#define NUM 3
int std_err(const char* name)
{
perror(name);
exit(1);
}
int main(void)
{
int cfd, ret;
char buf[BUFSIZ];
pid_t pid;
int i;
for(i = 0; i < NUM; i++){
pid = fork();
if(pid == 0)
break;
else if(pid < 0)
std_err("fork");
}
//子进程逻辑
if(pid == 0)
{
//创建套节字
cfd = socket(AF_INET, SOCK_STREAM, 0);
if(cfd == -1)
std_err("socket");
//定义IP , 端口
struct sockaddr_in clie_addr;
clie_addr.sin_family = AF_INET;
clie_addr.sin_port = htons(SERV_PORT);
//转换IP 字符串的地址
ret = inet_pton(AF_INET, SERV_IP, &clie_addr.sin_addr.s_addr);
if(ret != 1)
std_err("inet_pton");
//链接服务器
ret = connect(cfd, (struct sockaddr*)&clie_addr, sizeof(clie_addr));
if(ret == -1)
std_err("connect");
char buff[256];
int nRecv=recv(cfd,buff,256,0);
if(nRecv>0)
{
buff[nRecv]=' ';
printf("当前时间:%s
",buff);
}
}
//关闭套节字
close(cfd);
return 0;
}
-
实验结果截图
-
实践中遇到的问题与解决办法
刚开始测试的时候出现了客户端打印的当前时间不正确的情况,如下图所示
后来发现,是在获取时间的代码部分有错误,因为是多进程,因此获取时间要放在创建了子进程之后,在while循环里,如下所示
while(1)
{
…………
time_t t = time(0);
char tmp[64];
strftime( tmp, sizeof(tmp), "%Y/%m/%d %X %A
", localtime(&t) );
…………
}
多线程实现daytime服务器
-
知识储备
- 上面是是采用多进程的方法来实现服务器,可以明显看到,这样处理后的主进程的效率提高了许多。但是,创建一个新的进程本身开销还是比较大的,因为这样会消耗很多务器端的资源。因此可以采用多线程的方法来实现服务器端。
- 通过学习,Linux下创建一个线程也比较方便,调用pthread_create()函数来创建线程即可,其中的一个参数的回调函数,也就是线程本身的执行体函数。
-
在基于socket编程的基础上,以下为服务端代码部分
#include<stdio.h>
#include<stdlib.h>
#include<pthread.h>
#include<sys/socket.h>
#include<sys/types.h> //pthread_t , pthread_attr_t and so on.
#include<netinet/in.h> //structure sockaddr_in
#include<arpa/inet.h>
#include<assert.h>
#include<string.h>
#include<unistd.h>
#include<ctype.h>
#include<arpa/inet.h>
#include <time.h>
#define SOCK_PORT 9988
#define BUFFER_LENGTH 1024
#define MAX_CONN_LIMIT 5 //MAX connection limit
static void Data_handle(void * sock_fd);
struct sockaddr_in s_addr_in;
struct sockaddr_in s_addr_client;
int sockfd;
int main()
{
int sockfd_server
int fd_temp;
int client_length;
sockfd_server = socket(AF_INET,SOCK_STREAM,0); //ipv4,TCP
assert(sockfd_server != -1);
memset(&s_addr_in,0,sizeof(s_addr_in));
s_addr_in.sin_family = AF_INET;
s_addr_in.sin_addr.s_addr = htonl(INADDR_ANY);
s_addr_in.sin_port = htons(SOCK_PORT);
fd_temp = bind(sockfd_server,(struct scokaddr*)(&s_addr_in),sizeof(s_addr_in));
if(fd_temp == -1)
{
fprintf(stderr,"bind error!
");
exit(1);
}
fd_temp = listen(sockfd_server,MAX_CONN_LIMIT);
if(fd_temp == -1)
{
fprintf(stderr,"listen error!
");
exit(1);
}
while(1)
{
pthread_t thread_id;
client_length = sizeof(s_addr_client);
sockfd = accept(sockfd_server,(struct sockaddr*)(&s_addr_client),&client_length);
time_t t = time(0);
char tmp[64];
strftime( tmp, sizeof(tmp), "%Y/%m/%d %X %A
", localtime(&t) );
send(sockfd,tmp,strlen(tmp),0);
close(sockfd); //close a file descriptor.
if(sockfd == -1)
{
fprintf(stderr,"Accept error!
");
continue;
}
if(pthread_create(&thread_id,NULL,(void *)(&Data_handle),(void *)(&sockfd)) == -1)
{
fprintf(stderr,"pthread_create error!
");
break;
}
}
int ret = shutdown(sockfd_server,SHUT_WR); //shut down the all or part of a full-duplex connection.
assert(ret != -1);
return 0;
}
static void Data_handle(void * sock_fd)
{
int fd = *((int *)sock_fd);
int i_recvBytes;
char data_recv[BUFFER_LENGTH];
printf("服务器实现者20155321 ");
printf("客户端IP:%s
",inet_ntoa(s_addr_client.sin_addr));
pthread_exit(NULL); //terminate calling thread!
}
- 在基于socket编程的基础上,以下为客户端代码部分
#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<string.h>
#include<ctype.h>
#include<arpa/inet.h>
#include<unistd.h>
#include<sys/wait.h>
#include<signal.h>
#include<errno.h>
#include<pthread.h>
#define SERV_PORT 13321
#define SERV_IP "127.0.0.1"
#define NUM 3
int std_err(const char* name)
{
perror(name);
exit(1);
}
int main(void)
{
int cfd, ret;
char buf[BUFSIZ];
pid_t pid;
int i;
//创建套节字
cfd = socket(AF_INET, SOCK_STREAM, 0);
if(cfd == -1)
std_err("socket");
//定义IP , 端口
struct sockaddr_in clie_addr;
clie_addr.sin_family = AF_INET;
clie_addr.sin_port = htons(SERV_PORT);
//转换IP 字符串的地址
ret = inet_pton(AF_INET, SERV_IP, &clie_addr.sin_addr.s_addr);
if(ret != 1)
std_err("inet_pton");
//链接服务器
ret = connect(cfd, (struct sockaddr*)&clie_addr, sizeof(clie_addr));
if(ret == -1)
std_err("connect");
char buff[256];
int nRecv=recv(cfd,buff,256,0);
if(nRecv>0)
{
buff[nRecv]=' ';
printf("当前时间:%s
",buff);
}
}
//关闭套节字
close(cfd);
return 0;
}
-
实验结果截图
-
实践中遇到的问题与解决办法
在开始编译server.c文件的时候编译出现了错误,如下图所示:
后来在网上资料的提示下,才记得要加上-lpthread参数,这样编译就可以顺利通过了