嵌入式 Linux网络编程(二)——TCP编程模型

嵌入式 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;
}
时间: 2024-10-09 22:09:38

嵌入式 Linux网络编程(二)——TCP编程模型的相关文章

嵌入式 Linux网络编程(三)——UDP编程模型

嵌入式 Linux网络编程(三)--UDP编程模型 UDP编程模型: UDP循环服务器模型为: socket(...); bind(...); while(1) {    recvfrom(...);    process(...);    sendto(...); } server.c代码: #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #inc

嵌入式 Linux网络编程(一)——Socket网络编程基础

嵌入式 Linux网络编程一--Socket网络编程基础 一.Socket简介 1.网络中进程间通信 本机进程使用进程号区别不同的进程进程间通信方式有管道.信号.消息队列.共享内存.信号量等.网络中进程间的通信首先需要识别进程所在主机在网络中的唯一标识即网络层的IP地址主机上的进程可以通过传输层的协议与端口号识别. 2.Socket原理 Socket是应用层与TCP/IP协议族通信的中间软件抽象层是一种编程接口.Socket屏蔽了不同网络协议的差异支持面向连接(Transmission Cont

嵌入式 Linux网络编程(四)——Select机制

嵌入式 Linux网络编程(四)--Select机制 一.select工作机制 poll和select,都是基于内核函数sys_poll实现的,不同在于在linux中select是从BSD Unix系统继承而来,poll则是从SYSTEM V Unix系统继承而来,因此两种方式相差不大.poll函数没有最大文件描述符数量的限制.poll和 select与一样,大量文件描述符的数组被整体复制于用户态和内核的地址空间之间,开销随着文件描述符数量的增加而线性增大. select需要驱动程序的支持,驱动

嵌入式 Linux网络编程(五)——epoll机制

嵌入式 Linux网络编程(五)--epoll机制 一.epoll简介 epoll是在2.6内核中提出的,是select和poll的增强版本.epoll更加灵活,没有描述符限制,使用一个文件描述符管理多个描述符,将用户关系的文件描述符的事件存放到内核的一个事件表中. 1.epoll函数 #include <sys/epoll.h> int epoll_create(int size); 创建一个epoll的句柄,size表示监听的文件描述的数量 int epoll_ctl(int epfd,

linux网络编程之TCP/IP基础篇(一)

从今天起,将会接触到网络编程,平台是linux,实现语言C语言,最后将会实现一个简易的miniftp服务器. 主要的内容安排为:linux网络编程之TCP/IP基础篇,SOCKET编程篇,进程间通信篇,线程篇,实战ftp篇. 1.ISO/OSI参考模型:open system interconnection开放系统互联模型是由OSI(international organization for standardization )国际标准化组织定义的网络分层模型,共七层. 各层的具体含义: 物理层

嵌入式 Linux进程间通信(二)——exec族函数

嵌入式 Linux进程间通信(二)--exec族函数 exec函数族的作用是根据指定的文件名找到可执行文件,并用它来取代调用进程的内容,换句话说,就是在调用进程内部执行一个可执行文件.这里的可执行文件既可以是二进制文件,也可以是任何Linux下可执行的脚本文件. exec族函数包含如下函数: #include <unistd.h> extern char **environ; int execl(const char *path, const char *arg, ...); int exec

网络编程之TCP编程

html,body,div,span,applet,object,iframe,h1,h2,h3,h4,h5,h6,p,blockquote,pre,a,abbr,acronym,address,big,cite,code,del,dfn,em,img,ins,kbd,q,s,samp,small,strike,strong,sub,sup,tt,var,b,u,i,center,dl,dt,dd,ol,ul,li,fieldset,form,label,legend,table,caption

python网络编程之TCP编程

TCP编程 模拟客户端和服务器端进行通信.其中要素为ip地址和端口. 客户端: 建立socket对象,并且设置为TCP模式 用connect()方法设置端口和ip地址,需要传入一个set. 可以进行接受和发送的操作 关闭socket连接 服务器端: 因为一个服务器打开一个固定端口进行监听,但是可能响应不同的客户端,所以可以定义不同的进程序进行处理. 建立socket对象 绑定IP地址和端口 监听 利用进程响应不同的客户端请求,可以发送和接受数据等操作 关闭socket连接 实例: 客户端: im

嵌入式linux网络配置

在开发阶段需要用tftp等开发工具,这时就要配置Linux网络,首先确保windows网络IP地址为固定IP, 1.假设windows IP地址为19.168.2.10子网掩码:255.255.255.0默认网关:192.168.2.1DNS:202.96.128.86 2.虚拟机网络的配置 虚拟机右上方选项点击network conections后有对话框 点击编辑,选择IPv4 Setting,进行设置 其中ip地址与Windows IP在同一网段,完成设置后save. 3.配置Linux网