socket的基本操作:

(1)socket()函数:
(2)bind()函数:
(3)listen(),connect()函数;
(4)accept()函数;
(5)socket中的发送与接收函数:
(6)close()函数:
(7)服务器上调用socket函数:
(8)客户端调用socket函数:
(9)IP地址转换函数:inet_pton, inet_ntop, inet_addr:


(1)socket()函数:

1)函数原型:

int socket(int domain, int type, int protocol);

2)分析:

socket函数对应于普通文件的打开操作。普通文件的打开操作返回一个文件描述字,而socket()用于创建一个socket描述符(socket descriptor),它唯一标识一个socket。这个socket描述字跟文件描述字一样,后续的操作都有用到它,把它作为参数,通过它来进行一些读写操作。
正如可以给fopen的传入不同参数值,以打开不同的文件。创建socket的时候,也可以指定不同的参数创建不同的socket描述符。

3)参数:

1.domain:即协议域,又称为协议族(family):

常用的协议族有,AF_INET、AF_INET6、AF_LOCAL(或称AF_UNIX,Unix域socket)。
用来区分是创建ipv4的套接字(AF_INET)还是ipv6的套接字(AF_INET6)。

AF_UNIX, 表示这个socket既不是ipv4的socket, 也不是ipv6的socket, 而是非网络形式的unix域socket, 可以用来进行非网络形式的进程间通信(本地进程间的通信)。


2.type:指定socket类型:
常用的socket类型有,SOCK_STREAM、SOCK_DGRAM、SOCK_RAW、SOCK_PACKET、SOCK_SEQPACKET等。
1.SOCK_STREAM 这个协议是按照顺序的、可靠的、数据完整的基于字节流的连接。这是一个使用最多的socket类型,这个socket是使用TCP来进行传输。
2.SOCK_DGRAM 这个协议是无连接的、固定长度的传输调用。该协议是不可靠的,使用UDP来进行它的连接。
3.SOCK_RAW 这个socket类型提供单一的网络访问,这个socket类型使用ICMP公共协议。(ping、traceroute使用该协议)


3.protocol:公共协议;
常用的公共协议有,IPPROTO_TCP、IPPTOTO_UDP、IPPROTO_SCTP、IPPROTO_TIPC等,它们分别对应TCP传输协议、UDP传输协议、STCP传输协议、TIPC传输协议。

注意:上面的type和protocol不是可以随意组合的,如SOCK_STREAM不可以跟IPPROTO_UDP组合。当protocol为0时,会自动选择type类型对应的默认协议。

4)返回值:
返回的socket描述字,标识套接字。返回值存在于协议族(address family,AF_XXX)空间中,但没有一个具体的地址。如果想要给它赋值一个地址,就必须调用bind()函数,否则就当调用connect()、listen()时系统会自动随机分配一个端口。


(2)bind()函数:

1)作用:

将特定的ip地址,port端口号绑定到socket上;
bind()函数把一个地址族中的特定地址赋给socket。如对应AF_INET、AF_INET6就是把一个ipv4或ipv6地址和端口号组合赋给socket。
将socket与你本机上的一个端口相关联(往往当你在设计服务器端程序时需要调用该函数。随后你就可以在该端口监听服务请求;而客户端一般无须调用该函数)。

2)原型:

int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

3)参数:

1.sockfd:即socket描述字:
它是通过socket()函数创建了,唯一标识一个socket。

addr:一个const struct sockaddr *指针,指向要绑定给sockfd的协议地址,包含有关你的地址的信息:名称、端口和IP 地址。这个地址结构根据地址创建socket时的地址协议族的不同而不同。

如ipv4对应的是:

struct sockaddr_in {
sa_family_t sin_family; /* address family: AF_INET */
in_port_t sin_port; /* port in network byte order */
struct in_addr sin_addr; /* internet address */
//通常设定设定sin_addr为INADDR_ANY(表示任意的意思)
}; /* Internet address. */
/*sin_addr结构体中只有一个唯一的字段s_addr,表示IP地址,该字段是一个整数,一般用函数inet_addr()把字符串形式的IP地址转换成unsigned long型的整数值后再置给s_addr。*/
struct in_addr {
uint32_t s_addr; /* address in network byte order */
};

可以用下面的赋值实现自动获得本机IP地址和随机获取一个没有被占用的端口号:

    my_addr.sin_port = 0;    /* 系统随机选择一个未被使用的端口号 */
my_addr.sin_addr.s_addr = INADDR_ANY; /* 填入本机IP地址 */

注意:
1.服务程序在为其socket绑定IP地址时可以把htonl(INADDR_ANY)置给s_addr,这样做的好处是不论哪个网段上的客户程序都能与该服务程序通信;
2.如果只给运行在多宿主机上的服务程序的socket绑定一个固定的IP地址,那么就只有与该IP地址处于同一个网段上的客户程序才能与该服务程序通信。

ipv6对应的是:

struct sockaddr_in6 {
sa_family_t sin6_family; /* AF_INET6 */
in_port_t sin6_port; /* port number */
uint32_t sin6_flowinfo; /* IPv6 flow information */
struct in6_addr sin6_addr; /* IPv6 address */
uint32_t sin6_scope_id; /* Scope ID (new in 2.4) */
};
struct in6_addr {
unsigned char s6_addr[16]; /* IPv6 address */
};

Unix域对应的是:

#define UNIX_PATH_MAX    108

struct sockaddr_un {
sa_family_t sun_family; /* AF_UNIX */
char sun_path[UNIX_PATH_MAX]; /* pathname */
};

3)addrlen:对应的是地址的长度。

4)通常服务器在启动的时候都会绑定一个众所周知的地址(如ip地址+端口号),用于提供服务,客户就可以通过它来接连服务器;
客户端就不用指定,有系统自动分配一个端口号和自身的ip地址组合。
这就是为什么通常服务器端在listen之前会调用bind(),而客户端就不会调用,而是在connect()时由系统随机生成一个。

5)返回值:
bind()函数在成功被调用时返回0,当bind()函数调用错误的时候,它也是返回–1 作为错误发生的标志。errn 的值为错误代码。小于1024 的所有端口都是保留下来作为系统使用端口的,没有root 权利无法使用。你可以使用1024 以上的任何端口,一直到65535。调用bind()的常见错误是EADDRINUSE,即指定的地址正在使用,主要是指定的端口号被使用了,IP地址可以被多个进程使用,但端口在同一时刻只能被一个进程使用。

6)bind范例:
下面是一个bind函数调用的例子:

    struct sockaddr_in saddr;
memset((void *)&saddr,0,sizeof(saddr));
saddr.sin_family = AF_INET;
saddr.sin_port = htons(8888);
saddr.sin_addr.s_addr = htonl(INADDR_ANY);
//saddr.sin_addr.s_addr = inet_addr("192.168.22.5"); 绑定固定IP
bind(ListenSocket,(struct sockaddr *)&saddr,sizeof(saddr));

(二)网络字节序 与 主机字节序:

(1)主机字节序:
主机字节序就是我们平常说的大端和小端模式:不同的CPU有不同的字节序类型。这些字节序是指整数在内存中保存的顺序,这个叫做主机序。引用标准的Big-Endian和Little-Endian的定义如下:

a) Little-Endian就是低位字节排放在内存的低地址端,高位字节排放在内存的高地址端。
如32位的十六进制数0x12345678存储在4个字节的内存中为:78存储在第一个字节,56存储在第二个字节,34为第三个字节,12为第四个字节。

b) Big-Endian就是高位字节排放在内存的低地址端,低位字节排放在内存的高地址端。
如32位的十六进制数0x12345678存储在4个字节的内存中为:12存储在第一个字节,34存储在第二个字节,56为第三个字节,78为第四个字节。

c)大端小端范例图及其比较:
1.范例图:

2.比较:
采用大端方式 进行数据存放符合人类的正常思维,而采用小端方式进行数据存放利于计算机处理。

(2)网络字节序:
http://blog.csdn.net/legend050709/article/details/39890997

(3)注意:
在将一个地址绑定到socket的时候,请先将主机字节序转换成为网络字节序,而不要假定主机字节序跟网络字节序一样使用的是Big-Endian。由于这个问题曾引发过血案!公司项目代码中由于存在这个问题,导致了很多莫名其妙的问题,所以请谨记对主机字节序不要做任何假定,务必将其转化为网络字节序再赋给socket。

(2.1)htonl():

1)原型:

unsigned long int htonl(unsigned long int hostlong);

2)背景:
计算机数据存储有两种字节优先顺序:高位字节优先和低位字节优先。Internet上数据以高位字节优先顺序在网络上传输,所以对于在内部是以低位字节优先方式存储数据的机器,在Internet上传输数据时就需要进行转换。
几个字节顺序转换函数:
htons()–“Host to Network Short” ; htonl()–“Host to Network Long”
ntohs()–“Network to Host Short” ; ntohl()–“Network to Host Long”
在这里, h表示”host” ,n表示”network”,s 表示”short”,l表示 “long”。


(3)listen()函数:

1)原型:

int listen(int sockfd, int backlog);

2)作用:

调用listen()来监听这个socket,如果客户端这时调用connect()发出连接请求,服务器端就会接收到这个请求。

3)参数:

sockfd 是一个套接字描述符,为要监听的socket描述字。
backlog相应socket可以排队的最大连接个数。
backlog 具体一些是什么意思呢?每一个连入请求都要进入一个连入请求队列,等待listen 的程序调用accept()(accept()函数下面有介绍)函数来接受这个连接。当系统还没有调用accept()函数的时候,如果有很多连接,那么本地能够等待的最大数目就是backlog 的数值。

4)返回值:

listen()如果返回 –1 ,那么说明在listen()的执行过程中发生了错误。

5)注意:

socket()函数创建的socket默认是一个主动类型的,listen函数将socket变为被动类型的,等待客户的连接请求。


(3.1)connect()函数;

1)原型:

int connect (int sockfd, struct sockaddr *serv_addr, int addrlen);

2)参数:
sockfd :套接字文件描述符,由socket()函数返回的,此中为客户端的sockfd。
serv_addr 是一个存储远程计算机的IP 地址和端口信息的结构,一般为服务器的ip与port的结构。
addrlen 应该是sizeof(struct sockaddr)。

3)作用:
客户端通过调用connect函数来建立与TCP服务器的连接。


(4)accept()函数;

0)过程1:
TCP服务器端依次调用socket()、bind()、listen()之后,就会监听指定的socket地址了。TCP客户端依次调用socket()、connect()之后就想TCP服务器发送了一个连接请求。TCP服务器监听到这个请求之后,就会调用accept()函数取接收请求,这样连接就建立好了。之后就可以开始网络I/O操作了,即类同于普通文件的读写I/O操作。

1.0)过程2:
1.有人从很远很远的地方尝试调用connect()来连接你的机器上的某个端口(当然是你已经在listen()的)。
2.他的连接将被listen 加入等待队列等待accept()函数的调用(加入等待队列的最多数目由调用listen()函数的第二个参数backlog 来决定)。
3.你调用accept()函数,告诉他你准备连接。
accept函数将回返回一个新的套接字描述符,这个描述符就代表了这个连接!

1)原型:

int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

2)参数:

sockfd :为服务器的socket描述字;
addr:存储着远程连接过来的计算机的信息(比如远程计算机的IP 地址和端口)。用于返回客户端的协议地址。
addrlen:sizeof(struct sockaddr_in)

3)返回值:

如果accpet成功,那么其返回值是由内核自动生成的一个全新的描述字,代表与返回客户的TCP连接。

4)注意:

1.accept的第一个参数为服务器的socket描述字,是服务器开始调用socket()函数生成的,称为监听socket描述字.
2.accept函数返回的是已连接的socket描述字.
3.一个服务器通常通常仅仅只创建一个监听socket描述字,它在该服务器的生命周期内一直存在。
4.内核为每个由服务器进程接受的客户连接创建了一个已连接socket描述字,当服务器完成了对某个客户的服务,相应的已连接socket描述字就被关闭。


(5)socket中的发送与接收函数:

http://blog.csdn.net/legend050709/article/details/39804275


(6)close()函数:

1)背景:

完成了读写操作就要关闭相应的socket描述字,好比操作完打开的文件要调用fclose关闭打开的文件。

2)原型:

int close(int sockfd);

3)作用:

套接字将不会在允许进行读操作和写操作。任何有关对套接字描述符进行读和写的操作都会接收到一个错误。

4)shutdown():

1.原型:

#include <sys/socket.h>
int shutdown(int sockfd, int how);

2.作用:

允许你进行单向的关闭操作,或是全部禁止掉。

3.参数:

sockfd :是一个你所想关闭的套接字描述符.
how :可以取下面的值:
0 表示不允许以后数据的接收操作;
1 表示不允许以后数据的发送操作;
2 表示和close()一样,不允许以后的任何操作(包括接收,发送数据)

(7)服务器上调用socket函数:

1)顺序:
socket()————bind()————listen()————accept():

调用socket()函数创建一个套接字。
调用bind()函数把自己绑定在一个地址上。
调用listen()函数侦听连接。
调用accept()函数接受所有引入的请求。
调用recv()函数获取引入的信息然后调用send()回答。

(8)客户端调用socket函数:

socket()->connect():

(9)getsockname函数:

1)原型:

int getsockname(int sockfd, struct sockaddr *name, socklen_t *namelen);
int getpeername(int sockfd, struct sockaddr *peeraddr, int *addrlen);

2)作用:

getsockname: 返回本地协议地址:getpeername:返回远程协议地址

当不用bind()或调用bind()没有指定本地协议地址时,可以调用getsockname()来返回内核分配给此连接的本地IP地址和端口号,还可以获得某套接口的协议族。
当一个新的连接建立时,服务器也可以调用getsockname()来获得分配给此连接的本地IP地址。

当一个服务器的子进程调用exec函数启动执行时,只能调用getpeername()函数来获得客户的Ip地址和端口号。

3)参数:

sockfd:需要获取名称的套接字。
name:存放所获取套接字名称的缓冲区。
nemalen:作为入口参数,name指向空间的最大长度。

4)适用情况:

getsockname()函数用于获取一个套接口的名字。它用于一个已捆绑或已连接套接口s,本地地址将被返回。本调用特别适用于如下情况:未调用bind()就调用了connect(),这时唯有getsockname()调用可以获知系统内定的本地地址。在返回时,namelen参数包含了名字的实际字节数。

(9.0)IP地址转换函数: inet_addr:

1)原型:

 in_addr_t inet_addr(const char * strptr);

2)作用:

将字符串IP地址转换为IPv4地址结构in_addr值。


(9)IP地址转换函数:inet_pton:

1)作用:[将”点分十进制” -> “整数”]:

在将IP地址在“点分十进制”和“整数”之间转换。且可以处理能够处理ipv4和ipv6。

2)原型:

int inet_pton(int af, const char *src, void *dst);

//这个函数转换字符串到网络地址,第一个参数af是地址族,转换后存在dst中。

3)说明:

inet_pton是inet_addr的扩展,支持的多地址族有下列:
af = AF_INET
src为指向字符型的地址,即ASCII的地址的首地址(ddd.ddd.ddd.ddd格式的),函数将该地址转换为in_addr的结构体,并复制在*dst中。

af = AF_INET6
src为指向IPV6的地址,函数将该地址转换为in6_addr的结构体,并复制在*dst中。

4)返回值:

如果函数出错将返回一个负值,并将errno设置为EAFNOSUPPORT,如果参数af指定的地址族和src格式不对,函数将返回0。

(10)IP地址转换函数: inet_ntop

1)作用:[将”整数” -> “点分十进制”]

这个函数转换网络二进制结构到ASCII类型的地址,参数的作用和上面相同,只是多了一个参数socklen_t cnt,
//他是所指向缓存区dst的大小,避免溢出,如果缓存区太小无法存储地址的值,则返回一个空指针,并将errno置为ENOSPC。

2)原型:

const char *inet_ntop(int af, const void *src, char *dst, socklen_t cnt);
int inet_pton(int af, const *src, void* dst);

3)范例:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
int main (void)
{
char IPdotdec[20]; // 存放点分十进制IP地址
struct in_addr s; // IPv4地址结构体
// 输入IP地址
printf("Please input IP address: ");
scanf("%s", &IPdotdec);
// 转换
inet_pton(AF_INET, IPdotdec, (void *)&s);
printf("inet_pton: 0x%x\n", s.s_addr); // 注意得到的字节序
// 反转换
inet_ntop(AF_INET, (void *)&s, IPdotdec, 16);
printf("inet_ntop: %s\n", IPdotdec);
}

Socket的基本操作的更多相关文章

  1. socket基本操作

    我们深谙信息交流的价值,那网络中进程之间如何通信,如我们每天打开浏览器浏览网页时,浏览器的进程怎么与web服务器通信的?当你用QQ聊天时,QQ进程怎么与服务器或你好友所在的QQ进程通信?这些都得靠so ...

  2. Linux Socket编程

    “一切皆Socket!” 话虽些许夸张,但是事实也是,现在的网络编程几乎都是用的socket. ——有感于实际编程和开源项目研究. 我们深谙信息交流的价值,那网络中进程之间如何通信,如我们每天打开浏览 ...

  3. Socket通信原理探讨(C++为例)

    一.网络中进程之间如何通信? 本地的进程间通信(IPC)有很多种方式,但可以总结为下面4类: 1.消息传递(管道.FIFO.消息队列) 2.同步(互斥量.条件变量.读写锁.文件和写记录锁.信号量) 3 ...

  4. Linux Socket编程(不限Linux)【转】

    转自:http://www.cnblogs.com/skynet/archive/2010/12/12/1903949.html “一切皆Socket!” 话虽些许夸张,但是事实也是,现在的网络编程几 ...

  5. SOCKet 编程 简介

    “一切皆Socket!” 话虽些许夸张,但是事实也是,现在的网络编程几乎都是用的socket. ——有感于实际编程和开源项目研究. 我们深谙信息交流的价值,那网络中进程之间如何通信,如我们每天打开浏览 ...

  6. Linux Socket编程(不限Linux)

    "一切皆Socket!" 话虽些许夸张,但是事实也是,现在的网络编程几乎都是用的socket. --有感于实际编程和开源项目研究. 我们深谙信息交流的价值,那网络中进程之间如何通信 ...

  7. Linux Socket过程详细解释(包括三次握手建立连接,四次握手断开连接)

    我们深谙信息交流的价值,那网络中进程之间如何通信,如我们每天打开浏览器浏览网页时,浏览器的进程怎么与web 服务器通信的?当你用QQ聊天时,QQ进程怎么与服务器或你好友所在的QQ进程通信?这些都得靠s ...

  8. 网络编程之socket(转)

    “一切皆Socket!” 话虽些许夸张,但是事实也是,现在的网络编程几乎都是用的socket. ——有感于实际编程和开源项目研究. 我们深谙信息交流的价 值,那网络中进程之间如何通信,如我们每天打开浏 ...

  9. socket编程(Linux)

    “一切皆Socket!” 话虽些许夸张,但是事实也是,现在的网络编程几乎都是用的socket. ——有感于实际编程和开源项目研究. 我们深谙信息交流的价值,那网络中进程之间如何通信,如我们每天打开浏览 ...

随机推荐

  1. [iOS笔试600题]二、常识篇(共有72题)

    [B]1.NSObject是一个根类,几乎所有的类都是从它派生而来.但是根类并不拥有真它类都有的alloc和init方法?[判断题] A. 正确 B. 错误 [A]2. UIResponder可以让继 ...

  2. Win10内部更新:警告用户别用chrome和Firefox

    简评:别和 Chrome 和 Firefox 约行不,我 Edge 明明更美.屁股更翘.更性感... 微软正在测试 Windows 10 的一个更新:警告用户不要安装 Chrome 和 Firefox ...

  3. springMVC请求注解@RequestMapping各个属性值

    最近遇到了一个采用fastJson传输数据的方式,搞了半天,总是感觉模糊,觉得自己有必要在这里做一个系统的总结,今天先从@RequestMapping的属性开始,采用REST 风格的 URL 请求,R ...

  4. 总结day23 ---- 网络编程,以及计算机基础概念

    计算机网络的发展及基础网络概念 问题:网络到底是什么?计算机之间是如何通信的? 早期 : 联机 以太网 : 局域网与交换机 广播 主机之间“一对所有”的通讯模式,网络对其中每一台主机发出的信号都进行无 ...

  5. day03 --class --homework

    '''# >>>>>>2 :,有字符串s = "123a4b5c"#>>>>>^ 1: # 1)通过对s切片形成新 ...

  6. Azure Powershell部署使用平台映像的托管Windows VM及相关问题说明

    1.脚本背景信息: a.使用平台镜像(Windows Server 2016 zh-cn)部署高性能托管磁盘虚拟机 b.虚拟机默认不开启Boot诊断 c.添加三块已经创建好的数据磁盘 d.添加已创建好 ...

  7. 采用prometheus 监控mysql

    1. prometheus 是什么 开源的系统监控和报警工具,监控项目的流量.内存量.负载量等实时数据. 它通过直接或短时jobs中介收集监控数据,在本地存储所有收集到的数据,并且通过定义好的rule ...

  8. [原创] PHP 使用Redis实现锁

    目录 锁实现的注意点 加锁 connect 与 pconnect 解锁 Redis 中使用 Lua 脚本的注意点 Redis集群分布式锁 RedLock 算法 锁实现的注意点 互斥: 任意时刻, 只能 ...

  9. MyBatis学习笔记(一)创建第一个MyBatis项目

    一.新建Maven项目 http://www.mybatis.org/mybatis-3/zh/index.html 该链接为MyBatis官方地址 创建MyBatis项目主要有两种办法,一种是导入j ...

  10. android samsung note3  device not found

    descriptiong : android  samsung note3 device not found solution: usb link by PTP, DONE! (be curiosly ...