为了流量券,冲
B站就业班视频
比较起来,还是多进程的服务器更好,用线程函数写的代码,有如下俩问题:
1.客户端刚关闭的时候,服务器压根不知道,要关了再连接的时候,服务器才反应过来。
我自己分析原因:对应 线程执行函数,读取服务器数据 n=read() 当n <=0 时,因为函数一直在这里阻塞,所以收不到n=0的信号(我的见解不知道对不对)
2.主线程应该关闭accept函数产生的文件描述符newfd,现在找不到写主线程代码的地方 - -
3.线程与线程之间,两种文件描述符(监听,处理信息)都是共享的,如果想让它们实现不同享,需要先构建一个结构体Allinfo,把客户端地址sockadd_in,文件描述符,线程号包起来,再利用数组的元素内存地址不同,把这个大结构体Allinfo的指针传到线程回调函数里 当参数 ,太麻烦了,后面没听讲
//多线程 的 高并发服务器。注意添加pthread.h头文件
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include<netinet/in.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <ctype.h>
#include <pthread.h>
//先声明线程回调函数calbak
void * calbak (void *arg){
int *fd2 =(int *) arg;
//这个函数里面应该无法打印客户端地址了(ip和端口)因为这个局部变量不认识dst
//printf("child process,IP:%s,PORT: %d\n", inet_ntop(AF_INET, &dst.sin_addr.s_addr,addstring,sizeof(addstring)),ntohs(dst.sin_port));
//其次,把客户端发送过来的数据读出来
int i =0;
int n =0;
char buf[1024];
while (1){
memset(buf,0x00,sizeof(buf));
n = read (*fd2,buf,sizeof(buf));
if (n<=0){
printf("有一个客户端已关闭,或者读到的字符为0/n");
break;
}
for (i=0;i<n;i++){
buf[i] =toupper(buf[i]);
}
//把数据传回客户端,变成大写了已经
write (*fd2, buf, n);
}
//关闭通信描述符,其实通信描述符是newfd,作为参数传进来了,现在转化为*fd2整数。
close(*fd2);
pthread_exit(NULL);
}
int main() {
int sfd = socket(AF_INET, SOCK_STREAM, 0);
if (sfd <0){
printf("socket fun error/n");
return -1;
}
//定义客户端地址serad
struct sockaddr_in serad;
//外部客户可以连接本机任意IP地址。端口设置为8888
bzero(&serad, sizeof(serad));
serad.sin_family =AF_INET;
serad.sin_port =htons(8888);
serad.sin_addr.s_addr = htonl (INADDR_ANY);
int ret = bind(sfd, (struct sockaddr *)&serad, sizeof(serad));
if (ret<0) {
printf("bind error/n");
return -1;
}
//将socket从主动变为被动(服务器必备),这样可以监听来自客户的请求
listen( sfd,128);
// int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
//addr是传出参数,保留客户的地址,所以这是客户地址
//定义客户端地址dst,dst的端口也是8888
struct sockaddr_in dst;
pthread_t thnum;
int newfd;
while (1) {
bzero(&dst, sizeof(dst));
dst.sin_family =AF_INET;
dst.sin_port =htons(8888);
socklen_t dstleng = sizeof(dst);
//接收一个客户端的连接请求,得到信息传递描述符newfd
//并把这个newfd作为参数,传进子线程的执行函数(回调函数)中
newfd = accept (sfd, (struct sockaddr* )&dst, &dstleng);
if (newfd <0) {
printf("服务端,accept error、\n");
return -1;
}
//打印客户端地址和端口,
char addstring[128];
memset(addstring, 0x00,128);
printf("服务器端,还没有创建新线程,客户端地址是:\n");
printf("IP:%s,PORT: %d\n", inet_ntop(AF_INET, &dst.sin_addr.s_addr,addstring,sizeof(addstring)),ntohs(dst.sin_port));
//创建新的线程,让主线程负责监听,子线程负责收发信息
int ret2 = pthread_create(&thnum,NULL,calbak,&newfd);
if( ret2!= 0) {
printf("创建新线程失败\n");
return -1;
}
//设置线程分离属性,让子线程结束的时候自己回收资源,避免成为僵尸线程
pthread_detach(thnum);
}
//关闭监听描述符
close(sfd);
return 0;
}