TCP并发服务器的注意点: TCP服务器、提取多个客户端、开启进程或线程处理每个客户端 1、多线程(常用)
#include<stdio.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<string.h>
#include<pthread.h>
void* deal_client_fun(void *arg)
{
int fd = *(int *)arg;
while(1)
{
char buf[128]="";
int len = recv(fd,buf,sizeof(buf), 0);
if(len == 0)
break;
send(fd, buf, len, 0);
}
close(fd);
}
int main()
{
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
if(sockfd < 0)
{
perror("socket");
}
int yes = 1;
setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &yes,sizeof(yes));
struct sockaddr_in my_addr;
bzero(&my_addr,sizeof(my_addr));
my_addr.sin_family = AF_INET;
my_addr.sin_port = htons(8000);
my_addr.sin_addr.s_addr = htonl(INADDR_ANY);
int ret = bind(sockfd, (struct sockaddr *)&my_addr,sizeof(my_addr));
if(ret == -1)
{
perror("bind");
}
listen(sockfd, 10);
while(1)
{
struct sockaddr_in cli_addr;
socklen_t cli_len = sizeof(cli_addr);
int new_fd = accept(sockfd,(struct sockaddr *)&cli_addr , &cli_len);
unsigned short port=ntohs(cli_addr.sin_port);
char ip[16]="";
inet_ntop(AF_INET,&cli_addr.sin_addr.s_addr, ip, 16);
printf("已有客户端:%s:%hu连接上了服务器\n", ip, port);
pthread_t tid;
pthread_create(&tid,NULL, deal_client_fun, (void *)&new_fd);
pthread_detach(tid);
}
close(sockfd);
return 0;
}
运行结果: 上述代码 如果客户端 正常退出 不会有啥影响,但是如果服务器 意外退出 绑定的端口信息来不及释放,就会造成 系统临时占用服务器上次bind的端口,如果在5~6分钟之内再次运行服务器 ,会导致新运行的服务器 bind失败
2、解决上述问题:端口复用 仅仅是端口的复用 服务器的进程网络资源 任然被占用 一般1分钟作用释放
int yes = 1;
setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &yes,sizeof(yes));
将上面的两句话添加到socket只有 bind函数之前
3、并发服务器 多进程实现
#include<stdio.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<string.h>
#include<pthread.h>
#include<unistd.h>
void deal_client_fun(int fd)
{
while(1)
{
char buf[128]="";
int len = recv(fd,buf,sizeof(buf), 0);
if(len == 0)
break;
send(fd, buf, len, 0);
}
return;
}
int main()
{
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
if(sockfd < 0)
{
perror("socket");
}
int yes = 1;
setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &yes,sizeof(yes));
struct sockaddr_in my_addr;
bzero(&my_addr,sizeof(my_addr));
my_addr.sin_family = AF_INET;
my_addr.sin_port = htons(8000);
my_addr.sin_addr.s_addr = htonl(INADDR_ANY);
int ret = bind(sockfd, (struct sockaddr *)&my_addr,sizeof(my_addr));
if(ret == -1)
{
perror("bind");
}
listen(sockfd, 10);
while(1)
{
struct sockaddr_in cli_addr;
socklen_t cli_len = sizeof(cli_addr);
int new_fd = accept(sockfd,(struct sockaddr *)&cli_addr , &cli_len);
unsigned short port=ntohs(cli_addr.sin_port);
char ip[16]="";
inet_ntop(AF_INET,&cli_addr.sin_addr.s_addr, ip, 16);
printf("已有客户端:%s:%hu连接上了服务器\n", ip, port);
pid_t pid;
if(fork() == 0)
{
close(sockfd);
deal_client_fun(new_fd);
close(new_fd);
_exit(-1);
}
else
{
close(new_fd);
}
}
close(sockfd);
return 0;
}
运行结果: 总结: TCP并发服务器 进程版:父子进程 资源独立 某个进程结束 不会影响已有的进程 服务器更加稳定 代价多进程 会消耗很多资源。 TCP并发服务器 线程版:线程共享进程资源 资源开销小 但是一旦主进程结束 所有线程都会结束 服务器先对进程 不是那么稳定
|