首页 > 代码库 > TCP之简单回传

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 accept( SOCKET s, struct sockaddr * addr,int * addrlen);

客户端:

int socket(int domain, int type, int protocol);int PASCAL FAR connect( SOCKET s, const struct sockaddr FAR* name, int namelen);
服务器端和客户端:以上函数错误的情况下都是 -1

服务器端:

#include <stdio.h>#include <stdlib.h>#include <string.h>#include <unistd.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_service(int sockfd);int main(int argc, const char *argv[]){    //socket 返回一个监听的文件描述符    int listenfd = socket(PF_INET, SOCK_STREAM, 0);    if(listenfd == -1)        ERR_EXIT("socket");//地址复用--->记住即可    int on = 1;    if (setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0)        ERR_EXIT("setsockopt");        //bind 实现将服务器的地址IP,端口号PORT绑定    struct sockaddr_in addr;    memset(&addr, 0, sizeof addr);    addr.sin_family = AF_INET;    addr.sin_addr.s_addr = inet_addr("127.0.0.1");    addr.sin_port = htons(8976);    if(bind(listenfd, (struct sockaddr*)&addr, sizeof addr) == -1)        ERR_EXIT("bind");//listen 监听集合(监听客户端是否发送给服务器消息)    if(listen(listenfd, SOMAXCONN) == -1)        ERR_EXIT("listen");//accept 接受客户端请求,并返回另外一个文件描述符单独处理客户的请求    int peerfd = accept(listenfd, NULL, NULL);//read&write      do_service(peerfd);//close    close(peerfd);    close(listenfd);    return 0;}//read&writevoid do_service(int sockfd){    char recvbuf[1024] = {0};    while(1)    {            //read 读取客户端发送来的数据        int nread = read(sockfd, recvbuf, sizeof recvbuf);        if(nread == -1)//出错情况以及处理        {            if(errno == EINTR)                continue;            ERR_EXIT("read");        }        else if(nread == 0)//客户端已发送完毕,即客户端的写端关闭        {            printf("close ...\n");            exit(EXIT_SUCCESS);        }     //回传给客户端           write(sockfd, recvbuf, strlen(recvbuf));      //清空缓冲区,以免发送覆盖不完全情况          memset(recvbuf, 0, sizeof recvbuf);    }}


客户端:

//client.c#include <stdio.h>#include <stdlib.h>#include <string.h>#include <unistd.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_service(int sockfd);int main(int argc, const char *argv[]){ //socket    int peerfd = socket(PF_INET, SOCK_STREAM, 0);    if(peerfd == -1)        ERR_EXIT("socket");//connect请求与服务器连接    struct sockaddr_in addr;    memset(&addr, 0, sizeof addr);    addr.sin_family = AF_INET;    addr.sin_addr.s_addr = inet_addr("127.0.0.1"); //localhost    addr.sin_port = htons(8976);    socklen_t len = sizeof addr;    if(connect(peerfd, (struct sockaddr*)&addr, len) == -1)        ERR_EXIT("Connect");//send&recv    do_service(peerfd);    return 0;}//send&recvvoid do_service(int sockfd){    char recvbuf[1024] = {0}; //接收缓冲区    char sendbuf[1024] = {0};//发送缓冲区    while(1)    {        fgets(sendbuf, sizeof sendbuf, stdin);//从键盘中输入进发送缓冲区数据        write(sockfd, sendbuf, strlen(sendbuf)); //向服务器发送数据        //read        int nread = read(sockfd, recvbuf, sizeof recvbuf); //读取服务器发来的数据        if(nread == -1)//err        {            if(errno == EINTR)                continue;            ERR_EXIT("read");        }        else if(nread == 0)//EOF        {            printf("server close!\n");            close(sockfd);            exit(EXIT_SUCCESS);        }        printf("receive msg : %s", recvbuf); //打印所接收的数据        memset(recvbuf, 0, sizeof recvbuf);        memset(sendbuf, 0, sizeof sendbuf);    }}

这样我们就简单实现了tcp通信。

注意:本程序不能解决 字节流的 粘包问题

如下程序:服务器与客户端发生改变的代码仅仅是 do_serve 程序;

服务器端多改动的部分如下:

void do_service(int sockfd){    int cnt = 0;    char recvbuf[1024000] = {0}; //从缓冲区一次读取的数据远远大于客户端一次发送的数据(1024)    while(1)    {        int nread = read(sockfd, recvbuf, sizeof recvbuf); //从缓冲区读数据        if(nread == -1)        {            if(errno == EINTR)                continue;            ERR_EXIT("read");        }        else if(nread == 0)        {            printf("close ...\n");            exit(EXIT_SUCCESS);        }        printf("count = %d, receive size = %d\n", ++cnt, nread);        memset(recvbuf, 0, sizeof recvbuf);    }}

 

客户端所改动的数据如下:

void do_service(int sockfd){    #define SIZE 1024    char sendbuf[SIZE + 1] = {0};    int i;    for(i = 0; i < SIZE; ++i) //缓冲区中的每个字符的值        sendbuf[i] = a;    int cnt = 0; //次数    while(1)    {        int i;        for(i = 0; i < 10; ++i) //每次发送SIZE个字符,总共发送十次        {            write(sockfd, sendbuf, SIZE);            printf("count = %d, write %d bytes\n", ++cnt, SIZE);        }        nano_sleep(4); //暂停客户端4s钟        memset(sendbuf, 0, sizeof sendbuf);    }}void nano_sleep(double val){    struct timespec tv;    tv.tv_sec = val; //取整    tv.tv_nsec = (val - tv.tv_sec) * 1000 * 1000 * 1000;    int ret;    do    {        ret = nanosleep(&tv, &tv);    }while(ret == -1 && errno == EINTR);}

 

TCP之简单回传