文章系转载,便于整理和归纳,原文地址:https://blog.csdn.net/jelly_gemini/article/details/76736335
TCP 迭代服务器接受一个客户端的连接,然后处理,完成了这个客户的所有请求后,断开连接。TCP 迭代服务器一次只能处理一个客户端的请求,只有在这个客户的所有请求满足后,服务器才可以继续后面的请求。如果有一个客户端占住服务器不放时,其它的客户机都不能工作了,因此,TCP 服务器一般很少用迭代服务器模型的。
tcp服务器端框架
-
创建tcp套接字
-
绑定套接字
-
监听套接字
-
调用accept()阻塞等待
-
处理客户端的请求
-
关闭连接套接字
-
关闭监听套接字
tcp客户端框架
-
创建tcp套接字
-
调用connect()连接服务器
-
处理服务器端返回的信息
由于客户端不需要固定的端口号,因此不必调⽤bind(),客户端的端口号由内核⾃动分配。注意, 客户端不是不允许调⽤bind(),只是没有必要调⽤bind()固定⼀个端口号,服务器也不是必须调⽤bind(),但如果服务器不调⽤bind(),内核会⾃动给服务器分配监听端口,每次启动服务器时端口号都不⼀样,客户端要连接服务器就会遇到⿇烦。
单进程实现
server.c
#include<stdio.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<errno.h>
#include<unistd.h>
#include<string.h>
#include<sys/types.h>
#include<arpa/inet.h>
#include<netinet/in.h>
#define _PORT_ 9999
#define _BACKLOG_ 10
int main()
{
int sock=socket(AF_INET,SOCK_STREAM,0);
if(sock<0)
{
printf("socket()\n");
}
struct sockaddr_in server_socket;
struct sockaddr_in socket;
bzero(&server_socket,sizeof(server_socket));
server_socket.sin_family=AF_INET;
server_socket.sin_addr.s_addr=htonl(INADDR_ANY);
server_socket.sin_port=htons(_PORT_);
if(bind(sock,(struct sockaddr*)&server_socket,sizeof(struct sockaddr_in))<0)
{
printf("bind()\n");
close(sock);
return 1;
}
if(listen(sock,_BACKLOG_)<0)
{
printf("listen()\n");
close(sock);
return 2;
}
printf("success\n");
for(;;)
{
socklen_t len=0;
int client_sock=accept(sock,(struct sockaddr*)&socket,&len);
if(client_sock<0)
{
printf("accept()\n");
return 3;
}
char buf_ip[INET_ADDRSTRLEN];
memset(buf_ip,'\0',sizeof(buf_ip));
inet_ntop(AF_INET,&socket.sin_addr,buf_ip,sizeof(buf_ip));
printf("get connect\n");
while(1)
{
char buf[1024];
memset(buf,'\0',sizeof(buf));
read(client_sock,buf,sizeof(buf));
printf("client:# %s\n",buf);
printf("server:$ ");
memset(buf,'\0',sizeof(buf));
fgets(buf,sizeof(buf),stdin);
buf[strlen(buf)-1]='\0';
if(strncasecmp(buf,"quit",4)==0)
{
printf("quit\n");
break;
}
write(client_sock,buf,strlen(buf)+1);
printf("wait...\n");
}
close(client_sock);
}
close(sock);
return 0;
}
多进程实现
怎么将单进程的代码改为多进程的代码呢?
调用fork()函数,创建子进程,将所有的客户端的请求处理的内容都放在子进程中处理。
在 Linux 环境下多进程的应用很多,其中最主要的就是网络/客户服务器。多进程服务器是当客户有请求时,服务器用一个子进程来处理客户请求。父进程继续等待其它客户的请求。这种方法的优点是当客户有请求时,服务器能及时处理客户,特别是在客户服务器交互系统中。对于一个 TCP 服务器,客户与服务器的连接可能并不马上关闭,可能会等到客户提交某些数据后再关闭,这段时间服务器端的进程会阻塞,所以这时操作系统可能调度其它客户服务进程,这比起循环服务器大大提高了服务性能。
server.c
#include<stdio.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<errno.h>
#include<unistd.h>
#include<string.h>
#include<sys/types.h>
#include<arpa/inet.h>
#include<netinet/in.h>
#define _PORT_ 9999
#define _BACKLOG_ 10
int main()
{
int sock=socket(AF_INET,SOCK_STREAM,0);
if(sock<0)
{
printf("socket()\n");
}
struct sockaddr_in server_socket;
struct sockaddr_in socket;
bzero(&server_socket,sizeof(server_socket));
server_socket.sin_family=AF_INET;
server_socket.sin_addr.s_addr=htonl(INADDR_ANY);
server_socket.sin_port=htons(_PORT_);
if(bind(sock,(struct sockaddr*)&server_socket,sizeof(struct sockaddr_in))<0)
{
printf("bind()\n");
close(sock);
return 1;
}
if(listen(sock,_BACKLOG_)<0)
{
printf("listen()\n");
close(sock);
return 2;
}
printf("success\n");
for(;;)
{
socklen_t len=0;
int client_sock=accept(sock,(struct sockaddr*)&socket,&len);
if(client_sock<0)
{
printf("accept()\n");
return 3;
}
char buf_ip[INET_ADDRSTRLEN];
memset(buf_ip,'\0',sizeof(buf_ip));
inet_ntop(AF_INET,&socket.sin_addr,buf_ip,sizeof(buf_ip));
printf("get connect\n");
pid_t fd=fork();
if(fd<0)
printf("fork()\n");
if(fd==0)
{
close(sock);//关闭监听套接字
printf("port=%d,ip=%s\n",ntohs(socket.sin_port),buf_ip);
while(1)
{
char buf[1024];
memset(buf,'\0',sizeof(buf));
read(client_sock,buf,sizeof(buf));
printf("client:# %s\n",buf);
printf("server:$ ");
memset(buf,'\0',sizeof(buf));
fgets(buf,sizeof(buf),stdin);
buf[strlen(buf)-1]='\0';
if(strncasecmp(buf,"quit",4)==0)
{
printf("quit\n");
break;
}
write(client_sock,buf,strlen(buf)+1);
printf("wait...\n");
}
close(fd);
}
else if(fd>0)
{
close(fd);
}
}
close(sock);
return 0;
}
多线程实现
多线程和多进程的处理方式类似,都是创建一个新的线程,客户端有请求时,用新创建的线程处理。
#include<stdio.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<errno.h>
#include<unistd.h>
#include<string.h>
#include<sys/types.h>
#include<arpa/inet.h>
#include<netinet/in.h>
#define _PORT_ 9999
#define _BACKLOG_ 10
void *fun(void* arg)
{
int client_sock = (int)arg;
while(1)
{
char buf[1024];
memset(buf,'\0',sizeof(buf));
read(client_sock,buf,sizeof(buf));
printf("client:# %s\n",buf);
printf("server:$ ");
memset(buf,'\0',sizeof(buf));
fgets(buf,sizeof(buf),stdin);
buf[strlen(buf)-1]='\0';
if(strncasecmp(buf,"quit",4)==0)
{
printf("quit\n");
break;
}
write(client_sock,buf,strlen(buf)+1);
printf("wait...\n");
}
close(client_sock);
}
int main()
{
int sock=socket(AF_INET,SOCK_STREAM,0);
if(sock<0)
{
printf("socket()\n");
}
struct sockaddr_in server_socket;
struct sockaddr_in socket;
pthread_t thread_id;
bzero(&server_socket,sizeof(server_socket));
server_socket.sin_family=AF_INET;
server_socket.sin_addr.s_addr=htonl(INADDR_ANY);
server_socket.sin_port=htons(_PORT_);
if(bind(sock,(struct sockaddr*)&server_socket,sizeof(struct sockaddr_in))<0)
{
printf("bind()\n");
close(sock);
return 1;
}
if(listen(sock,_BACKLOG_)<0)
{
printf("listen()\n");
close(sock);
return 2;
}
printf("success\n");
for(;;)
{
socklen_t len=0;
int client_sock=accept(sock,(struct sockaddr*)&socket,&len);
if(client_sock<0)
{
printf("accept()\n");
return 3;
}
char buf_ip[INET_ADDRSTRLEN];
memset(buf_ip,'\0',sizeof(buf_ip));
inet_ntop(AF_INET,&socket.sin_add,buf_ip,sizeof(buf_ip));
printf("get connect,ip is%s\n",buf_ip);
printf("port=%d\n",ntohs(socket.sin_port));
pthread_create(&thread_id, NULL, (void *)fun, (void *)client_sock);
pthread_detach(thread_id);
}
close(sock);
return 0;
}
不管是哪一种的服务器,客户端都是一样的
client.c
#include<stdio.h>
#include<unistd.h>
#include<sys/socket.h>
#include<string.h>
#include<errno.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<sys/types.h>
#define SERVER_PORT 9999
int main(int argc,char* argv[])
{
if(argc!=2)
{
printf("Usage:client IP\n");
return 1;
}
char *str=argv[1];
char buf[1024];
memset(buf,'\0',sizeof(buf));
struct sockaddr_in server_sock;
int sock = socket(AF_INET,SOCK_STREAM,0);
bzero(&server_sock,sizeof(server_sock));
server_sock.sin_family=AF_INET;
inet_pton(AF_INET,str,&server_sock.sin_addr);
server_sock.sin_port=htons(SERVER_PORT);
int ret=connect(sock,(struct sockaddr *)&server_sock,sizeof(server_sock));
if(ret<0)
{
printf("connect()\n");
return 1;
}
printf("connect success\n");
while(1)
{
printf("client:# ");
fgets(buf,sizeof(buf),stdin);
buf[strlen(buf)-1]='\0';
write(sock,buf,sizeof(buf));
if(strncasecmp(buf,"quit",4)==0)
{
printf("quit\n");
break;
}
printf("wait..\n");
read(sock,buf,sizeof(buf));
printf("server:$ %s\n",buf);
}
close(sock);
return 0;
}