一、为什么TCP不能实现并发,而UDP可以
因为TCP服务器端有两个读阻塞函数,accept和recv,两个函数需要先后运行,所以导致运行一个函数的时候另一个函数无法执行,所以无法保证一边连接客户端,一边与其他客户端通信。
而UDP只有一个阻塞函数recvfrom,并且无需连接,因此,可以实现并发。
二、如何实现TCP并发服务器
2.1、常用的两种方式
1、使用多进程实现TCP并发服务器
2、使用多线程实现TCP并发服务器
三、使用多进程实现TCP并发服务器
3.1、使用多进程实现TCP并发服务器简单流程
int sockfd = socket() 创建套接字
bind() 与套接字绑定自己的信息
listen() 将套接字设置为被动监听状态,并设置同时可以连接多少台客户端
while(1) { 循环等待,客户连接
acceptfd = accept() 接收到客户端的sockfd ,并返回一个专属客户端acceptfd标识符
pid = fork(); 创建父子进程
if(pid > 0) 大于0为父进程
{ }
else if(pid == 0) 等于0为子进程
{
while(1) 与专属的客户端通信
{
recv()/send() 接与收
}}}
弊端:进程结束,没有回收资源,造成资源浪费,解决方法,使用signal信号函数回收子进程。
如有不懂,请查看signal信号的章节学习。
3.2、使用多进程实现TCP并发服务器代码实现
3.3、服务器代码
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <signal.h>
#include <sys/wait.h>
#include <unistd.h>
#define N 128
void handler()
{
wait(NULL);
}
int main(int argc , char *argv[])
{
if(argc<3)
{
printf("fail : lose ip port\n");
exit(1);
}
int sockfd;
signal(SIGCHLD,handler);
if((sockfd=socket(AF_INET,SOCK_STREAM,0))==-1)
{
perror("fail to sockfd");
exit(1);
}
struct sockaddr_in mysockaddr;
mysockaddr.sin_family=AF_INET;
mysockaddr.sin_port = htons(atoi(argv[2]));
mysockaddr.sin_addr.s_addr =inet_addr(argv[1]);
socklen_t addrlen =sizeof(mysockaddr);
if(bind(sockfd,(struct sockaddr *)&mysockaddr,addrlen)==-1)
{
perror("fail to bind");
exit(1);
}
if(listen(sockfd,5)==-1)
{
perror("fail to listen");
exit(1);
}
struct sockaddr_in acceptaddr;
socklen_t acceptaddrlen=sizeof(acceptaddr);
pid_t pid;
int acceptfd;
while(1)
{
if((acceptfd=accept(sockfd,(struct sockaddr*)&acceptaddr,&acceptaddrlen))==-1)
{
perror("fail to acceptfd");
exit(1);
}
printf("reciver from ip = %s ,port:%d\n",inet_ntoa(acceptaddr.sin_addr),ntohs(acceptaddr.sin_port));
pid=fork();
if(pid<0)
{
perror("fail to fork");
exit(1);
}
else if(pid>0)
{
}
else
{
ssize_t byte;
while(1)
{
char text[N]="";
char buf[N]="";
if((byte=recv(acceptfd,buf,N,0))==-1)
{
perror("fail to recv");
exit(1);
}
else if(byte==0)
{
printf("the client quit\n");
exit(1);
}
printf("from client :%s\n",buf);
strcat(text,"^_^") ;
if(send(acceptfd,text,N,0)==-1)
{
perror("fail to ssend");
exit(1);
}
}
}
}
return 0;
}
3.4、 客户端代码
#include<stdio.h>
#include<stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include<string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#define N 128
int main(int argc ,char *argv[])
{
if(argc<3)
{
printf("fail ,lose ip port\n");
exit(1);
}
int sockfd;
if((sockfd=socket(AF_INET,SOCK_STREAM,0))==-1)
{
perror("fail to sockfd");
exit(1);
}
// printf("TCP:sockfd= %d\n",sockfd);
struct sockaddr_in mysockaddr;
mysockaddr.sin_family=AF_INET;
mysockaddr.sin_port=htons(atoi(argv[2]));
mysockaddr.sin_addr.s_addr=inet_addr(argv[1]);
socklen_t addrlen= sizeof(mysockaddr);
if(connect(sockfd,(struct sockaddr*)&mysockaddr,addrlen)==-1)
{
perror("fail to connect");
exit(1);
}
char buf[N]="";
char text[N]="";
while(1)
{
fgets(buf,N,stdin);
if(send(sockfd,buf,N,0)==-1)
{
perror("fail to send");
exit(1);
}
if(recv(sockfd,text,N,0)==-1)
{
perror("fail to recv");
exit(1);
}
printf("from server: %s\n",text);
}
close(sockfd);
return 0;
}
3.5、运行结果
四、使用多线程实现TCP并发服务器
4.1、多线程实现TCP并发服务器简单流程
typedef struct { //定义一个结构体
struct sockaddr_in addr; //用来传给子线程
int acceptfd;
} MSG;
void *thread_fun(void *arg)
{MSG msg = *(MSG *)arg; //客户端的信息,传给子线程
while(1)
{
recv() / send() // 接与收
} }
sockfd = socket() //创建套接字
bind() //绑定套接字与自己的信息
listen() // 将套接字设置为被动监听状态,并设置同时可以连接多少台客户端
while(1) //循环等待 客户端连接
{
accept() //创建专属对应客户端的acceptfd标识符
//只要有客户端连接上,则创建一个子线程与之通信
pthread_create(, , thread_fun, );
pthread_detach(); //分离模式,自动回收资源,不等待
}
4.2、服务器代码
#include<stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <pthread.h>
#include<string.h>
#include<unistd.h>
#define N 128
typedef struct
{
struct sockaddr_in addr;
int acceptfd ;
}MSG;
void * pthread_fun(void *arg)
{
char buf[N]="";
ssize_t size ;
MSG msg = *(MSG *)arg;
while(1)
{
char text[N]="";
if((size=recv(msg.acceptfd,buf,N,0))==-1)
{
perror("fail to recv");
pthread_exit(NULL);
}
else if(size ==0)
{
perror("quit client");
pthread_exit(NULL);
}
printf("[%s ‐ %d]: %s\n", inet_ntoa(msg.addr.sin_addr), ntohs(msg.addr.sin_port), buf);
strcat(text," ^_^");
if(send(msg.acceptfd,text,N,0)==-1)
{
perror("fail to send");
exit(1);
}
}
}
int main(int argc ,char *argv[])
{
if(argc <3)
{
printf("fail ; lose ip port \n");
exit(1);
}
int sockfd;
if((sockfd=socket(AF_INET,SOCK_STREAM,0))==-1)
{
perror("fail to socket");
exit(1);
}
int on=1;
if(setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on))<0)
{
perror("fail to setsockopt");
exit(1);
}
struct sockaddr_in mysockaddr;
struct sockaddr_in clientaddr;
mysockaddr.sin_family=AF_INET;
mysockaddr.sin_port = htons(atoi(argv[2]));
mysockaddr.sin_addr.s_addr=inet_addr(argv[1]);
socklen_t socklen = sizeof(mysockaddr);
socklen_t addrlen = sizeof(clientaddr);
if(bind(sockfd,(struct sockaddr *)&mysockaddr,socklen)==-1)
{
perror("fail to bind");
exit(1);
}
if(listen(sockfd,5)==-1)
{
perror("fail to listen");
exit(1);
}
int acceptfd;
while(1)
{
if((acceptfd=accept(sockfd,(struct sockaddr *)&clientaddr,&addrlen))==-1)
{
perror("fail to accept");
exit(1);
}
MSG msg;
msg.addr = clientaddr;
msg.acceptfd=acceptfd;
pthread_t thread;
if(pthread_create(&thread,NULL,pthread_fun,&msg)!=0)
{
perror("fail to pthread_create");
exit(1);
}
pthread_detach(thread);
}
return 0;
}
4.3、客户端代码
#include<stdio.h>
#include<stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include<string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#define N 128
int main(int argc ,char *argv[])
{
if(argc<3)
{
printf("fail ,lose ip port\n");
exit(1);
}
int sockfd;
if((sockfd=socket(AF_INET,SOCK_STREAM,0))==-1)
{
perror("fail to sockfd");
exit(1);
}
// printf("TCP:sockfd= %d\n",sockfd);
struct sockaddr_in mysockaddr;
mysockaddr.sin_family=AF_INET;
mysockaddr.sin_port=htons(atoi(argv[2]));
mysockaddr.sin_addr.s_addr=inet_addr(argv[1]);
socklen_t addrlen= sizeof(mysockaddr);
if(connect(sockfd,(struct sockaddr*)&mysockaddr,addrlen)==-1)
{
perror("fail to connect");
exit(1);
}
char buf[N]="";
char text[N]="";
while(1)
{
fgets(buf,N,stdin);
if(send(sockfd,buf,N,0)==-1)
{
perror("fail to send");
exit(1);
}
if(recv(sockfd,text,N,0)==-1)
{
perror("fail to recv");
exit(1);
}
printf("from server: %s\n",text);
}
close(sockfd);
return 0;
}
4.4、运行结果
你学会了??