简单的TCP-Server2

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

/*
 * http://fanqiang.chinaunix.net/program/netpro/2001-05-08/1965.shtml
 * 6.1 recv和send
 * recv和send函数提供了和read和write差不多的功能.不过它们提供 了第四个参数来控制读写操作.
 *
 *  int recv(int sockfd,void *buf,int len,int flags)
 *  int send(int sockfd,void *buf,int len,int flags)
 *
 * 前面的三个参数和read,write一样,第四个参数可以是0或者是以下的组合
 _______________________________________________________________
|  MSG_DONTROUTE        |  不查找路由表                         |
|  MSG_OOB              |  接受或者发送带外数据                  |
|  MSG_PEEK             |  查看数据,并不从系统缓冲区移走数据      |
|  MSG_WAITALL          |  等待所有数据                         |
|--------------------------------------------------------------|

MSG_DONTROUTE:是send函数使用的标志.这个标志告诉IP协议.目的主机在本地网络上面,没有必要查找路由表.这个标志一般用网络诊断和路由程序里面.
MSG_OOB:表示可以接收和发送带外的数据.关于带外数据我们以后会解释的.
MSG_PEEK:是recv函数的使用标志,表示只是从系统缓冲区中读取内容,而不清楚系统缓冲区的内容.这样下次读的时候,仍然是一样的内容.一般在有多个进程读写数据时可以使用这个标志.
MSG_WAITALL是recv函数的使用标志,表示等到所有的信息到达时才返回.使用这个标志的时候recv回一直阻塞,直到指定的条件满足,或者是发生了错误. 1)当读到了指定的字节时,函数正常返回.返回值等于len 2)当读到了文件的结尾时,函数正常返回.返回值小于len 3)当操作发生错误时,返回-1,且设置错误为相应的错误号(errno)

如果flags为0,则和read,write一样的操作.还有其它的几个选项,不过我们实际上用的很少,可以查看 Linux Programmer's Manual得到详细解释.

 * 6.2 recvfrom和sendto
 * 这两个函数一般用在非套接字的网络程序当中(UDP),我们已经在前面学会了.
 *
 * 6.3 recvmsg和sendmsg
 * recvmsg和sendmsg可以实现前面所有的读写函数的功能.
 *
 *  int recvmsg(int sockfd,struct msghdr *msg,int flags)
 *  int sendmsg(int sockfd,struct msghdr *msg,int flags)
 *
 *   struct msghdr
        {
                void *msg_name;
                int msg_namelen;
                struct iovec *msg_iov;
                int msg_iovlen;
                void *msg_control;
                int msg_controllen;
                int msg_flags;
        }

 struct iovec
        {
                void *iov_base; // 缓冲区开始的地址
                size_t iov_len; // 缓冲区的长度
        }

msg_name和 msg_namelen当套接字是非面向连接时(UDP),它们存储接收和发送方的地址信息.msg_name实际上是一个指向struct sockaddr的指针,msg_name是结构的长度.当套接字是面向连接时,这两个值应设为NULL.
msg_iov和msg_iovlen指出接受和发送的缓冲区内容.msg_iov是一个结构指针,msg_iovlen指出这个结构数组的大小.
msg_control和msg_controllen这两个变量是用来接收和发送控制数据时的
msg_flags指定接受和发送的操作选项.和recv,send的选项一样

6.4 套接字的关闭
关闭套接字有两个函数close和shutdown.用close时和我们关闭文件一样.

6.5 shutdown
 int shutdown(int sockfd,int howto)

TCP连接是双向的(是可读写的),当我们使用close时,会把读写通道都关闭,有时侯我们希望只关闭一个方向,这个时候我们可以使用shutdown.针对不同的howto,系统回采取不同的关闭方式.
howto=0这个时候系统会关闭读通道.但是可以继续往接字描述符写.
howto=1关闭写通道,和上面相反,着时候就只可以读了.
howto=2关闭读写通道,和close一样 在多进程程序里面,如果有几个子进程共享一个套接字时,如果我们使用shutdown, 那么所有的子进程都不能够操作了,这个时候我们只能够使用close来关闭子进程的套接字描述符.
 *
 */

int complete_write(int fd,void *buffer,int length)
{
    int bytes_left;
    int written_bytes;
    char *ptr;

    ptr=buffer;
    bytes_left=length;
    while(bytes_left>0)
    {
        /* 开始写*/
        written_bytes = write(fd,ptr,bytes_left);
        if(written_bytes<=0) /* 出错了*/
        {
            if(errno==EINTR) /* 中断错误 我们继续写*/
                written_bytes=0;
            else             /* 其他错误 没有办法,只好撤退了*/
                return(-1);
        }
        bytes_left-=written_bytes;
        ptr+=written_bytes;     /* 从剩下的地方继续写  */
    }
    return(0);
}

int complete_read(int fd,void *buffer,int length)
{
    int bytes_left;
    int bytes_read;
    char *ptr;

    bytes_left=length;
    while(bytes_left>0)
    {
        bytes_read = read(fd,ptr,bytes_read);
        if(bytes_read<0)
        {
            if(errno==EINTR)
                bytes_read=0;
            else
                return(-1);
        }
        else if(bytes_read==0)
            break;
        bytes_left-=bytes_read;
        ptr+=bytes_read;
    }
    return(length-bytes_left);
}

int main(int argc, char *argv[])
{
    size_t len;
    int sk, talk, ret;
    char buff[1024] = {'\0'};
    struct sockaddr_in server, client;

    sk = socket(AF_INET, SOCK_STREAM, 0);
    if(sk == -1){
        printf("%s\n", strerror(errno));
        return -1;
    }

    bzero(&server, sizeof(server));
    server.sin_family = AF_INET;
    server.sin_port = htons(3000);
    server.sin_addr.s_addr = htonl(INADDR_ANY);

    ret = bind(sk, (struct sockaddr*)&server, sizeof(server));
    if(ret != 0){
        printf("%s\n", strerror(errno));
        return -1;
    }

    ret = listen(sk, 5);
    if(ret != 0){
        printf("%s\n", strerror(errno));
        return -1;
    }

    bzero(&client, sizeof(client));
    len = sizeof(client);
    talk = accept(sk, (struct sockaddr*)&client, (socklen_t *)&len);
    if(talk == -1){
        printf("%s\n", strerror(errno));
        return -1;
    }

    ret = complete_write(talk, "Hello", strlen("Hello") + 1);
    if(ret < 0){
        printf("send error\n");
        return -1;
    }else{
        printf("the number sent %d bytes\n", ret);
    }

    ret = complete_read(talk, buff, sizeof(buff));
    if(ret < 0){
        printf("send error\n");
        return -1;
    }else{
        printf("the number read %d bytes\n", ret);
    }
    printf("%s\n", buff);

    close(talk);
    close(sk);
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值