TCP之简单回传(三)

鉴于TCP之简单回传(一)   中所出现的问题,本博文所要采取的一种方法是:

服务器端和客户端共同遵守如下约定:
接收的字节流中,若遇到‘\n‘,表示一次传送完毕。

具体为:

客户端把每次欲发送的数据的最后一个字符设置为 ‘\n‘;

而服务器每次接收时,一个一个字符的从缓冲区中取出一个字符,然后再判断该字符是否为‘\n‘,若不是,则继续读取;若是,则退出循环,表示本次接收结束;

实现代码如下:

//按字符读取--->由于每读取一个字符就产生一次系统调用,故效率较低
ssize_t readline_slow(int fd, void *usrbuf, size_t maxlen)
{
    char *bufp = (char*)usrbuf;//偏移位置
    ssize_t nread;
    size_t nleft = maxlen -1 ;//剩余字节数
    char ch;//保存每次读取的字符
    while( nleft > 0)//只要还有没读的,就一直循环
    {
        if(-1 == (nread=read(fd, &ch, 1)))//把从fd中读取的字符存进ch中
        {
            if(errno == EINTR)//被中断信号打断
                continue;
            return -1;//err
        }else if(0 == nread )
            break; //EOF

        *bufp = ch;//将读取的字符存进buf
        bufp++;//向前移动

        nleft --;//剩余字节数--
        if(ch == ‘\n‘)//如果该字符为\n。本次读取完成
            break;
    }
    *bufp =‘\0‘;//
    return (maxlen- nleft -1);//最大长度 -剩余的字符 - ‘\0‘
}

本程序中所用到的函数都可以在 TCP之函数封装 中找到;

server服务器端:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#define ERR_EXIT(m)     do {         perror(m);        exit(EXIT_FAILURE);    }while(0)

void do_server(int fd);

int main(int argc, const char *argv[])
{
//socket
    int listenfd = socket(AF_INET, SOCK_STREAM, 0 );
    if( -1 == listenfd)
        ERR_EXIT("socket");
//地址复用
    int on = 1;
    if(setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof on) < 0)
        ERR_EXIT("setsockopt");
//bind
    struct sockaddr_in addr;
    memset(&addr, 0, sizeof(addr));
    addr.sin_family = AF_INET;
    addr.sin_port = htons(8888); //主机字节序转化为网络字节序
    addr.sin_addr.s_addr = inet_addr("127.0.0.1");//点分十进制转化成网络字节序
    if( -1 == bind( listenfd, (struct sockaddr*)&addr, sizeof(addr)))
        ERR_EXIT("bind");

//listen
    if( -1 == listen(listenfd,SOMAXCONN ))
        ERR_EXIT("listen");

//accept
    int peerfd = accept(listenfd, NULL, NULL);//对方的IP&PORT

//read&write
    do_server(peerfd);

//close
    close(peerfd);
    close(listenfd);
    return 0;
}

void do_server(int  fd)
{
    char recvbuf[1024100] = {0};
    int cnt =0;
    while(1)
    {
        int nread = readline_slow(fd, recvbuf, sizeof(recvbuf));
        if(nread == -1)//err
        {
            if(errno == EINTR)
                continue;
            ERR_EXIT("read");
        }else if (nread == 0 )//write close
        {
            printf("close...\n");
            exit(EXIT_FAILURE);
        }

        // ok
        printf("count = %d recv size = %d\n",++cnt, nread);
        memset(recvbuf, 0, sizeof(recvbuf));
    }
}

client客户端:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#define ERR_EXIT(m)     do {         perror(m);        exit(EXIT_FAILURE);    }while(0)
void do_server(int fd);

int main(int argc, const char *argv[])
{
//socket
    int peerfd = socket(AF_INET, SOCK_STREAM,0);
    if( -1 == peerfd)
        ERR_EXIT("socket");
//connect
    struct sockaddr_in addr;
    addr.sin_family = AF_INET;
    addr.sin_port = htons(8888);
    addr.sin_addr.s_addr = inet_addr("127.0.0.1");

    if( -1 == connect(peerfd,(struct sockaddr*)&addr, sizeof(addr) ))
        ERR_EXIT("connect");

//read&write
    do_server(peerfd);

    return 0;
}

void do_server(int fd)
{
    #define SIZE 1024
    char sendbuf[SIZE +1]= {0};
    int i ;
    for (i = 0; i < SIZE-1; i++) //attention
    {
        sendbuf[i]= ‘a‘;
    }
    sendbuf[SIZE-1] = ‘\n‘;//标志位
    int cnt =0;
    while(1)
    {
        int i;
        for (i = 0; i < 10; i++)
        {
            int nwrite =writen(fd, sendbuf, SIZE);//发送SIZE个数据中最后一个字符为\n
            if( nwrite != SIZE)
                ERR_EXIT("writen");
            printf("cout = %d,write %d bytes\n",++cnt, SIZE);
        }
        nano_sleep(4.5);
    }
}

测试结果:

./server
count = 1 recv size = 1024
count = 2 recv size = 1024
count = 3 recv size = 1024
count = 4 recv size = 1024
count = 5 recv size = 1024
count = 6 recv size = 1024
count = 7 recv size = 1024
count = 8 recv size = 1024
count = 9 recv size = 1024
count = 10 recv size = 1024
count = 11 recv size = 1024
count = 12 recv size = 1024
count = 13 recv size = 1024
count = 14 recv size = 1024
count = 15 recv size = 1024
count = 16 recv size = 1024
count = 17 recv size = 1024
count = 18 recv size = 1024
count = 19 recv size = 1024
count = 20 recv size = 1024

./client
count = 1 recv size = 1024
count = 2 recv size = 1024
count = 3 recv size = 1024
count = 4 recv size = 1024
count = 5 recv size = 1024
count = 6 recv size = 1024
count = 7 recv size = 1024
count = 8 recv size = 1024
count = 9 recv size = 1024
count = 10 recv size = 1024
count = 11 recv size = 1024
count = 12 recv size = 1024
count = 13 recv size = 1024
count = 14 recv size = 1024
count = 15 recv size = 1024
count = 16 recv size = 1024
count = 17 recv size = 1024
count = 18 recv size = 1024
count = 19 recv size = 1024
count = 20 recv size = 1024

注意:本程序效率比较低下,原因在于:

每次读取一个字符都要系统调用一次read函数。

解决方法:TCP之简单回传(四)

时间: 2024-10-12 20:10:33

TCP之简单回传(三)的相关文章

TCP之简单回传(四)

继续采用 TCP之简单回传(三) 的思路,不过由于其转型时,每次读取一个字符都要调用系统函数一次,故其效率较低: 本次我们采用系统中一个函数recv实现预读取: int PASCAL FAR recv( SOCKET s, char FAR* buf, int len, int flags); s:一个标识已连接套接口的描述字. buf:用于接收数据的缓冲区. len:缓冲区长度. flags:指定调用方式. MSG_PEEK 查看当前数据.数据将被复制到缓冲区中,但并不从输入队列中删除. 通过

TCP之简单回传(二)

鉴于TCP之简单回传(一)   中所出现的问题,本博文所要采取的一种方法是: 客户端:先向服务器传送一个 int32_t 大小的数据,表示随后所要发送数据的真实长度: 服务器:先接收一个int32_t 大小的数据,再接收真实的数据: 本程序中所用到的函数都可以在 TCP之函数封装 中找到: server服务器端具体实现: #include <stdio.h> #include <stdlib.h> #include <string.h> #include <uni

TCP之简单回传

本文介绍Tcp的简单应用:简单的 回传(即客户端发送什么,服务器就转发给客户端什么). 主要包含以下几个函数原型: 服务器端: //服务器端主要函数原型:int socket(int domain, int type, int protocol); int bind( int sockfd , const struct sockaddr * my_addr, socklen_t addrlen); int listen( int fd, int backlog); SOCKET PASCAL a

TCP协议中的三次握手和四次挥手(图解)【转】

建立TCP需要三次握手才能建立,而断开连接则需要四次握手.整个过程如下图所示: 先来看看如何建立连接的. [更新于2017.01.04 ]该部分内容配图有误,请大家见谅,正确的配图如下,错误配图也不删了,大家可以比较下,对比理解效果更好.这么久才来更新,抱歉!! 错误配图如下: 首先Client端发送连接请求报文,Server段接受连接后回复ACK报文,并为这次连接分配资源.Client端接收到ACK报文后也向Server段发生ACK报文,并分配资源,这样TCP连接就建立了. 那如何断开连接呢?

TCP建立连接通过三次握手,释放连接通过四次挥手

tcp建立连接通过三次握手.释放连接通过四次挥手 一.tcp建立连接通过三次握手 首先tcp的特性:tcp是面向连接的安全可靠的传输协议. 传输连接是用来传送tcp报文的.tcp传输连接的建立和释放是每一次面向连接的通信中必不可少的过程.因此,传输连接就有三个阶段:连接建立.数据传输.连接释放.传输连接的管理就是使传输连接的建立和释放都能正常的进行. 在tcp连接建立的过程中要解决以下三个问题: 1.要使每一方都能够确知对方的存在. 2.要允许双方协商一些参数(如最大窗口值.是否使用窗口扩大选项

Http协议与TCP协议简单理解

在C#编写代码,很多时候会遇到Http协议或者TCP协议,这里做一个简单的理解. TCP协议对应于传输层,而HTTP协议对应于应用层,从本质上来说,二者没有可比性.Http协议是建立在TCP协议基础之上的,当浏览器需要从服务器获取网页数据的时候,会发出一次Http请求.Http会通过TCP建立起一个到服务器的连接通道,当本次请求需要的数据完毕后,Http会立即将TCP连接断开,这个过程是很短的.所以Http连接是一种短连接,是一种无状态的连接.所谓的无状态,是指浏览器每次向服务器发起请求的时候,

tcp/ip协议第三章 ip:网际协议

tcp/ip协议第三章读后总结 ip是tcp/ip协议族中最为核心的协议.ip提供不可靠传输,不可靠的意思是它不保证ip数据包能成功的到达目的地.ip仅提供最好的传输服务.任何要求的可靠性必须由上层来提供(如tcp) 子网的划分缩小了internet路由表的规模,因为许多网络经常可以通过单个表目就可以访问了. 接口网络的有关信息通过ifcongif和netstat命令可以获得,包括接口的ip地址.子网掩码.广播地址.以及mtu等

[Java] Tcp/udp 简单通信

本文转自  我自己的博客guozeyiblog.cn 欢迎来访 效果图: //UDP通信 import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.IOException; import java.net.*; import javax.swing.*; class send extends JFrame implements ActionL

TCP/IP之TCP协议首部、三次握手、四次挥手、FSM

TCP包头 <--------------------------------32 位------------------------------> 0 8 16 24 32 |----------------|----------------|----------------|----------------| ----- | Source port | Destination port | | |-----------------------------------------------