1.线程资源的回收
一、服务器所创建的线程是分离式的,线程运行完毕(客户端挂了,断开连接)自动回收
二、客户端子线程不是分离式,需要手动回收,但是子线程进入了阻塞,无法自己跳出循环,如果ctrl+c结
束程序,子线程无法回收,自己采用的解决方法是:退出时置子线程循环开关为-1,再让服务器发送一个信号
过来打通recv函数的阻塞,使其无法进入下一次循环,然后子线程运行完毕结束,正常回收。不知道正
确的解决方法应该是怎样的
2.对线程子函数的传参
注意传入和传出是的类型装换
最多连三个客户端,服务器端至少要连两个客户端才能有群聊效果
客户端用户:chen 对应密码:123456
:xia :223456
:fang :323456
如果你看到了这篇博客,想要试一下效果注意编译时链接动态库gcc xxx -pthread
并且注意修改成自己的ip地址
会报两个警告!可以忽略
服务器端
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define Myport 8848 //端口号
#define Backlog 100 //
int sockfd=-1;
int recvret=-1;
char recvbuf[50]={0};
int acceptfd=-1;
int i=0;
int j=0;
char username[12]={0};
int flag=0 ;
int jionret=-1;
struct User
{
int opclo;
char name[20];
char passwd[6];
char name1[20];
int fd;
pthread_t th;
}user[3]={{-1,"chen","123456","chen123456",-1,-1},
{-1,"xia","223456","xia223456",-1,-1},
{-1,"fang","323456","fang323456",-1,-1}};
////////////////////////////////////////////////////////////////////////////////////
void sigfunc(int sig)
{
if(sig==SIGINT)
{
int k;
flag=-1;
for(k=0;k<3;k++)
{
if(user[k].opclo==1)
{
if((close(user[k].fd))!=0)
{
printf("关闭出错sig\n");
}
}
}
if(close(sockfd)==0)
{
printf("安全退出!\n");
exit(0);
}
}
}
/////////////////////////////////////////////////////////////////////////////////////
void* thfunc(void *arg)
{
int f=(int)arg;
char buff[400]={0};
while(flag==0)
{
size_t size;
if((size=recv(user[f].fd,buff,sizeof(buff),0))<0)
{
perror("recv error");
break;
}else if(size==0)
{
printf("%s已断开\n",user[f].name);
break;
}else{
char opensig[]={" "};
if((strcmp(buff,"signal_quit"))==0)
{
send(user[f].fd,opensig,strlen(opensig),0);
memset(opensig,0,sizeof(opensig));
break;
}
for(int i=0;i<3;i++)
{
if((user[i].opclo==1)&&(i!=f))
{
char sendbuff[512]={0};
memcpy(sendbuff,user[f].name,sizeof(user[f].name));
strcat(sendbuff," : "); strcat(sendbuff,buff);
if(send(user[i].fd,sendbuff,strlen(sendbuff),0)<0)
{
if(errno==ENOTCONN)
perror("send error");
break;
}
}
}
}
memset(buff,0,sizeof(buff));
}
if((close(user[f].fd))==0)
user[f].opclo=-1;
printf("子线程已退出\n");
pthread_exit(NULL);
return (void *)0;
}
///////////////////////////////////////////////////////////////////////////////////////
int main(int argc,char *argv[])
{
signal(SIGINT,sigfunc);
//1.创建套接字
//2.绑定端口和ip地址
//套接字返
int bindfd=-1;
int listenfd=-1; //监听函数返回值
char savedate[100]={0};
int a=-1;
sockfd=socket(AF_INET,SOCK_STREAM,0);
if(-1==sockfd)
{
perror("socket"); //返回值验证
close(sockfd);
return -1;
}
printf("sockfd=%d\n",sockfd);
// 设置套接字选项避免地址使用错误
int on=1;
if((setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on)))<0)
{
perror("setsockopt failed");
exit(EXIT_FAILURE);
}
/***********************************
/*上面部分为创建套接字*/
struct sockaddr_in svraddr={0}; //bind函数需要传入的ip端口结构体
svraddr.sin_family=AF_INET; //1 //ip类型 ipv4;
svraddr.sin_port=htons(Myport); //2 //端口号 需要转换成机器能识别的端口号 不能直接等于6003
svraddr.sin_addr.s_addr=INADDR_ANY; //3 //ip
bindfd=bind(sockfd,(const struct sockaddr *)&svraddr,sizeof(svraddr));
if(-1==bindfd)
{
perror("bind");
close(sockfd);
return -1;
}
printf("bind success.\n");
printf("bindfd=%d\n",bindfd);
//3.第三步:listen 监听端口
listenfd=listen(sockfd,Backlog);
if(-1==listenfd)
{
perror("listen");
close(sockfd);
return -1;
}
//4.第四步:accept服务器阻塞等待连接
struct sockaddr_in Acceptretaddr={0};
socklen_t lenth={0};
printf("等待客户端连接\n");
/********************************/
/*设置线程分离属性*/
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED);
/********************************/
//第五步:服务器接收数据
while(flag==0)
{
acceptfd=accept(sockfd,(struct sockaddr *)&Acceptretaddr,&lenth);
if(acceptfd<0)
{
perror("accept");
}
//连上了,检测用户和密码
memset(username,0,sizeof(username));
recvret=recv(acceptfd,username,sizeof(username),0);
int t=0;
for(j=0;j<3;j++)
{
int cm=-1;
cm=strcmp(username,user[j].name1);
if(cm==0)
{//用户密码都正确创建子线程进行通信
char information[]={"1"};
send(acceptfd,information,strlen(information),0);
memset(information,0,sizeof(information));
printf("%s进入\n",user[j].name);
user[j].opclo=1;
user[j].fd=acceptfd;
int err=-1;
err=pthread_create(&user[j].th,&attr,thfunc,(void*)j);//将用户的信息传给对应的线程
if(err!=0)
{
perror("pthread create error");
}
pthread_attr_destroy(&attr);//销毁分离属性
}else{t++;}
}
if(t==3)
{
char error_information[]={"0"};
send(acceptfd,error_information,sizeof(error_information),0);
close(acceptfd);
}
}
close(acceptfd);
close(sockfd);
return 0;
}
客户端
/*************************************************************************
> File Name: client.c
> Author: 客户端程序
> Mail:
> Created Time: 2018年04月07日 星期六 22时05分05秒
************************************************************************/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define SVERPNET "192.168.1.103"
//客户端不用定义自己的端口号,
//但是需要声明服务器开放给客户端的端口号。
#define SERVport 8848
int sendret=-1;
int recvret=-1;
int flag = 0;
char recvbuf[100]={0}; //收数据的数组
int setsocketret=-1;
int bindfd=-1;
int connectfd=-1;
int sendfd=-1;
char sendbuf[100]={0}; //发送数据的数组
struct sockaddr_in servaddr={0};
pthread_t th=-1; //线程标识号
int sockfd=-1;
int jionret=-1;
//显示登录////////////////////////////////////////////////////////////////////////////////
void Display()
{
char passwd[6]={0};
int b=-1;
char namebuf[12]={0}; //六位数用户账号
printf("用户名账号:");
scanf("%s",namebuf);
printf("用户密码:");
scanf("%s",passwd);
strcat(namebuf,passwd);
send(sockfd,namebuf,strlen(namebuf),0);
//等待登录结果
recvret=recv(sockfd,recvbuf,sizeof(recvbuf),0);
if((recvret==0)||(strcmp(recvbuf,"0"))==0)
{
printf("登录错误 !请重新登录\n");
close(sockfd);
exit(-1);
}
if((recvret>0)&&(strcmp(recvbuf,"1"))==0)
{
printf("成功登录\n");
}
}
///////////////////////////////////////////////////////////////////////////////////////////////
//终止处理函数
void sig1(int signum)
{
char quit[]={"signal_quit"};
if(SIGINT==signum)
{
flag=-1;
send(sockfd,quit,strlen(quit),0);
close(sockfd);
sleep(1);
jionret=pthread_join(th,NULL);
if(jionret!=0)
{
printf("子线程没有被回收!");
}
if(jionret==0)
{
printf("安全退出!\n");
}
exit(0);
}
}
//////////////////////////////////////////////////////////////////////////////////////////////
//子线程函数
void *func(void *arg)
{
while( flag==0 )
{
recvret=recv(sockfd,recvbuf,sizeof(recvbuf),0); //阻塞接收,
if(recvret>0)
{
printf("\t\t%s\n",recvbuf);
memset(recvbuf,0,sizeof(recvbuf));
}
if(recvret==0)
{
printf("连接断开\n");
break;
}
}
printf("子线程已退出\n");
pthread_exit(NULL);
}
////////////////////////////////////////////////////////////////////////////////////////////
int main(int argc,char *argv[])
{
signal(SIGINT,sig1);
//第一步:创建套接字
sockfd=socket(AF_INET,SOCK_STREAM,0);
if(-1==sockfd)
{
perror("socket");
return -1;
}
//第二步:connect 连接服务器
servaddr.sin_family=AF_INET;
servaddr.sin_port=htons(SERVport);
servaddr.sin_addr.s_addr=inet_addr(SVERPNET);
connectfd=connect(sockfd,(const struct sockaddr *)&servaddr,sizeof(servaddr));
if(-1==connectfd)
{
perror("connect");
return -1;
}
//显示登录
Display();
//子线程读
pthread_create(&th,NULL,func,NULL);
//主线程写
while(1)
{
scanf("%s",sendbuf);
send(sockfd,sendbuf,strlen(sendbuf),0);
memset(sendbuf,0,sizeof(sendbuf));
}
return 0;
}