Linux 网络 tcp C/S通信模型

C/S模型就是server 与 client 的模型

TCP服务器模型流程图                                                               TCP 客户端模型流程图:

                

  函数使用:

(1)创建一个网络通信套接字描述符  int socket(int domain, int type, int protocol);

  参数:domain : 协议系列,常用的是 AF_INET 表示IPV4

        type : 常用的两个

        SOCK_STREAM    流式套接字 TCP 常用

        SOCK_DGRAM  数据报套接字 UDP 常用

    

    protocol:一般为0

    

  返回值:通信过程中使用的socket(一个文件描述符)

(2)把socket与 ip, 端口绑定一起     int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

  参数 :sockfd :通过socket 函数创建返回的通信文件描述符

       addr:addr 是指向 sockaddr_in 结构的指针,包含本机IP 地址和端口号协议类型等

      addrlen: addrLen : sizeof (struct sockaddr_in) 是第二个参数addr结构所占的有效长度

struct sockaddr结构 和 struct sockaddr_in结构

struct sockaddr {
  sa_family_t sa_family; //协议族
  char sa_data[14];  // 14字节协议地址
}

struct sockaddr_in{
  u_short sin_family;// 地址族, AF_INET,2 bytes
  u_short sin_port; // 端口,2 bytes
  struct in_addr sin_addr; // IPV4地址结构,4 bytes
  char sin_zero[8]; // 8 bytes unused,作为填充
};

在bind时使用 struct sockaddr_in 结构进行初始化,然后通过 struct sockaddr 强制类型转换

sockaddr_in 与 sockaddr 结构的区别

(3) 监听绑定了ip和port的socket :int listen(int sockfd, int backlog);

  参数:sockfd:监听连接的套接字

     backlog 指定了完全成功连接的最大队列长度,它的作用在于处理可能同时出现的几个连接请求。此参数现在基本已经不用。

  返回值: 0 或 -1

完成listen()调用后,socket变成了监听

(4)阻塞等待连接 :int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

  参数: sockfd : 监听套接字

      addr : 对方地址

     addrlen:地址长度

  返回值:已建立好连接的套接字 或 -1

(5) 发送数据

  ssize_t send(int sockfd, const void *buf, size_t len, int flags);

  参数: sockfd :向哪个socket发送数据

     buf :存放数据缓存首地址

     len :发送的数据长度

     flags:发送方式(通常为0)

  返回值:成功:实际发送的字节数     失败:-1, 并设置errno

(6) 接收数据

  ssize_t recv(int sockfd, void *buf, size_t len, int flags);

  参数:socket:向哪个socket发送数据

      buf : 发送缓冲区首地址

      length : 发送的字节数

      flags : 接收方式(通常为0)

  返回值: 成功:实际接收的字节数      失败:-1, 并设置errno

(7) 客户端连接服务器的函数

  int connect(int  sockfd,  const struct sockaddr   *addr,  socklen_t  addrlen);

  参数:sockfd : socket返回的文件描述符

     serv_addr : 服务器端的地址信息

     addrlen : serv_addr的长度

  返回值:0 或 -1

(8) 关闭通信socket    int close(int fd);

 编写TCP 的server和client 通信程序,服务器监听接收数据

 server.c文件:

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <errno.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <strings.h>

#define MAX_BUF          1024
int main(int argc, const char *argv[])
{
    char buf[MAX_BUF] = {0};//接收数据缓存
    int server_sock_fd; //服务器,socket描述符
    int client_sock_fd; //链接的客户端,socket 描述符
    ssize_t tybes;

    struct sockaddr_in server_addr;//存放服务器ip和端口
    struct sockaddr_in client_addr;//存放客户端ip和端口
    socklen_t len; 

    len = sizeof(server_addr);
    bzero(&server_addr,len);//清零
    server_sock_fd = socket(AF_INET, SOCK_STREAM,0);//创建socket,返回socket描述符
    if(server_sock_fd < 0)
    {
        perror("socket fail ");
        exit(1);
    }

    //填充 地址和端口结构
    server_addr.sin_family = AF_INET; //协议族
//    server_addr.sin_port = htons(atoi(argv[2])); //服务器端口    //这两行是通过执行程序时传入参数ip和port
//    server_addr.sin_addr.s_addr = inet_addr(argv[1]);//IP地址
    server_addr.sin_port = htons(atoi("8080")); //服务器端口 , htons 把主机字节序转为网络字节序,atoi 把字符数据转为整数
    server_addr.sin_addr.s_addr = inet_addr("192.168.3.132");//IP地址 ,inet_addr把字符串ip转为网络字节序整数
    if(bind(server_sock_fd, (struct sockaddr*)&server_addr, len) < 0) //绑定服务器 ip和port
    {
        perror("fail bind ");
        exit(1);
    }

    if( listen(server_sock_fd,10) < 0 ) //监听 服务器
    {
        perror("fail listen ");
        exit(1);
    }

    while(1)
    {
        client_sock_fd = accept(server_sock_fd,(struct sockaddr*)&client_addr,&len);//阻塞等待客户端链接
        if(client_sock_fd < 0)
        {
            perror("client socket fail ");
            exit(1);
        }
        printf("client ip %s   port %u\n",inet_ntoa(client_addr.sin_addr),ntohs(client_addr.sin_port)); //打印链接的ip 和 port
        while(1)
        {
            memset(buf,0,1024);
        //    tybes = recv(client_sock_fd,buf,10,0);
            tybes = read(client_sock_fd,buf,10);
            if(tybes <= 0) //断开链接或者出错
            {
                puts("recv error !");
                break;
            }
            printf("recv data : %s",buf);//IO缓冲 加 换行输出
            if(strstr(buf,"quit") != NULL ) //接收到 “quit” 断开连接的客户端,等待下一个连接
            {
                close(client_sock_fd);
                break;
            }
        }

    }
    close(server_sock_fd);
    return 0;
}

  client.c 文件;

#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <strings.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>

int main(int argc, const char *argv[])
{
    int client_sock_fd;
    char buf[20] = {0};
    ssize_t send_tybes;
    struct sockaddr_in server_addr;
    socklen_t len;
    len = sizeof(server_addr);
    bzero(&server_addr,len); //清零

    client_sock_fd = socket(AF_INET,SOCK_STREAM,0); //创建客户端 socket
    if(client_sock_fd < 0)
    {
        perror("client_sock_fd fail ");
        exit(1);
    }

    //填充服务器 地址
    server_addr.sin_family = AF_INET;
//    server_addr.sin_port = htons(atoi(argv[2]));
//    server_addr.sin_addr.s_addr = (inet_addr(argv[1]));
    server_addr.sin_port = htons(atoi("8080"));
    server_addr.sin_addr.s_addr = (inet_addr("192.168.3.132"));

    if( connect(client_sock_fd, (struct sockaddr*)&server_addr, len) < 0) //连接服务器
    {
        perror("connect fail ");
        exit(1);
    }

    while(1)
    {
        fgets(buf,10,stdin); //终端读取数据
        send_tybes = send(client_sock_fd,buf,10,0);
        if(send_tybes <= 0) //接收数据失败或者断开连接
        {
            printf("send fail !");
            break;
        }
        if(strstr(buf,"quit") != NULL) //输入 “quit” 断开连接
        {
            break;
        }
    }
    close(client_sock_fd);
    return 0;
}

测试:此时,服务器只是接收数据,不能发送数据,因为接收和终端获取数据是两个阻塞函数,需要开启多进程或者多线程处理两个阻塞问题

  

原文地址:https://www.cnblogs.com/electronic/p/10981614.html

时间: 2024-10-05 00:33:57

Linux 网络 tcp C/S通信模型的相关文章

(转)同步异步/阻塞非阻塞 和 5种linux网络通信模型

会阻塞的函数:connect, accept,send/recv/sendto/recvfrom等读写函数. 不会阻塞的函数:bind, listen,socket, closesocket. linux网络通信模型有: 阻塞IO模型(同步),非阻塞IO模型(拷贝同步),IO复用模型(多线程同步),信号驱动IO模型((拷贝同步),异步IO模型(异步). node.js对同步/异步,阻塞非阻塞的解释: 线程在执行中如果遇到磁盘读写或网络通信(统称为I/O 操作),通常要耗费较长的时间,这时 操作系

linux网络编程笔记——TCP

1.TCP和UDP TCP是长连接像持续的打电话,UDP是短消息更像是发短信.TCP需要消耗相对较多的资源,但是传输质量有保障,UDP本身是不会考虑传输质量的问题. 2.网络传输内容 我习惯的做法是直接通过TCP传送结构体,当然前提是收发两端都在程序里对目标结构体有充分的定义.特别说明的一点是,要小心收发两端处理器的大小端问题!而且传输信息头里必须包含长度信息,而且通用的是大端.但是,这里的长度和结构体,我选择用小端进行传输. 3.TCPserver实现 参考了别人多线程的回调写法,看起来不错.

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

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

Linux网络编程——tcp并发服务器(poll实现)

想详细彻底地了解poll或看懂下面的代码请参考<Linux网络编程--I/O复用之poll函数> 代码: #include <string.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/select.h> #include <sys/time.h> #include <sys/socket.h> #incl

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

嵌入式 Linux网络编程(二)--TCP编程模型 一.TCP编程模型 TCP编程的一般模型如下图: TCP编程模型分为客户端和服务器端编程,两者编程流程如下: TCP服务器端编程流程: A.创建套接字: B.绑定套接字: C.设置套接字为监听模式,进入被动接受连接状态: D.接受请求,建立连接: E.读写数据: F.终止连接. TCP客户端编程流程: A.创建套接字: B.与远程服务器建立连接: C.读写数据: D.终止连接. 二.TCP迭代服务器编程模型 TCP循环服务器接受一个客户端的连接

20150908 Linux运维网络基础和TCP有限状态机状态转换原理、Linux网络属性配置及网络

1.网络的O互联网模型 物理层:电信号.网卡.比特流,迷数据端设备提供传送数据 数据链路层:将源自网络层来的数据传输至相邻目标网络层中.IP报文. 网络层:负责为分组交换机上的不同主机提供通信服务,TCP/IP层    ( 是在Linux内核实现) 传输层:二个主机中进程间的通信提供服务,通过端口的形式(随机端口)是通信源头 应用层:直接为用户的应用提供服务 (在客户应用中实现)例如:http.tomcat OSI 数据传输格式 协议 会话.表示.应用层 报文 HTTP  FTP    SMTP

Linux下TCP网络编程与基于Windows下C#socket编程间通信

一.linux下TCP网络编程基础,需要了解相关函数 Socket():用于套接字初始化. Bind():将 socket 与本机上的一个端口绑定,就可以在该端口监听服务请求. Listen():使socket处于被动的监听模式,并为该  socket  建立一个输入数据队列,将到达的服务器, 请求保存在此队列中,直到程序处理他们. Accept():让服务器接收客户的连接请求. Connect():客户端使用connect函数来配置 socket并与远端服务器建立一个 TCP 连接. Clos

Linux下TCP网络编程与基于Windows下C#socket编程之间通信

一.linux下TCP网络编程基础,需要了解相关函数 Socket():用于套接字初始化. Bind():将 socket 与本机上的一个端口绑定,就可以在该端口监听服务请求. Listen():使socket处于被动的监听模式,并为该  socket  建立一个输入 数据队列,将到达的服务器, 请求保存在此队列中,直到程序处理他们. Accept():让服务器接收客户的连接请求. Connect():客户端使用connect函数来配置 socket并与远端服务器建立一个 TCP 连接. Clo

linux网络编程 基于TCP的程序开发

面向连接的TCP程序设计 基于TCP的程序开发分为服务器端和客户端两部分,常见的核心步骤和流程: 其实按照上面这个流程调用系统API确实可以完全实现应用层程序的开发,一点问题没有.可随着时间的推移,你会觉得这样子的开发毫无激情.为什么TCP的开发就要按照这样的流程来呢?而且一般出的问题几乎都不在这几个系统调用上,原因何在?当我们弄清网络开发的本质,协议栈的设计原理.数据流向等这些问题的答案也就会慢慢浮出水面了.接下来这几篇博文主要是围绕网络编程展开,目的是引出后面对于Linux下TCP/IP协议