Socket编程实践(1) 基本概念
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];
};
说明:
- sin_len:整个sockaddr_in结构体的长度,在4.3BSD-Reno版本之前的第一个成员是sin_family。
- sin_family:指定带地址家族,在这里必须设置为AF_INET。socket在设计时不仅可以用于TCP/IP协议,它还可以用于其他协议,例如unix域协议,地址家族用于指定该套接字用于哪种协议。AF_INET表示用于IPV4协议。
- sin_port:端口号,16位的无符号整数,能够表示到65535。2个字节。
- sin_addr: IPV4的地址。4个字节的整数。
- 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];
};
说明:
- sin_len:整个sockaddr结构大小
- sin_family:指定该地址家族
- 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. 套接字类型
套接字类型主要有三种:
- 流方套接字(SOCK_STREAM):它对应TCP协议,它提供面向连接的、可靠的数据传输服务,数据无差错、无重复的发送,且按发送顺序接收。
- 数据报套接字(SOCK_DGREAM):提供无连接服务。不提供无错保证,数据可能丢失或重复,并且接收顺序混乱。
- 原始套接字(SOCK_RAW):它提供一种能力,让我们直接跨越传输层,直接对IP层进行数据封装,通过该套接字,我们可以直接将数据封装成IP层能够认识的协议格式。
文章连接:http://www.cnblogs.com/QG-whz/p/5426634.html
Socket编程实践(1) 基本概念的更多相关文章
- C# socket编程实践
C# socket编程实践——支持广播的简单socket服务器 在上篇博客简单理解socket写完之后我就希望写出一个websocket的服务器了,但是一路困难重重,还是从基础开始吧,先搞定C# ...
- Socket编程实践(10) --select的限制与poll的使用
select的限制 用select实现的并发服务器,能达到的并发数一般受两方面限制: 1)一个进程能打开的最大文件描述符限制.这可以通过调整内核参数.可以通过ulimit -n(number)来调整或 ...
- Socket编程实践(6) --TCP服务端注意事项
僵尸进程处理 1)通过忽略SIGCHLD信号,避免僵尸进程 在server端代码中添加 signal(SIGCHLD, SIG_IGN); 2)通过wait/waitpid方法,解决僵尸进程 sign ...
- Socket编程实践(6) --TCPNotes服务器
僵尸进程过程 1)通过忽略SIGCHLD信号,避免僵尸进程 在server端代码中加入 signal(SIGCHLD, SIG_IGN); 2)通过wait/waitpid方法.解决僵尸进程 sign ...
- C# socket编程实践——支持广播的简单socket服务器
在上篇博客简单理解socket写完之后我就希望写出一个websocket的服务器了,但是一路困难重重,还是从基础开始吧,先搞定C# socket编程基本知识,写一个支持广播的简单server/clie ...
- Socket编程实践(2) Socket API 与 简单例程
在本篇文章中,先介绍一下Socket编程的一些API,然后利用这些API实现一个客户端-服务器模型的一个简单通信例程.该例子中,服务器接收到客户端的信息后,将信息重新发送给客户端. socket()函 ...
- Socket编程实践(12) --UDP编程基础
UDP特点 无连接,面向数据报(基于消息,不会粘包)的传输数据服务; 不可靠(可能会丢包, 乱序, 反复), 但因此普通情况下UDP更加高效; UDP客户/服务器模型 UDP-API使用 #inclu ...
- Socket编程实践(2) --Socket编程导引
什么是Socket? Socket可以看成是用户进程与内核网络协议栈的接口(编程接口, 如下图所示), 其不仅可以用于本机进程间通信,可以用于网络上不同主机的进程间通信, 甚至还可以用于异构系统之间的 ...
- Socket编程实践(11) --epoll原理与封装
常用模型的特点 Linux 下设计并发网络程序,有典型的Apache模型(Process Per Connection,PPC), TPC(Thread Per Connection)模型,以及 se ...
随机推荐
- ATM-PROGRAM 关于Proprties的问题
public static void turnMoney(String ToAccNo, int money){ d = new Date(); dateStr = noteDate.format(d ...
- PHP HTTP请求
stream_context_create 1.curl仍然是最好的HTTP库,没有之一. 可以解决任何复杂的应用场景中的HTTP 请求2. 文件流式的HTTP请求比较适合处理简单的HTTP POST ...
- 【原】EasyUI ComboGrid 集成分页、按键示例
需求: 1.下拉框下拉时出现表格: 2.表格带分页功能: 3.可以使用向上键.向下键在表格中移动选择行数据: 4.可以使用回车键在表格中选中行数据: 5.在下拉框的文本框中输入内容,能查询表格: 6. ...
- 有了大量微信用户,就不需要App了吗?
小卢同学是我半年前在中关村车库咖啡认识的一个自由创业者,他从北航毕业后在一家IT上市公司只上了1年多的班就辞职创业了,他的创业项目属于国内度假旅游垂直细分领域:积累大量详细的旅游攻略,组成一个个温馨舒 ...
- Android开发学习—— Service 服务
Service运行于后台的一个组件,用来运行适合运行在后台的代码,服务是没有前台界面,可以视为没有界面的activity. 服务可以被手动关闭,不会重启,但是如果被自动关闭,内存充足就会重启. sta ...
- 看看C# 6.0中那些语法糖都干了些什么(中篇)
接着上篇继续扯,其实语法糖也不是什么坏事,第一个就是吃不吃随你,第二个就是最好要知道这些糖在底层都做了些什么,不过有一点 叫眼见为实,这样才能安心的使用,一口气上五楼,不费劲. 一:字符串嵌入值 我想 ...
- 简历生成平台项目开发-STEP2问卷调查结果统计分析
根据之前设计的调查问卷,截止目前为止,一共收到64份问卷结果.一共16题,分别从基本信息.是否对简历制作有需要.对产品期望的特点和建议采纳四个方面设计问题.下面逐题分析问卷结果: 1.您的性别 可以看 ...
- DHtmlx组件获取选中行的某一列对应的值
最近刚刚接触DHtmlx这个js组件,对它还不是太了解,还在学习中,算是记录自己学习该组件的历程吧. 首先xml文件里有一个grid,有对应的checkbox,通过 var selectedId = ...
- JS二分查找
二分法查找,也称折半查找,是一种在有序数组中查找特定元素的搜索算法.查找过程可以分为以下步骤:(1)首先,从有序数组的中间的元素开始搜索,如果该元素正好是目标元素(即要查找的元素),则搜索过程结束,否 ...
- (转)Java中的static关键字解析
转载: http://www.cnblogs.com/dolphin0520/p/3799052.html 一.static关键字的用途 在<Java编程思想>P86页有这样一段话: &q ...