嵌入式 Linux网络编程(二)――TCP编程模型
一、TCP编程模型
TCP编程的一般模型如下图:
TCP编程模型分为客户端和服务器端编程,两者编程流程如下:
TCP服务器端编程流程:
A、创建套接字;
B、绑定套接字;
C、设置套接字为监听模式,进入被动接受连接状态;
D、接受请求,建立连接;
E、读写数据;
F、终止连接。
TCP客户端编程流程:
A、创建套接字;
B、与远程服务器建立连接;
C、读写数据;
D、终止连接。
二、TCP迭代服务器编程模型
TCP循环服务器接受一个客户端的连接,然后处理,完成了客户端的所有请求后,断开连接。TCP循环服务器一次只能处理一个客户端的请求,只有在完成这个客户的所有请求病断开这个客户端后,服务器才可以继续后面的请求。如果有一个客户端占住服务器不放时,其它的客户端都不能工作了。
TCP循环服务器模型为:
socket(...);bind(...);
listen(...);
while(1)
{
accept(...);
process(...);
close(...);
}
代码实例:
#include <sys/types.h>#include <sys/socket.h>#include <stdio.h>#include <netinet/in.h>#include <arpa/inet.h>#include <unistd.h>#include <string.h>#include <stdlib.h>#include <fcntl.h>#include <sys/shm.h> #define PORT 8888#define LISTEN_QUEUE 10#define BUFFER_SIZE 1024 int main(){ ///定义sockfd int listenfd = socket(AF_INET,SOCK_STREAM, 0); ///定义sockaddr_in struct sockaddr_in server_sockaddr; bzero(&server_sockaddr, sizeof(server_sockaddr)); server_sockaddr.sin_family = AF_INET; server_sockaddr.sin_port = htons(PORT); server_sockaddr.sin_addr.s_addr = htonl(INADDR_ANY); ///bind,成功返回0,出错返回-1 if(bind(listenfd, (struct sockaddr *)&server_sockaddr, sizeof(server_sockaddr))==-1) { fprintf(stderr, "bind function failed.\n"); exit(-1); } ///listen,成功返回0,出错返回-1 if(listen(listenfd,LISTEN_QUEUE) == -1) { fprintf(stderr, "listen function failed.\n"); exit(-1); } fprintf(stdout, "listening on %d\n", PORT); ///客户端套接字 char recvbuf[BUFFER_SIZE]; char sendbuf[BUFFER_SIZE]; struct sockaddr_in client_addr; socklen_t length = sizeof(client_addr); bzero(&client_addr, sizeof(client_addr)); while(1) { bzero(recvbuf, sizeof(recvbuf)); bzero(sendbuf, sizeof(sendbuf)); ///成功返回非负描述字,出错返回-1 int connsockfd = accept(listenfd, (struct sockaddr*)&client_addr, &length); if(connsockfd<0) { fprintf(stderr, "connect function failed.\n"); exit(-1); } int len = recvfrom(connsockfd, recvbuf, sizeof(recvbuf), 0, (struct sockaddr *)&client_addr, &len); if(strcmp(recvbuf,"exit\n")==0) break; if(strcmp(recvbuf,"q\n")==0) break; if(strcmp(recvbuf,"quit\n")==0) break; fprintf(stdout, "have a new client:%s\n", inet_ntoa(client_addr.sin_addr)); fprintf(stdout, "message: %s\n", recvbuf); strcpy(sendbuf, recvbuf); send(connsockfd, sendbuf, len, 0); close(connsockfd); } close(listenfd); return 0;}
三、TCP并发服务器编程模型
1、TCP多进程并发服务器
TCP多进程并发服务器的思想是每一个客户机的请求并不由服务器直接处理,而是由服务器创建一个子进程来处理。
TCP多进程并发服务器:
socket(...);bind(...);listen(...);while(1){ accpet(...); if(fork(...) == 0) { process(...); close(...); exit(...); } close(...);}
代码:
#include <sys/types.h>#include <sys/socket.h>#include <stdio.h>#include <netinet/in.h>#include <arpa/inet.h>#include <unistd.h>#include <string.h>#include <stdlib.h>#include <fcntl.h>#include <sys/shm.h> #define PORT 8888#define LISTEN_QUEUE 10#define BUFFER_SIZE 1024 int main(){ ///定义sockfd int listenfd = socket(AF_INET,SOCK_STREAM, 0); ///定义sockaddr_in struct sockaddr_in server_sockaddr; bzero(&server_sockaddr, sizeof(server_sockaddr)); server_sockaddr.sin_family = AF_INET; server_sockaddr.sin_port = htons(PORT); server_sockaddr.sin_addr.s_addr = htonl(INADDR_ANY); ///bind,成功返回0,出错返回-1 if(bind(listenfd, (struct sockaddr *)&server_sockaddr, sizeof(server_sockaddr))==-1) { fprintf(stderr, "bind function failed.\n"); exit(-1); } ///listen,成功返回0,出错返回-1 if(listen(listenfd,LISTEN_QUEUE) == -1) { fprintf(stderr, "listen function failed.\n"); exit(-1); } fprintf(stdout, "listening on %d\n", PORT); ///客户端套接字 struct sockaddr_in client_addr; socklen_t length = sizeof(client_addr); bzero(&client_addr, sizeof(client_addr)); ///成功返回非负描述字,出错返回-1 while(1) { int connsockfd = accept(listenfd, (struct sockaddr*)&client_addr, &length); if(connsockfd<0) { fprintf(stderr, "connect function failed.\n"); exit(-1); } pid_t pid = fork(); if(pid == 0) { close(listenfd);//关闭从父进程继承的监听套接字 char recvbuf[BUFFER_SIZE]; char sendbuf[BUFFER_SIZE]; bzero(recvbuf, sizeof(recvbuf)); bzero(sendbuf, sizeof(sendbuf)); int len; while((len = recv(connsockfd, recvbuf, sizeof(recvbuf), 0)) > 0) { if(strcmp(recvbuf,"exit\n")==0 || strcmp(recvbuf,"q\n")==0 || strcmp(recvbuf,"quit\n")==0) break; else { fprintf(stdout, "have a new client:%s port: %d\n", inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port)); fprintf(stdout, "message: %s\n", recvbuf); strcpy(sendbuf, recvbuf); send(connsockfd, sendbuf, len, 0); } } close(connsockfd); fprintf(stdout, "client %s close.\n", inet_ntoa(client_addr.sin_addr)); exit(0); } else if(pid > 0) { close(connsockfd); } else { fprintf(stderr, "fork function failed.\n"); exit(-1); } } close(listenfd); return 0;}
2、TCP多线程并发服务器
多线程服务器是对多进程的服务器的改进 ,由于多进程服务器在创建进程时要消耗较大的系统资源 ,所以用线程来取代进程 ,这样服务处理程序可以较快的创建。据统计 ,创建线程与创建进程要快10――100倍,所以又把线程称为“轻量级”进程。线程与进程不同的是:一个进程内的所有线程共享相同的全局内存、全局变量等信息。多线程需要解决线程的同步问题。
TCP多线程服务器模板:
socket(...);bind(...);listen(...);while(1){ accpet(...); if((pthread_create(...))!==-1) { process(...); close(...); exit(...); } close(...);}
代码:
#include <sys/types.h>#include <sys/socket.h>#include <stdio.h>#include <netinet/in.h>#include <arpa/inet.h>#include <unistd.h>#include <string.h>#include <stdlib.h>#include <fcntl.h>#include <sys/shm.h>#include <pthread.h> #define PORT 8888#define LISTEN_QUEUE 10#define BUFFER_SIZE 1024 void *client_process(void *arg) { int recvlen = 0; char recvbuf[BUFFER_SIZE]; bzero(recvbuf, sizeof(recvbuf)); char sendbuf[BUFFER_SIZE]; bzero(sendbuf, sizeof(sendbuf)); int connfd = (int )arg; // 已连接套接字 // 接收数据 while((recvlen = recv(connfd, recvbuf, sizeof(recvbuf), 0)) > 0) { if(strcmp(recvbuf,"exit\n")==0 || strcmp(recvbuf,"q\n")==0 || strcmp(recvbuf,"quit\n")==0) break; else { fprintf(stdout, "message: %s\n", recvbuf); strcpy(sendbuf, recvbuf); send(connfd, sendbuf, sizeof(sendbuf), 0); } } printf("client closed!\n"); close(connfd); //关闭已连接套接字 return NULL; } int main(){ ///定义sockfd int listenfd = socket(AF_INET,SOCK_STREAM, 0); ///定义sockaddr_in struct sockaddr_in server_sockaddr; bzero(&server_sockaddr, sizeof(server_sockaddr)); server_sockaddr.sin_family = AF_INET; server_sockaddr.sin_port = htons(PORT); server_sockaddr.sin_addr.s_addr = htonl(INADDR_ANY); ///bind,成功返回0,出错返回-1 if(bind(listenfd, (struct sockaddr *)&server_sockaddr, sizeof(server_sockaddr))==-1) { fprintf(stderr, "bind function failed.\n"); exit(-1); } ///listen,成功返回0,出错返回-1 if(listen(listenfd,LISTEN_QUEUE) == -1) { fprintf(stderr, "listen function failed.\n"); exit(-1); } fprintf(stdout, "listening on %d\n", PORT); ///客户端套接字 struct sockaddr_in client_addr; socklen_t length = sizeof(client_addr); bzero(&client_addr, sizeof(client_addr)); ///成功返回非负描述字,出错返回-1 pthread_t thread_id; while(1) { int connsockfd = accept(listenfd, (struct sockaddr*)&client_addr, &length); if(connsockfd<0) { fprintf(stderr, "connect function failed.\n"); exit(-1); } fprintf(stdout, "have a new client:%s port: %d\n", inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port)); pthread_create(&thread_id, NULL, (void *)client_process, (void *)connsockfd); //创建线程 pthread_detach(thread_id); // 线程分离,结束时自动回收资源 } close(listenfd); return 0;}
3、TCP IO复用服务器模型
I/O复用技术是为了解决进程或线程阻塞到某个 I/ O系统调用而出现的技术 ,使进程不阻塞于某个特定的I/ O系统调用,也可用于并发服务器的设计,常用函数select 或 poll来实现。
socket(...); // 创建套接字bind(...); // 绑定listen(...); // 监听while(1){ if(select(...) > 0) // 检测监听套接字是否可读 { if(FD_ISSET(...)>0) // 套接字可读,证明有新客户端连接服务器 { accpet(...);// 取出已经完成的连接 process(...);// 处理请求,反馈结果 } } close(...); // 关闭连接套接字:accept()返回的套接字}
关于IO复用将在后续详细介绍
4、TCP客户端编程模型
socket(...);connect(...);process(...);close(...);
代码:
#include <sys/types.h>#include <sys/socket.h>#include <stdio.h>#include <netinet/in.h>#include <arpa/inet.h>#include <unistd.h>#include <string.h>#include <stdlib.h>#include <fcntl.h>#include <sys/shm.h> #define PORT 8888#define BUFFER_SIZE 1024 int main(){ ///定义sockfd int clientsockfd = socket(AF_INET, SOCK_STREAM, 0); ///定义sockaddr_in struct sockaddr_in servaddr; bzero(&servaddr, sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_port = htons(PORT); ///服务器端口 servaddr.sin_addr.s_addr = inet_addr("192.168.0.200"); ///服务器ip ///连接服务器,成功返回0,错误返回-1 if (connect(clientsockfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) < 0) { fprintf(stderr, "connect function failed.\n"); exit(-1); } char sendbuf[BUFFER_SIZE]; char recvbuf[BUFFER_SIZE]; bzero(sendbuf, sizeof(sendbuf)); bzero(recvbuf, sizeof(recvbuf)); while (1) { fgets(sendbuf, sizeof(sendbuf), stdin); send(clientsockfd, sendbuf, strlen(sendbuf),0); ///发送 if(strcmp(sendbuf,"exit\n")==0) break; if(strcmp(sendbuf,"q\n")==0) break; if(strcmp(sendbuf,"quit\n")==0) break; recv(clientsockfd, recvbuf, sizeof(recvbuf),0); ///接收 fprintf(stdout, "%s\n", recvbuf); bzero(sendbuf, sizeof(sendbuf)); bzero(recvbuf, sizeof(recvbuf)); } close(clientsockfd); return 0;}
本文出自 “生命不息,奋斗不止” 博客,转载请与作者联系!