-
socket
函数介绍
函数原型
domain type protocol
errno
示例
函数原型
socket()
函数的原型如下,
这个函数<
/p>
建立一个协议族为
domain
、
协议类型为
type
、
协议编号为
protocol
的
套接
字文件描述符
。
如果函数调用成功,
会返回一个
标识这个套接字的文件描述符
,失败的时候返回
p>
-1
。
#include
#include
int
socket(
int
domain,
int
type,
int
protocol);
1<
/p>
)
domain
函数
socket()
参数
doma
in
用于设置网络通信的域
,
表
1 domain
的值及含义
函数
socket()
根据这个参数选择通信协议的
族。通信协议族在文件
sys/socket.h
中定义。
p>
名称
PF_UNIX,PF_LOCAL
AF_INET,PF_INET
PF_INET6
PF_IPX
PF_NETLINK
含义
本地通信
IPv4
Internet
协议
IPv6
Internet
协议
IPX-
Novell
协议
内核用户界面设备
名称
PF_X25
PF_AX25
PF_ATMPVC
PF_APPLETALK
PF_PACKET
含义
ITU-T X25 /
ISO-8208
协议
Amateur radio AX.25
原始
ATM
PVC
访问
Appletalk
底层包访问
2
)
Type
函数
socket()
的参数
type
用于设置套接字通信的类型
,
表
2
type
的值及含义
主要有
SOCKET_STREAM
(流式套接字)、
SOCK
——
DGRAM
(数据包套接
字)等。
名称
含义
SOCK_STREAM
p>
Tcp
连接,提供序列化的、可靠的、双向连接的字节流。支持带外
数据传输
SOCK_DGRAM
SOCK_SEQPAC
KET
SOCK_RAW
SOCK_RDM
SOCK_PACKET
支持
UDP
连接(无连接状态的消息)
序列化包
,提供一个序列化的、可靠的、双向的基本连接的数据传输通道,
数据长度定常。每次调
用读系统调用时数据需要将全部数据读出
RAW
类型,提供原始网络协议访问
提供可靠的数据报文,不过可能数据会有乱序
这是一个专用类型,不能呢过在通用程序中使用
并
不
是
所有
的协
议
族都实
现
了
这些
协议
类
型,例
如
,
AF_INET
协
议族就
没
有
实现
SOCK_SEQPACKET
协
议类型。
protocol
函数
soc
ket()
的第
3
个参数
protocol
用于制定
某个协议的特定类型,即
type
类型中的某个类
型
。
通常某协议中只有一种特定类型,这样
prot
ocol
参数仅能设置为
0
;
但是有些协议有多种特定的类型,就需要设置这个参数来选
择特定的类型
。
值
EACCES
EAFNOSUPPORT
EINVAL
EMFILE
ENFILE
ENOBUFS/ENOMEM
其他
含义
没有权限建立制定的
domain
的<
/p>
type
的
socket
不支持所给的地址类型
不支持此协议或者协议不可用
进程文件表溢出
已经达到系统允许打开的文件数量,打开文件过多
内存不足。
socket
只有到资源足够或者有进
程释放内存
EPROTONOSUPPORT
制定的协议
type
在
domain
中不
存在
类型为
SOCK_STREAM
的套接字表示一个双向的字节流,与管道类似。流式的套接字在
进行数据收发之前必须已经连接,
连接使用
connect()
函数进行。
一旦连接,
可以使用
read()
或者
write()
函数进行数据的传输。流式通信方式保证数据不会丢失或者重复接收,当数据
在一段时间内任然没有接受完毕,可以将这个连接人为已经死掉。
< br>SOCK_DGRAM
和
SOCK_RAW
这个两种套接字可以使用函数
sendto()
来
发送数据
,
使
1
用
recvfrom()
函数接受数据,
recvfrom()
接受来自制
定
IP
地址的发送方的数据。
SOCK_PACKET
是一种专用的数据包,它直接从设备驱动接受
数据。
errno
函数
socket()
并不总是执行成功,
有可能会出现
错误,
错误的产生有多种原因,
可以
通
过
errno
获得:
表
3
errno
的值及含义
示例
建立一个流式套接字:
int
sock = socket(AF_INET, SOCK_STREAM, 0);
看一下
socket
函数的原型:
S
OCKET PASCAL FAR socket (int af, int type, int
protocol);
典型的调用方式为:
unsigned
int sockSrv = socket(AF_INET, SOCK_STREAM, 0);
是
address
family
的缩写,实际上就是指明域
domain,
p>
这个
af
主要是用来区分是创建
ipv4
的套接字还是
ipv6
< br>的套接字。
当然,
顺便说一下,
如果是在
unix
中,
第一个参
数还可以是
AF_UNIX,
表示这个
socket
既不是
ipv4
的
socket,
< br>也不是
ipv6
的
socket
,
而
是非网络形式的
unix
域
socket,
可以用来进行非网络形式的进程间通信。
在很多嵌入式系
统中,
进程间的通信均是通过非网络形式的
unix
域套接
字来完成的。
不过,
我们要明白,
unix
域套接字的客户端和服务端进行通信时,
客户端和服务端必须位于同一台机器上,
而
且效率比网络套接字更高。
2.
我以前总是以为,
这个
type
值决定了是
tcp
套接字还是
udp
套接字,
p>
其实不是的。
type
值决定的是流套接字还是数据报套接字或者其他。
<
/p>
注意流套接字不一定是
tcp
,
数据报套
接字也不一定是
udp.
3.
protocol
这个值通常为
0,
为
0
的时候是什么意思呢?
意思是,
如果
type
是流套接字,
且
protocol
为
0
,
那么就是就是默认的流套接字
--
-tcp
套接字。
同理,
如果
type
是数据
报套接字,
且
protocol
为
0,
那么就是默认的数据报套接字
---udp
套接字。
返回值其实就是一个无符号整形,
用于标识和索引套接字。
可以通过返回值判断套接字是否
创建成功。
sockaddr_in
中
sin_zero
的意义,以及
sockaddr_in sockaddr
in_addr
区别联系
转自:
/blog/static/88881620289/
struct sockaddr {unsigned short
sa_family; // address family, AF_xxx
char sa_data[14];
// 14
bytes of protocol address};
sa_family
p>
是地址家族,
“AF_xxx”
。
常设为
“AF_INET”
,
< br>代表
Internet
(
TCP
/IP
)
地址族。
< br>sa_data
是协议地址,由
sa_family
p>
决定。若
sa_family=AF_INET
,则
sa_data
是
socka
ddr_in
的
sin_addr
和<
/p>
sin_port
。
换句话说这时
sockaddr
可当作
sockaddr_
in
看。
struct
sockaddr_in {
short int sin_family; //
Address family
unsigned short int
sin_port; // Port number
struct in_addr
sin_addr; // Internet address
unsigned
char sin_zero[8]; // Same size as struct sockaddr
};
struct in_addr {unsigned
long s_addr; // t
hat’s a
32
-bit long, or 4
bytes};[::
备注
1]
si
n_family
意义与
sa_family
< br>同。
sin_port
存储端
口号(使用网络字节顺序)
sin_addr
存储
IP
地址,使用
in_a
ddr
这个数据结构
sin_zer
o
是为了让
sockaddr
与
sockaddr_in
两个数据结构保持大小相同而保留的空字节。
在
in_
addr
结构体中,
s_addr
按照
网络字节顺序存储
IP
地址。
sin_zero
用来将
sockaddr_
in
结构填充到与
struct sockaddr
同样的长度,可以用
bzero()
或
2
memset()
函数将其置为零。
指向
sockaddr_in
的指针和指向
sockaddr
的指针可以相互转换,
这意味着如果一个函数所需参数类型是
sockaddr
类型时,你可以在函数调用的时候将一个
指向
sockaddr_in
的指针转换为指向
sockaddr<
/p>
的指针;或者相反。
想来你是要进行网络编程,使用
socket,
listen, bind
等函数。你只要记住,填值的时候使用
sockaddr_in
结构,而作为函数的参数传入的时候转换成
< br>sockaddr
结构就行了,毕竟都是
16
个字符长。
[::
备注
1]
/*
实际上在
Winsock2.h
中查看
in_addr*/
struct in_addr
{
union {
struct {
u_char s_b1,s_b2,s_b3,s_b4; } S_un_b;
struct { u_short s_w1,s_w2; } S_un_w;
u_long
S_addr;
} S_un;
#define s_addr
S_un.S_addr
/* can be used for most tcp & ip code */
#define s_host
S_un.S_un_b.s_b2/* host on imp */
#define s_net
S_un.S_un_b.s_b1/* network */
#define s_imp
S_un.S_un_w.s_w2 /* imp */
#define s_impno S_un.S_un_b.s_b4 /* imp
# */
#define s_lh
S_un.S_un_b.s_b3/* logical host */
};
socket
编程
——
sockaddr_in
结构体操作
sockaddr
结构体
sockaddr
的缺陷:
sa_data
把目标地址和端口信息混在一起了
struct sockaddr {
unsigned short sa_family;char sa_data[14];
};
sa_family
是通信类型,最常用的值是
sa_data14
字节,包含套接字中的目标地址和端口信息
sockaddr_in
结构体
<
/p>
sockaddr_in
结构体解决了
s
ockaddr
的缺陷,把
port
和
addr
分开储存在两个变量中
struct sockaddr_in {
short int
sin_family;
unsigned short int sin_port;
struct in_addr sin_addr;
struct in_addr { unsigned long s_addr;
}
unsigned char
sin_zero[8];
}
sin_por
t
和
sin_addr
都必须是
NBO
一般可视化的数字都是
HBO
(本机字节顺序)
sin_zero
初始值应该使用函数
bzero()
来
全部置零。
一般采用下面语句
struct sockaddr_in cliaddr;
bzero(&cliaddr,sizeof(cliaddr));
sockaddr_in
结构体变量的基本配置
3
struct
sockaddr_in ina;
bzero(&ina,sizeof(ina));
_family=AF_INET;
_port=htons(23);
_addr.s_addr =
inet_addr(
sockaddr
和
sockaddr_in
的相互关系
一般先把
sockaddr_in
变量
赋值后,强制类型转换后传入用
sockaddr
做参数的函数
sockaddr_in
用于
socket
定义和赋值
sockaddr
用于函数参数
p>
最典型的源、目的节点
socket
定义<
/p>
对于源、目的地址和源、目的地址端口,需要建立两个
socket
变量
cliaddr
绑定源地址和源端口
servaddr
用于
connect
和
sendto
的设定目的地址和目的
端口
struct sockaddr_in
servaddr,cliaddr;
create_socket(char
*server_addr_string,unsigned int server_port)
{
源
socket
< br>赋值
bzero(&cliaddr,sizeof(cliaddr));
_family = AF_INET;
通常
TCP/UDP
协议源地址和端口都是随机的
_addr.s_addr = htons(INADDR_ANY);
_port = htons(0);
目的
socket
p>
赋值
bzero(&servaddr,sizeof(servaddr));
_family = AF_INET;
inet_aton(server_addr_string,&_addr);
_port = htons(server_port);
}
网络字节顺序
(Network
Byte
Order)
NBO
,结构体的
sin_port
和
sin_addr
都必须是
NBO
本机字节顺序
(Host Byte
Order) HBO
一般可视化的数字都是
HBO
NBO
,
HBO
二者转换
inet_addr()
将字符串点数格
式地址转化成无符号长整型
(
unsigned long
s_addr s_addr;
)
inet_aton()
将字符串点数格式地址转化成
NBO
inet_ntoa ()
将
NBO
地址转化成字符串点数格式
htons()
htonl()
ntohs()
ntohl()
常用的是
htons(),inet_addr()
正好对应结构体的端口类型和地址类型
三种给
socket
赋值地址的方法
inet_aton(server_addr_string,&_addr);
_addr.s_addr =
inet_addr(
;
INADD
R_ANY
转不转
NBO
随便
4
_addr.s_addr = htons(INADDR_ANY);
_addr.s_addr = INADDR_ANY;
两种给
socket
赋值端口的方法
#define
MYPORT 3490
_port = htons(MYPORT);
0
(随机端口)转不转
NBO
随便
_port = htons(0);
_port = 0;
htons/l
< br>和
ntohs/l
等数字转换都不能用于地址转换,
p>
因为地址都是点数格式,
所以地址只
能采用
数字
/
字符串转换如
inet_ato
n,inet_ntoa;
唯一可以用于地址转换的
hton
s
是针对
INADDR_ANY
_addr.s_addr = htons(INADDR_ANY)
inet_a
ddr()
与
inet_aton()
的区别
inet_addr()
是返回值型
struct
sockaddr_in ina;
_addr.s_addr =
inet_addr(
inet_aton()
是参数指针型
struct
sockaddr_in ina;
inet_aton(
inet_ntoa
将
NBO
地址
转化成字符串点数格式
参数:结构体变量
.sinaddr
返回值:字符串指针
a1 =
inet_ntoa(_addr);
printf(
address 1:
132.241.5.10
inet_addr()
的缺陷
:
必须对
-1
做检测处理,
inet_addr()
的结果是整型,
发生错误时返回
-1
。
而
_addr.s_addr<
/p>
是
unsigned
long
型。
1
在
long <
/p>
short
显示成
111111111,
和
IP
地址
2
55.255.255.255
相符合!会被误认为广播地址!
SOCKADDR_IN
结构体的作用是:定义
“
地方
”
,宣誓主权
前面我们已经说了
,
套接字也创建了,
“
地方
”
也定义了,下面就需要将
socket
放置在这个
“
地方
p>
”(TCP)
,将他们紧紧地捆绑在一起,用
bind
函数吧
,
我们来看看函数原型:
int
PASCAL FAR bind (SOCKET s, const struct sockaddr
FAR *addr, int namelen);
第一个参数当然是待绑定的套接字啦,第二个参数是标识绑定在哪个
“
地方
”
,
第三个参
数是这个
“
地方
p>
”
的占地大小。
返回值表示绑定操作是否成功,
0
表示
成功,
-1
表示不成功。函数的返回
值千万不要
忽视,上次就被人说了。
一般是这么调用的:
iRet =
bind(sockSrv,(SOCKADDR*)&addrSrv,
sizeof(SOCKADDR)); //
注意强制转换
我们来对比一下文件
I/O
操作和网络
I/O
操作:
打开一个文件后,
便可以对文件进行读写
操作了,
但是,
网络
I/O
实际上有三个步骤来完成这个功能:
1.
打开
/
创建
socket
2.
命名
socket,
我们知道,
socket
名称包含
协议,
ip
地址
,
端口号
这三个要素,
< br>
而
5