TCP并发服务器(多进程)
#include <myhead.h>
#define IP "192.168.124.74"
#define PORT 6666
int fork_child(int newfd,struct sockaddr_in cin);
void re_fork(){
while(waitpid(-1,NULL,WNOHANG)>0);
}
int main(int argc, const char *argv[])
{
if(signal(17,re_fork)==SIG_ERR){
ERR_MSG("signal");
return -1;
}
//创建socket文件
int sfd=socket(AF_INET,SOCK_STREAM,0);
if(sfd<0){
ERR_MSG("socket");
return -1;
}
printf("sfd create success\n");
//绑定服务器的IP和端口号
struct sockaddr_in sin;
sin.sin_family=AF_INET;
sin.sin_port=htons(PORT);
sin.sin_addr.s_addr=inet_addr(IP);
if(bind(sfd,(struct sockaddr*)&sin,sizeof(sin))<0){
ERR_MSG("bind");
return -1;
}
printf("bind IP&PORT success\n");
//将文件转为被动监听模式
if(listen(sfd,128)<0){
ERR_MSG("listen");
return -1;
}
printf("listen success\n");
struct sockaddr_in cin;
socklen_t len=sizeof(cin);
int newfd =0;
int cpid=0;
while(1){
//用连接成功的客户端的信息生成accept文件
newfd=accept(sfd,(struct sockaddr*)&cin,&len);
if(newfd<0){
ERR_MSG("accept");
return -1;
}
cpid=fork(); //父进程创建newfd,子进程负责通信
if(cpid==0){
fork_child(newfd,cin);
exit(0);
}else if(cpid<0){
ERR_MSG("fork");
return -1;
}
//把客户端的IP和端口提出来
printf("newfd:%d %s:%d 连接成功\n",newfd\
,inet_ntoa(cin.sin_addr),ntohs(cin.sin_port));
close(newfd);//后面每个的进程文件号都是4,因为被父进程关了
}
//关闭所有文件描述符
close(sfd);
return 0;
}
int fork_child(int newfd,struct sockaddr_in cin)
{
char buf[128]="";
ssize_t res=0;
while(1){
bzero(buf,sizeof(buf));
//接收客户端数据
res=recv(newfd,&buf,sizeof(buf),0);
if(res<0){
ERR_MSG("recv");
break;
}else if(res==0){
printf("客户端下线\n");
break;
}
printf("[%s:%d]:%s\n",\
inet_ntoa(cin.sin_addr),\
ntohs(cin.sin_port),buf);
//向客户端发送数据
//要发送的数据
strcat(buf,"^-^");
res=send(newfd,buf,sizeof(buf),0);
if(res<0){
ERR_MSG("send");
return -1;
}
printf("send success\n");
}
return 0;
}
TCP并发服务器(多线程)
#include <myhead.h>
#define IP "192.168.124.74"
#define PORT 6666
struct data{
int newfd;
struct sockaddr_in cin;
};
void* pthread_child(void* arg);
int main(int argc, const char *argv[])
{
//创建socket文件
int sfd=socket(AF_INET,SOCK_STREAM,0);
if(sfd<0){
ERR_MSG("socket");
return -1;
}
printf("sfd create success\n");
//允许端口快速被复用---监测端口号是否真的有进程在占用,如果没有,则快速复用
int reuse = 1;
if(setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) < 0)
{
ERR_MSG("setsockopt");
return -1;
}
printf("允许端口快速复用成功\n");
//绑定服务器的IP和端口号
struct sockaddr_in sin;
sin.sin_family=AF_INET;
sin.sin_port=htons(PORT);
sin.sin_addr.s_addr=inet_addr(IP);
if(bind(sfd,(struct sockaddr*)&sin,sizeof(sin))<0){
ERR_MSG("bind");
return -1;
}
printf("bind IP&PORT success\n");
//将文件转为被动监听模式
if(listen(sfd,128)<0){
ERR_MSG("listen");
return -1;
}
printf("listen success\n");
struct sockaddr_in cin;
socklen_t len=sizeof(cin);
pthread_t tid;
struct data info;
while(1){
//用连接成功的客户端的信息生成accept文件
info.newfd=accept(sfd,(struct sockaddr*)&info.cin,&len);
if(info.newfd<0){
ERR_MSG("accept");
return -1;
}
//把客户端的IP和端口提出来
printf("newfd:%d %s:%d 连接成功\n",info.newfd\
,inet_ntoa(info.cin.sin_addr),ntohs(info.cin.sin_port));
//创建线程
if(pthread_create(&tid,NULL,pthread_child,(void*)&info)!=0){
printf("pthread_child create fail\n");
return -1;
}
//分离线程:join会阻塞,使用detach直接分离回收
if(pthread_detach(tid)!=0){
printf("pthread_detach fail\n");
return -1;
}
}
//关闭所有文件描述符
close(sfd);
return 0;
}
void* pthread_child(void* arg) //void* arg=&info
{
//必须转存,因为info每连接一个客户端就会改变
int newfd=((struct data*)arg)->newfd;
struct sockaddr_in cin=((struct data*)arg)->cin;
ssize_t res=0;
char buf[128]="";
while(1){
bzero(buf,sizeof(buf));
//接收客户端数据
res=recv(newfd,&buf,sizeof(buf),0);
if(res<0){
ERR_MSG("recv");
break;
}else if(res==0){
printf("客户端下线\n");
break;
}
printf("[%s:%d]:%s\n",\
inet_ntoa(cin.sin_addr),\
ntohs(cin.sin_port),buf);
//向客户端发送数据
//要发送的数据
strcat(buf,"^-^");
res=send(newfd,buf,sizeof(buf),0);
if(res<0){
ERR_MSG("send");
break;
}
printf("send success\n");
}
close(newfd);
}