TCP之简单回传(四)

继续采用 TCP之简单回传(三) 的思路,不过由于其转型时,每次读取一个字符都要调用系统函数一次,故其效率较低;

本次我们采用系统中一个函数recv实现预读取;

int PASCAL FAR recv( SOCKET s, char FAR* buf, int len, int flags);
s:一个标识已连接套接口的描述字。
buf:用于接收数据的缓冲区。
len:缓冲区长度。
flags:指定调用方式。
MSG_PEEK 查看当前数据。数据将被复制到缓冲区中,但并不从输入队列中删除。

通过预览内核缓冲区中的数据判断,我们所预览的数据中是否存在‘\n‘;
若存在,则将 ‘\n‘之前的数据(包括‘\n‘)读进我们自己的缓冲区;

若不存在,则直接将我们预览的所有数据读进缓冲区;

recv_peek 以及readn解决这个问题;

代码如下:

//预览内核缓冲区数据
ssize_t recv_peek(int fd, void *usrbuf, size_t maxlen)
{
    ssize_t nread;
    do
    {
        nread = recv(fd, usrbuf, maxlen, MSG_PEEK);
    }
    while(nread == -1 && errno == EINTR);
    return nread;
}

ssize_t readline(int fd, void *usrbuf, size_t maxlen)
{
    char *bufp = (char *)usrbuf;
    size_t nleft = maxlen - 1;
    ssize_t count = 0;

    ssize_t  nread;
    while(nleft > 0)
    {
        nread = recv_peek(fd, bufp, nleft);//预览内核缓冲区数据
        if( nread <= 0)  //由客户端处理
            return nread;
        //遍历bufp,以确定是否存在\n
        int i;
        for ( i = 0; i < nread; i++)
        {
        //存在‘\n‘
            if(bufp[i] == ‘\n‘)
            {
                size_t nsize = i +1;
                if( readn(fd, bufp, nsize) != nsize)//说明\n前有i个字符
                    ERR_EXIT("readn");
                bufp +=nsize; //重置偏移量
                count +=nsize;//统计读取个数
                *bufp = 0;
                return count;
            }
        }
        //不存在‘\n‘
        if( readn(fd, bufp, nread) != nread)
            ERR_EXIT("readn");
        bufp += nread;
        count += nread;
        nleft -=nread;
    }
    *bufp = 0;
    return count;
}

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(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

时间: 2024-10-06 01:10:08

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

TCP之简单回传(三)

鉴于TCP之简单回传(一)   中所出现的问题,本博文所要采取的一种方法是: 服务器端和客户端共同遵守如下约定: 接收的字节流中,若遇到'\n',表示一次传送完毕. 具体为: 客户端把每次欲发送的数据的最后一个字符设置为 '\n': 而服务器每次接收时,一个一个字符的从缓冲区中取出一个字符,然后再判断该字符是否为'\n',若不是,则继续读取:若是,则退出循环,表示本次接收结束: 实现代码如下: //按字符读取--->由于每读取一个字符就产生一次系统调用,故效率较低 ssize_t readlin

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

Http协议与TCP协议简单理解

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

wireshark抓包图解-tcp三次握手四次挥手详解/

http://www.seanyxie.com/wireshark%E6%8A%93%E5%8C%85%E5%9B%BE%E8%A7%A3-tcp%E4%B8%89%E6%AC%A1%E6%8F%A1%E6%89%8B%E5%9B%9B%E6%AC%A1%E6%8C%A5%E6%89%8B%E8%AF%A6%E8%A7%A3/ wireshark抓包图解-tcp三次握手四次挥手详解/ tcpdump抓包命令使用方法及内容解析/

计算机网络——TCP三次、四次握手详解

三次握手:建立TCP连接 连接建立过程: B的TCP服务器进程先创建传输控制块TCB(存储了每一个连接中的一些重要信息,如:TCP连接表,到发送和接收缓存的指针,到重传队列的指针,当前的发送和接收序号,等),准备接受客户进程的连接请求.然后服务器进程就处于LISTEN(收听)状态,等待客户的连接请求.如有,即作出相应. A的TCP客户进程也是首先创建传输控制模块TCB,然后向B发出连接请求报文段,这时首部中的同步为SYN=1,同时选择一个初始序号seq=x.TCP规定,SYN报文段(即SYN=1

TCP三次握手/四次挥手详解

TCP三次握手/四次挥手详解 TCP(Transmission Control Protocol) 传输控制协议 TCP是主机对主机层的传输控制协议,提供可靠的连接服务,采用三次握手确认建立一个连接: 位码即tcp标志位,有6种标示: SYN(synchronous建立联机) ACK(acknowledgement 确认) PSH(push传送) FIN(finish结束) RST(reset重置) URG(urgent紧急) Sequence number(顺序号码) Acknowledge

TCP三次握手&amp;四次挥手(示意图)

经典的三次握手示意图:(#add,“握手”即图中左边到右边的连线) 经典的四次握手关闭图: TCP是主机对主机层的传输控制协议,提供可靠的连接服务,采用三次握手确认建立一个连接: 位码即tcp标志位,有6种标示: SYN(synchronous建立联机) ACK(acknowledgement 确认) PSH(push传送) FIN(finish结束) RST(reset重置) URG(urgent紧急) Sequence number(顺序号码) Acknowledge number(确认号码

[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