1. 什么是socket

socket可以看成是用户进程与内核网络协议栈的编程接口。TCP/IP协议的底层部分已经被内核实现了,而应用层是用户需要实现的,这部分程序工作在用户空间。用户空间的程序需要通过套接字来访问内核网络协议栈。

套接口是全双工的通信,它不仅可以用于本机的进程间通信,还可以用于网络上不同主机的进程间通信。

套接字还可以异构系统间进行通信,异构系统指的是在硬件或软件上有所差别的系统,例如安卓系统的手机与windows系统的PC机上都可以实现QQ通信,套接字可以实现在这两个设备上的通信。

2. IPV4套接口地址结构

套接口既然能够连接两个端系统,那它就需要一个地址来标记该端系统,例如两个电话需要电话号码来标记才可以进行拨号。这抽象成套接口的地址结构。IPV4套接口地址结构通常也称为“网际套接字地址结构”,它以sockaddr_in命名,定义在头文件< netinet/in/h >中。


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

说明:

  1. sin_len:整个sockaddr_in结构体的长度,在4.3BSD-Reno版本之前的第一个成员是sin_family。
  2. sin_family:指定带地址家族,在这里必须设置为AF_INET。socket在设计时不仅可以用于TCP/IP协议,它还可以用于其他协议,例如unix域协议,地址家族用于指定该套接字用于哪种协议。AF_INET表示用于IPV4协议。
  3. sin_port:端口号,16位的无符号整数,能够表示到65535。2个字节。
  4. sin_addr: IPV4的地址。4个字节的整数。
  5. sin_zero:暂不使用,一般将其设置为0。

其中,struct in_addr仅仅是一个32位的无符号整数,可以在终端下输入man 7 ip进行查看:

接下来看一下通用的地址结构。上面说过,socket可以用于不同的协议上,通用的地址结构可以用于任何协议的socket编程。


struct sockaddr{ uint8_t sin_len; sa_family sin_family; char sa_data[14]; };

说明:

  1. sin_len:整个sockaddr结构大小
  2. sin_family:指定该地址家族
  3. sa_data:由sin_family决定它的形式

可以看到,在通用地址结构中sa_data是14个字节,而在IPV4的地址结构中,sin_port、sin_addr、sin_zero三个变量加起来也等于14个字节。也即是说,这两种结构是兼容的。

3. 网络字节序

字节序可以分为大端字节序与小端字节序:

  • 大端字节序(Big Endian) :最高有效位存储于最低内存地址处,最低有效位存储于最高地址内存处。
  • 小端字节序(Little Endian):刚好与大端字节序倒过来,最高有效位存于最高内存地址处,最低有效位存储于最低内存地址处。

这样说起来挺抽象,通过一幅图来说明:

上面说过,socket可以用于异构系统之间的通信。而不同的系统采用的字节序可能是不同的,有的系统采用大端字节序,例如Motorola 6800;有的采用小端字节序,如X86。因此,在进行字节传输时,应该同一一个字节序,称为网络字节序。网络字节序采用大端字节序。如果主机A为小端字节序的系统,那么在传输时需要先将小端字节序转换成网络字节序。这需要一些字节序的转换函数。

我们可以编写程序来测试自己的主机是什么字节序:


#include<stdio.h> int main(void) { unsigned int x = 0x12345678; unsigned char *p = (unsigned char*)&x; printf("%0x,%0x,%0x,%0x\n",p[0],p[1],p[2],p[3]); return 0; }

在我的电脑上输出结果为:78,56,34,12. 因此我的主机为小端字节序。

4. 字节序转换函数

如果主机的字节序与网络字节序不同,那么需要进行字节序的转换。下面是一些字节序转换函数:

   # include < arpa/inet.h >

   uint32_t htonl(uint32_t hostlong);

   uint16_t htons(uint16_t hostshort);

   uint32_t ntohl(uint32_t netlong);

   uint16_t ntohs(uint16_t netshort);

说明:h代表host;n代表network;s代表short;l代表long

描述:

  • htonl()函数将无符号整数hostlong从主机字节序转换成网络字节序。
  • htons()函数将无符号短整型hostshort从主机字节序转换成网络字节序。
  • ntohl()函数功能与 htonl()函数相反
  • ntohs()函数功能与htons()函数相反

我们可以进行验证,刚才已经通过程序测试出我的主机是小端字节序,接下来使用函数 htonl()将整数0x12345678转换成网络字节序。


#include<stdio.h> #include <arpa/inet.h> int main(void) { unsigned int x = 0x12345678; unsigned char *p = (unsigned char*)&x; printf("转换前:%0x,%0x,%0x,%0x\n",p[0],p[1],p[2],p[3]); unsigned int y = htonl(x); p = (unsigned char *) &y; printf("转换后:%0x,%0x,%0x,%0x\n",p[0],p[1],p[2],p[3]); return 0; }

结果输出:

转换前:78,56,34,12

转换后:12,34,56,78

5. 地址转换函数

对于IP地址,我们通常采用点分十进制的形式进行直观的认识,而程序更多的时候是处理32位的地址,因此需要有函数在点分十进制与32位地址这两种形式间进行转换。

   # include < sys/socket.h>

   # include < netinet/in.h>

   # include < arpa/inet.h>

   int inet_aton(const char *cp, struct in_addr *inp);

   in_addr_t inet_addr(const char *cp);

   char *inet_ntoa(struct in_addr in);

描述:

  • inet_addr()函数:表示将点分十进制的IP地址转换成32位的ip地址(整数)。
  • inet_ntoa()函数:将32位ip地址(网络字节序)转换成点分十进制的ip之地。

例程:


#include<stdio.h> #include<arpa/inet.h> int main() { unsigned long addr = inet_addr("192.168.0.100");//将点分十进制转换为32bit地址 printf("addr = %u\n",htonl(addr)); struct in_addr ipaddr; ipaddr.s_addr = addr; printf("ipaddr = %s\n",inet_ntoa(ipaddr)); //网络字节序地址转换为点分十>进制 return 0; }

输出:

addr = 3232235620

ipaddr = 192.168.0.100

6. 套接字类型

套接字类型主要有三种:

  1. 流方套接字(SOCK_STREAM):它对应TCP协议,它提供面向连接的、可靠的数据传输服务,数据无差错、无重复的发送,且按发送顺序接收。
  2. 数据报套接字(SOCK_DGREAM):提供无连接服务。不提供无错保证,数据可能丢失或重复,并且接收顺序混乱。
  3. 原始套接字(SOCK_RAW):它提供一种能力,让我们直接跨越传输层,直接对IP层进行数据封装,通过该套接字,我们可以直接将数据封装成IP层能够认识的协议格式。

文章连接:http://www.cnblogs.com/QG-whz/p/5426634.html

Socket编程实践(1) 基本概念的更多相关文章

  1. C# socket编程实践

    C# socket编程实践——支持广播的简单socket服务器   在上篇博客简单理解socket写完之后我就希望写出一个websocket的服务器了,但是一路困难重重,还是从基础开始吧,先搞定C# ...

  2. Socket编程实践(10) --select的限制与poll的使用

    select的限制 用select实现的并发服务器,能达到的并发数一般受两方面限制: 1)一个进程能打开的最大文件描述符限制.这可以通过调整内核参数.可以通过ulimit -n(number)来调整或 ...

  3. Socket编程实践(6) --TCP服务端注意事项

    僵尸进程处理 1)通过忽略SIGCHLD信号,避免僵尸进程 在server端代码中添加 signal(SIGCHLD, SIG_IGN); 2)通过wait/waitpid方法,解决僵尸进程 sign ...

  4. Socket编程实践(6) --TCPNotes服务器

    僵尸进程过程 1)通过忽略SIGCHLD信号,避免僵尸进程 在server端代码中加入 signal(SIGCHLD, SIG_IGN); 2)通过wait/waitpid方法.解决僵尸进程 sign ...

  5. C# socket编程实践——支持广播的简单socket服务器

    在上篇博客简单理解socket写完之后我就希望写出一个websocket的服务器了,但是一路困难重重,还是从基础开始吧,先搞定C# socket编程基本知识,写一个支持广播的简单server/clie ...

  6. Socket编程实践(2) Socket API 与 简单例程

    在本篇文章中,先介绍一下Socket编程的一些API,然后利用这些API实现一个客户端-服务器模型的一个简单通信例程.该例子中,服务器接收到客户端的信息后,将信息重新发送给客户端. socket()函 ...

  7. Socket编程实践(12) --UDP编程基础

    UDP特点 无连接,面向数据报(基于消息,不会粘包)的传输数据服务; 不可靠(可能会丢包, 乱序, 反复), 但因此普通情况下UDP更加高效; UDP客户/服务器模型 UDP-API使用 #inclu ...

  8. Socket编程实践(2) --Socket编程导引

    什么是Socket? Socket可以看成是用户进程与内核网络协议栈的接口(编程接口, 如下图所示), 其不仅可以用于本机进程间通信,可以用于网络上不同主机的进程间通信, 甚至还可以用于异构系统之间的 ...

  9. Socket编程实践(11) --epoll原理与封装

    常用模型的特点 Linux 下设计并发网络程序,有典型的Apache模型(Process Per Connection,PPC), TPC(Thread Per Connection)模型,以及 se ...

随机推荐

  1. JavaScript 字符串操作

    JavaScript 字符串用于存储和处理文本.因此在编写 JS 代码之时她总如影随形,在你处理用户的输入数据的时候,在读取或设置 DOM 对象的属性时,在操作 Cookie 时,在转换各种不同 Da ...

  2. 实现文字自动横移--- jquery尺寸相关函数

    效果图: 一实现文字自动横移 <style type="text/css"> #demo {overflow:scroll;width:740px; } #indemo ...

  3. 用block做事件回调来简化代码,提高开发效率

       我们在自定义view的时候,通常要考虑view的封装复用,所以如何把view的事件回调给Controller就是个需要好好考虑的问题, 一般来说,可选的方式主要有target-action和de ...

  4. [Erlang 0124] Erlang Unicode 两三事 - 补遗

    最近看了Erlang User Conference 2013上patrik分享的BRING UNICODE TO ERLANG!视频,这个分享很好的梳理了Erlang Unicode相关的问题,基本 ...

  5. putty提供的两个文件传输工具PSCP、PSFTP详细介绍

    用 SSH 来传输文件 PuTTY 提供了两个文件传输工具 PSCP (PuTTY Secure Copy client) PSFTP (PuTTY SFTP client) PSCP 通过 SSH ...

  6. 使用vscode访问和修改远程计算机文件

    使用vscode访问和修改远程文件,分三步实现:在远程linux机器上安装rmate:在本地windows上安装openssh:在vscode中安装扩展remote vscode. 1. 在远程lin ...

  7. Hibernate的 Restrictions用法

    方法说明 方法 说明 Restrictions.eq = Restrictions.allEq 利用Map来进行多个等于的限制 Restrictions.gt > Restrictions.ge ...

  8. 04.ubuntu下kvm 命令行安装64位ubuntu报"Couldn't find hvm kernel for Ubuntu tree."的问题

    1.安装ubuntu时使用的virt-install的配置: virt-install \ --name test4 \ --ram 1024 \ --disk path=/data/01_ubunt ...

  9. Ubuntu管理开机启动服务项 -- 图形界面的Boot-up Manager

    有时学习时安装的服务太多,比如mysql.mongodb.redis.apache.nginx等等,它们都是默认开机启动的,如果不想让它们开机启动,用到时再自己手工启动怎么办呢? 使用sysv-rc- ...

  10. [No00009E]几种常见的命名规则

    变量命名规则 必须遵循的命名规则 1.    变量名首字母必须为字母(a-z A-Z),下划线(_),或者美元符号($)开始php编程中所有变量必须以$开始. 2.    变量名只能是字母(a-z A ...