关闭

UNP-基本TCP编程-1

标签: 编程tcpipv4结构struct
156人阅读 评论(0) 收藏 举报
分类:

套接字地址结构:

-> 通用套接字地址结构:

  #include <sys/socket.h>
        struct sockaddr{
            sa_family_t         sa_family;
                char                sa_data[14];
        }

-> ipv4 套接字地址结构

#include <netinet_in.h>
        struct sockaddr_in{
            sa_family_t         sin_family;
            in_port_t           sin_port;
            struct in_addr      sin_addr;
            char                sin_zero[8];    
        }

    struct in_addr{
        int32_t         s_addr;
    }
-> ipv6 套接字地址结构
#include <netinet/in.h>
    struct sockaddr_in6{
        sa_family_t sin6_family;
        in_port_t   sin6_port;
        uint32_t    sin6_flowinfo;
        struct in6_addr sin6_addr;
        uint32_t    sin6_scope_id;  
        }
        struct in6_addr{
            uint8_t s6_addr[16];
        }

=> 协议地址长度:

        INET_ADDRSTRLEN     // 16
        INET6_ADDRSTRLEN    // 46

=> 字节排序函数:

    #include <netinet/in.h>
    uint16_t    htons(uint16_t host16value);
    uint16_t    ntohs(uint16_t net16value);
    uint32_t    htonl(uint32_t host32value);
    uint32_t    ntohl(uint32_t net32value);
    #include <arpa/inet.h>
    int   inet_pton(int family, const char *prt,void*addr); // 本地地址转网络地址

    char *inet_ntop(int family, const void *addr, char *prt, size_t len);   // 网络地址转本地地址

socket:

    #include <sys/socket.h>
    int socket(int family, int type, int protocol);

            family:
                AF_INET:            IPV4 协议
                AF_INET6:           IPV6 协议
                AF_LOCAL:           UNIX域套接字协议
                AF_ROUTE:           路由套接字
                AF_KEY  :           密钥套接字
            type:
                SOCK_STREAM:        字节流套接字
                SOCK_DGRAM:         数据报套接字
                SOCK_SEQPACKET:     有序分组套接字
                SOCK_RAW:           原始套接字
            protocol:
                IPPROTO_TCP         TCP 传输协议
                TPPROTO_UDP         UDP 传输协议
                IPPROTO_SCTP        SCTP传输协议
connect:
        #include <sys/socket.h>
        int connect(int sockfd, const struct sockaddr* servaddr, socklen_t addrlen);

        1> 当ARP表中没有目的主机的MAC地址的时候: 首先发送ARP请求协议再发送SYN分节
        2> 当ARP表中有原目的主机的MAC地址,现在已经失效的时候, 首先发送三次 SYN分节,在这之内没有收到ACK|SYN,就清除ARP的相关表项,
           之后在重新发送 ARP

        ->TCP链接失败的原因:

            1> 没有收到响应时, 发送 ETIMEOUT 错误--等待时间一般为75秒,但不确定
            2> 若服务器上没有在请求端口上有监听程序, 则对SYN的请求返回RST, 客户端返回ECONNREFUSED错误
                //若是UDP数据包则服务器会产生 端口不可达的ICMP错误(3,3) 
            3> 客户端发送的SYN分节在某个路由器上引发了 目的不可达的 ICMP 错误,一般有两种
                -> 主机不可达: ICMP(3 1) // EHOSTUNREACH
                -> 网路不可达: ICMP(3 0) // ENETUNREACH
-> connect 成功案例:
        // 第一次握手协议
        14:33:56.868365 IP (tos 0x0, ttl 64, id 15339, offset 0, flags [DF], proto TCP (6), length 60)
            saligia.linux.55895 > 192.168.1.79.saligia-service: 
            Flags [S], cksum 0x8fe6 (correct), seq 1019698398, win 14600, 
            options [mss 1460,sackOK,TS val 7104309 ecr 0,nop,wscale 6], length 0
        // 第二次握手协议
        14:33:56.871411 IP (tos 0x0, ttl 64, id 0, offset 0, flags [DF], proto TCP (6), length 60)
            192.168.1.79.saligia-service > saligia.linux.55895: 
            Flags [S.], cksum 0x1175 (correct), seq 193439328, ack 1019698399, win 14480, 
            options [mss 1460,sackOK,TS val 5754009 ecr 7104309,nop,wscale 6], length 0
        // 第三次握手协议
        14:33:56.871496 IP (tos 0x0, ttl 64, id 15340, offset 0, flags [DF], proto TCP (6), length 52)
            saligia.linux.55895 > 192.168.1.79.saligia-service:
            Flags [.], cksum 0x77e8 (correct), seq 1, ack 1, win 229, 
            options [nop,nop,TS val 7104312 ecr 5754009], length 0
bind:
        #include <sys/socket.h>
        int bind(int sockfd, const struct sockaddr* servaddr, socklen_t addrlen);

        -> 如果TCP服务器没有把IP绑定到他的套接字上(ip设置为0),内核就把客户端的目的IP作为他的源IP地址
            -> servaddr->sin_addr.s_addr = htonl(INADDR_ANY);
        -> 如果端口号为0, 内核就选择一个临时端口号分配给他

        -> 常见错误是 EADDRINUSE
            -> 解决办法 : setsockopt(sockfd, SO_REUSEADDR, ..);
listen:
        -> 将一个主动套接字转成被动套接字

        #include <sys/socket.h>
        int listen(int blocklog);

        -> 队列中包含已经完成三次握手和未完成三次握手的链接
accept:
        -> 从已经完成三次握手的队列中取下一个链接进行交互

        #include <sys/socket.h>
        int accept(int sockfd, struct sockaddr *clienaddr, socklen_t * addrlen);
关闭链接:

        -> close: 关闭套接字
                #include <unistd.h>
                int close(int sockfd);


获取链接信息:
        #include <sys/socket.h>

        -> getsockname: // 返回本地协议地址
            int getsockname(int fd, struct sockaddr *addr, socklen_t *addrlen);
        -> getpeername:// 获取交互对端地址
            int getpeername(int fd, struct sockaddr *addr, socklen_t *addrlen);

client 端

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

void err_que(const char *msg)
{
    puts(msg);
    exit(0);
}

void err_sys(const char *msg)
{
    printf("%s:%s\n", msg, strerror(errno));
    exit(errno);
}
void * recvfromservice(void *msg);

int main(int argc, char *argv[])
{
    int                                         sockfd;
    int                                         recv_len;
    int                                         read_len;
    pthread_t                           ptid;
    char                                        strbuff[1024];
    struct sockaddr_in          serviceaddr;
    pthread_attr_t                  pthattr;


    if(argc != 2)
        err_que("client Ipaddress");

    if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
        err_sys("socket error");

    memset(&serviceaddr, 0, sizeof(serviceaddr));
    serviceaddr.sin_family = AF_INET;
    serviceaddr.sin_port    = htons(8068);
    if(inet_pton(AF_INET, argv[1], (void *)&serviceaddr.sin_addr.s_addr) == -1)
        err_sys("inet_pton error");

    // 链接服务器
    if(connect(sockfd, (struct sockaddr *)&serviceaddr, sizeof(serviceaddr)) == -1)
        err_sys("connect error");

    pthread_attr_init(&pthattr);
    pthread_attr_setdetachstate(&pthattr, PTHREAD_CREATE_DETACHED);
    pthread_create(&ptid, &pthattr, recvfromservice, &sockfd);

    while(read_len=read(STDIN_FILENO, strbuff, sizeof(strbuff)))
    {
        if(read_len == -1)
            err_sys("read_error");
        else if(read_len == 0)
        {
            close(sockfd);
            break;
        }
        else
            if(send(sockfd, strbuff, read_len, 0) == -1)
                err_sys("send error");
    }

    return 0;
}

void * recvfromservice(void *msg)
{
    int recv_len;
    char buff[1024];
    int servicefd = *(int *)msg;

    while(recv_len = recv(servicefd, buff, sizeof(buff), 0))
    {
        if(recv_len == -1)
            err_sys("recv error");

        if(write(STDOUT_FILENO, buff, recv_len) == -1)
            err_sys("send error");
    }
    exit(0);
}

服务器端

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

void err_sys(const char *msg)
{
    printf("%s:%s\n", msg, strerror(errno));
    exit(errno);
}

void * sendtoclient(void * args);

int main(int argc, char *argv[])
{
    int                 i;
    pthread_t           ppid;
    pthread_attr_t      pthattr;
    int                 sockfd;
    int                 clientfd;
    int                 addr_len;
    int                 read_len;
    char                ip_addr[INET_ADDRSTRLEN];
    struct sockaddr_in  service_addr;
    struct sockaddr_in  client_addr;
    char                readbuff[1024];

    // 建立套接字
    if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
        err_sys("socket error");


    // 设置绑定地址
    memset(&service_addr, 0, sizeof(service_addr));
    service_addr.sin_family     = AF_INET;
    service_addr.sin_port       = htons(8068);
    service_addr.sin_addr.s_addr= 0;

    // 绑定地址
    if(bind(sockfd, (struct sockaddr *)&service_addr, sizeof(service_addr)) == -1)
        err_sys("bind error");
    // 进入Listen 状态
    if(listen(sockfd, 5) == -1)
        err_sys("listen error");

    while(1)
    {
        // 等待客户端的连接
        if((clientfd=accept(sockfd, (struct sockaddr *)&client_addr, &addr_len)) == -1)
                err_sys("client error");

        printf("Accept form : %s\n", inet_ntop(AF_INET, &client_addr.sin_addr.s_addr, ip_addr, INET_ADDRSTRLEN));

        // 创建线程
        if(pthread_attr_init(&pthattr) == -1)
            err_sys("pthread_attr_init error");
        pthread_attr_setdetachstate(&pthattr, PTHREAD_CREATE_DETACHED);
        if(pthread_create(&ppid, &pthattr, sendtoclient, &clientfd) != 0)
            err_sys("pthread_creat error");

        // 接受客户端数据
        while(read_len = recv(clientfd, readbuff, sizeof(readbuff), 0))
        {
            if(read_len == -1)
                err_sys("read error");

            if(write(STDOUT_FILENO, readbuff, read_len)== -1)
                err_sys("write error");
        }

        // 取消线程
        close(clientfd);
        exit(0);
    }


    return 0;
}
void * sendtoclient(void * args)
{
    char strbuf[1024];
    int  strlen;
    int clientfd = *(int *)args;

    while(strlen = read(STDIN_FILENO, strbuf, sizeof(strbuf)))
    {
        if(send(clientfd, strbuf, strlen, 0) == -1) 
            err_sys("send error");
    }
}

0
0
查看评论
发表评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场

UNP-基本TCP编程-2(复用技术)

tcp 协议基本信息:选项子段:(tcp/ip : p192) 选项表结束: 无操作: 最大报文长度 窗口扩大因子: TCP连接的建立与中止:– – 连接的建立:22:22:46.510758 IP ...
  • gg782112163
  • gg782112163
  • 2016-08-30 12:42
  • 169

Socket编程实践(1) --TCP/IP简述

ISO的OSI    OSI(open system interconnection)开放系统互联模型是由ISO国际标准化组织定义的网络分层模型,共七层, 从下往上为:OSI七层参考模型物理层(Phy...
  • hanqing280441589
  • hanqing280441589
  • 2015-03-14 11:50
  • 3721

Linux系统编程(34)—— socket编程之TCP服务器与客户端的交互

前面几篇中实现的client每次运行只能从命令行读取一个字符串发给服务器,再从服务器收回来,现在我们把它改成交互式的,不断从终端接受用户输入并和server交互。 /* client.c */ #in...
  • yincheng01
  • yincheng01
  • 2014-09-04 07:49
  • 1328

java实现的基于TCP网络编程步骤

参考 java 程序设计教程(第二版)雍俊海老师编著的书12章 服务器端程序设计模型的建立通常由如下五个步骤组成: (1)在服务器端,首先要创建ServerSocket的实例对象,注册在服务器端进...
  • u012270113
  • u012270113
  • 2014-03-30 23:29
  • 2156

Socket编程:TCP客户端/服务器应用程序

客户机/服务器应用程序模型通常被看作是位于远处的,高功率计算装置,其存储大量数据的业务逻辑。而用户界面在相对便宜的机器上由客户端软件进行处理。这种观点有些模糊,因为任何服务机器的请求可能会被称为服务器...
  • woalger
  • woalger
  • 2016-03-17 18:00
  • 800

socket编程之实现一个简单的TCP通信

一、理解socket1、socket即为套接字,在TCP/IP协议中,“IP地址+TCP或UDP端口号”唯一的标识网络通讯中的一个进程,“IP地址+TCP或UDP端口号”就为socket。 2、在T...
  • qq_33951180
  • qq_33951180
  • 2017-03-29 18:33
  • 1589

UNIX网络编程:socket套接字(TCP与UDP)

套接字简介:套接字是网络编程中的一种通信机制,是支持TCP/IP的网络通信的基本操作单元,可以看做是不同主机之间的进程进行双向通信的端点,简单的说就是通信的两方的一种约定,用套接字中的相关函数来完成通...
  • Dandelion_gong
  • Dandelion_gong
  • 2016-06-07 18:46
  • 2433

raw socket编程实例

由于最经做模拟TCP三次握手的实验,需要用到raw socket编程。 运行第一段代码,发送ip数据报,第二段代码接收ip数据报。需要运行第二段代码,否则将无法接收数据报。 #include #i...
  • nice_wen
  • nice_wen
  • 2016-12-01 00:58
  • 1249

Linux网络编程(2)——采用TCP的基本server的实现

一个基本的C/S服务器模型很简单:         客户端        服务器 简而言之就是客户端跟服务器之间的通话,通话方式一般采用TCP和UDP这两种。 ...
  • sshhiixx
  • sshhiixx
  • 2015-08-10 12:27
  • 2104

UNIX网络编程(一)一个简易的TCP C/S模型(echo sever)

简易的TCP C/S模型实现
  • zero_witty
  • zero_witty
  • 2017-02-08 16:22
  • 340
    我的微信
    个人资料
    • 访问:16156次
    • 积分:859
    • 等级:
    • 排名:千里之外
    • 原创:72篇
    • 转载:4篇
    • 译文:0篇
    • 评论:2条
    我的 QQ
    QQ 邮箱