码迷,mamicode.com
首页 > 其他好文 > 详细

TCP之简单回传

时间:2014-10-15 04:16:59      阅读:254      评论:0      收藏:0      [点我收藏+]

标签:style   blog   color   io   os   ar   for   文件   sp   

本文介绍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&write
void 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&recv
void 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之简单回传

标签:style   blog   color   io   os   ar   for   文件   sp   

原文地址:http://www.cnblogs.com/xfxu/p/4025514.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!