套接字编程简介

简介:

套接字编程简介

项目:UNIX网络编程学习

作者:曾金龙

供职:(深圳迅雷网络技术股份有限公司)

领域:迅雷下载库研发

日期:2014-07-25


1, TCP连接图

socket编程,过眼烟云的去看,无外乎就那么几个API,但是,如果想登堂入室,必须注重里面的每一个细节。


对于TCP编程而言,最重要的是记住这么一幅图。死记的基础上理解。

windows下有visio,ubuntu下只好用Dia,不是很习惯,而且不支持中文输入。


图1 TCP连接的数据包交换图

你特别需要注意的是:

1)哪个地方会被阻塞住,例如读取数据的时候,如果对方没有写数据,则会被阻塞,对方要是关闭了连接则会读取到0(EOF);

2)在每一个阶段如果顺序发生了变化,将会是怎么样。例如,我们总是习惯对方写,我们读,然后我们写,对方读,如果不读怎么样?或者一直写。write是很少会被阻塞的,除非写满了了内核的缓冲区,free空间比它的下限还要小。当然一旦free空间大于其下限,Epoll等待的时候是会被唤醒的,EPOLLOUT,表示可以写。

3)如果彼此在不同的时机宕机了,脱离网络了,拒绝访问了,关闭了等,又将会发


生什么?会有一些是TIME_OUT,一些是会受到RST,一些是受到FIN。这个就是TCP的细节。而细节决定成败。

在迅雷的网络模组中,已经把socket打包在公共库里面,配合异步框架使用。前辈们的代码还是很优秀的。特别是用select ,poll,epoll,配合signal,pipe,mutex等这些实现的异步框架,是迅雷架构里面最优美的代码之一。(迅雷用C实现C++的代码也值得细品)


2 地址结构

编程要用到的地址结构就是这个,再在用socket函数的时候强制类型转换成struct sockaddr

struct in_addr{in_addr_t    s_addr;};

struct sockaddr_in{
uint8_t    sin_len;
sa_family_t    sin_family;
in_port_t    sin_addr;
char    sin_zero[8];
};

struct sockaddr{
uint8_t    sa_len;
sa_family_t sa_family;
char sa_data[14];
};

其他的一般还用不到,用到再查阅。

对了,有些博客你会发现它们写的结构不太一样,就是没有开头的sin_len和sa_len,这个问题在书中有说过,为了兼容了ISO,后来在BSD4.3的时候加入的,其实,作为一个数据结构,在头部加一个本结构的长度,就实现了该结构可变长。

3 主机--网络字节顺序问题

不同的主机它存在大小端问题,所以在网络中交换的数据,必须处理大小端不一致的问题。

3.1)如何检测自己的机子是大端还是小端


书中定义了一个union联合体,其实,不用。

BOOL isBigEndian()
{
     short  v=0x0102;
     char* c=(char*)&v;
     if(c[0]==0x01&&c[1]==0x02)return TRUE;
     else return FALSE;
}

3.2)主机字节顺序和网络顺序之间的转换函数

host to network short(long)
network to host short(long)
就这四个函数。
#include <netinet/in.h>
uinit16_t htons(uint16_t host16bitvalue);
uinit32_t htonl(uint32_t host32bitvalue);
uinit16_t ntohs(uint16_t nett16bitvalue);
uinit32_t ntohl(uint32_t net32bitvalue);

3.2)内存操作函数

bxx和memxx的对比,特别是拷贝函数。我们用实验要验证bcopy和memcpy对重叠内存的处理。

#include <strings.h>
void bzero(void* dest,size_t nbytes);
void bcopy(const void* src ,void* dest,size_t nbytes);
int bcmp(const void *ptr1,const void *ptr2,size_t nbytes);

#include <string.h>
void* memset(void* dst, int c, size_t len);
void* memcpy(void* dst,const void* src,size_t nbytes);
int memcpy(const void* ptr1,const void* ptr2,size_t nbytes);

下面我们通过测试代码一看究竟,bcopy和memcpy的区别。
/*author:ZengJinlong ,xunlei*/
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <string.h>

int main()
{
    int a=0x01020304;
    printf("int size:%d\n",sizeof(int));

    char* p_a=&a;
    printf("a adress:%p,p_a:%x\n",&a,p_a);
    if(p_a==&a)
    {
        printf("equal\n");
    }

   printf("%2x,%2x,%2x,%2x\n",p_a[0],p_a[1],p_a[2],p_a[3]);

    if(p_a[0]==0x01&&p_a[1]==0x02)printf("Big-Endian\n");
    else printf("Little-Endian\n");

    printf("case 1\n");
    char* p1=p_a+2;
    printf("before bcopy:%2x,%2x,%2x,%2x,%2x,%2x\n",p_a[0],p_a[1],p_a[2],p_a[3],p_a[4],p_a[5]);
    printf("bcopy 0 to 2 ,2 size\n");
    bcopy(p_a,p1,2);
    printf("0x%x\n",(int)*p1);
    printf("after bcopy:%2x,%2x,%2x,%2x,%2x,%2x\n",p_a[0],p_a[1],p_a[2],p_a[3],p_a[4],p_a[5]);

    printf("case 2\n");
    a=0x01020304;
    p1=p_a+1;
    printf("before bcopy:%2x,%2x,%2x,%2x,%2x,%2x\n",p_a[0],p_a[1],p_a[2],p_a[3],p_a[4],p_a[5]);
    printf("bcopy 0 to 1, 2 size\n");
    bcopy(p_a,p1,2);
    printf("0x%x\n",(int)*p1);
    printf("after bcopy:%2x,%2x,%2x,%2x,%2x,%2x\n",p_a[0],p_a[1],p_a[2],p_a[3],p_a[4],p_a[5]);

    printf("case 3\n");
    a=0x01020304;
    printf("before memcpy:%2x,%2x,%2x,%2x,%2x,%2x\n",p_a[0],p_a[1],p_a[2],p_a[3],p_a[4],p_a[5]);
    printf("memcpy 0 to 2, 2 size\n");
    char* p2=p_a+2;
    memcpy(p2,p_a,2);
    printf("after memcpy:%2x,%2x,%2x,%2x,%2x,%2x\n",p_a[0],p_a[1],p_a[2],p_a[3],p_a[4],p_a[5]);
    printf("0x%x\n",(int)*p2);

    printf("case 4\n");
    a=0x01020304;
    printf("before memcpy:%2x,%2x,%2x,%2x,%2x,%2x\n",p_a[0],p_a[1],p_a[2],p_a[3],p_a[4],p_a[5]);
    printf("0x%x\n",a);
    p2=p_a+1;
    printf("memcpy 0 to 1 ,2 size\n");
    memcpy(p2,p_a,2);
    printf("after memcpy:%2x,%2x,%2x,%2x,%2x,%2x\n",p_a[0],p_a[1],p_a[2],p_a[3],p_a[4],p_a[5]);
    printf("0x%x\n",(int)*p2);

    return 0;
}

输出结果为如图所示,可以看出bcopy是可以正确处理内存重叠时候的拷贝,而memcpy则不能。想想,bcopy是如何实现的呢?memcpy很简单,我们就不再多说。bcopy的代码,我个人有个实现方案,很简单。


图 bcopy 和 memcpy的拷贝效果对比

bcopy实现原理,计算重叠了多少,然后先拷贝重叠部分,回过头再拷贝前面的部分。
实现代码:
void bcopy_new(const void* src,void* dest,size_t nbytes)
{
    if(dest>src && ((unsigned)src+ nbytes > (unsigned)dest))
    {
        size_t gap=(unsigned int)dest - (unsigned int)src;
        size_t overlap = nbytes-gap;
        int i=0;
        while(i<overlap)
        {
            *((char*)dest+gap+i)=*((char*)dest+i);
            ++i;
        }
        i=0;
        while(gap--)*((char*)dest++)= *((char*)src++);
        return;
    }
    while(nbytes--)*((char*)dest++)= *((char*)src++);
    return;
}

将bcopy_new替换掉上面bocpy的例子,可以得出和bcopy一样的结果。
3.3)IP地址转换函数
由点分格式转换成网络字节码以及反过来。记住两个即可。

#include <arpa/inet.h>
int inet_pton(int familyconst char* strptr,void* addrptr);
const char* inet_ntop(int family,const void* addrptr,char* strptr,size_t len);




相关文章
|
Linux 开发工具 Windows
设备接入--海康摄像头SDK
springboot-对接海康摄像头,兼容window和Linux环境
5750 3
设备接入--海康摄像头SDK
|
安全 大数据
数据集不是“越多越好”:微调里最容易被误解的一件事
微调中数据非“越多越好”,而是“越清楚越好”。它本质是约束而非燃料:重目标一致性、表达稳定性与边界清晰度,而非规模。小而精的数据更易定位问题、验证假设;盲目扩量反致模型平均化、难调试、掩盖目标缺陷。关键在明确“教模型什么”,而非堆砌数量。
|
存储 XML Java
Flowable工作流-高级篇
Flowable工作流-高级篇
10212 1
|
6月前
|
数据采集 人工智能 搜索推荐
AI搜索时代的权衡:Geo优化中哪些是核心基石,哪些是次要干扰?
在生成式AI重塑搜索的今天,SEO正加速演进为GEO(生成式引擎优化)。本文基于麦肯锡2025报告与于磊专家实践,提出“两大核心”(人性化Geo、内容交叉验证)与“四轮驱动”(EEAT、结构化内容、关键词兼容、精准引用)方法论,厘清必须做与不必急做的关键边界,助力企业抢占AI搜索新入口。
296 2
|
18天前
|
人工智能 运维 安全
2026 软件定义广域网走向何方?AI 原生网络与 SD-WAN 的技术融合趋势
本文聚焦2026年SD-WAN发展趋势,剖析AI原生网络如何重塑广域网架构:从MPLS替代迈向SASE入口,融合智能选路、AIOps、零信任与多云统一策略,助力企业实现“连接→智能→原生”的演进跃迁。(239字)
134 0
|
关系型数据库 MySQL 数据库
零基础带你学习MySQL—备份恢复数据库(三)
零基础带你学习MySQL—备份恢复数据库(三)
|
缓存 监控 数据处理
【编程底层原理】从播放音乐的网页中提取mp3音频文件的两种方式及背后的技术思考【短连接和长连接】
本文介绍了两种从网页提取音乐文件的方法:一是通过IE临时缓存获取,二是利用开发者模式捕捉网络流量并下载音频URL。同时探讨了网页播放音乐的技术实现,包括短连接和长连接的区别及其适用场景,以及数据传输中的阻塞概念。
5662 0
|
安全 C# 索引
C#一分钟浅谈:属性与索引器的定义
本文深入浅出地介绍了C#编程中的属性和索引器。属性让字段更安全,通过访问器方法在读写时执行额外操作,如验证数据有效性;索引器则赋予类数组般的访问方式,支持基于索引的数据访问模式。文章通过示例代码展示了如何定义及使用这两种特性,并提供了常见问题及其解决方案,帮助读者写出更健壮、易维护的代码。希望读者能从中学习到如何有效利用属性和索引器增强C#类的功能性。
474 12
|
物联网 数据管理
HarmonyOS学习路之开发篇—网络与连接(蓝牙开发 一)
蓝牙是短距离无线通信的一种方式,支持蓝牙的两个设备必须配对后才能通信。HarmonyOS蓝牙主要分为传统蓝牙和低功耗蓝牙(通常称为BLE,Bluetooth Low Energy)。传统蓝牙指的是蓝牙版本3.0以下的蓝牙,低功耗蓝牙指的是蓝牙版本4.0以上的蓝牙。
|
JSON Kubernetes Cloud Native
在k8S中,CNI模型概念是什么?
在k8S中,CNI模型概念是什么?

热门文章

最新文章