伯克利套接字
----------维基百科,自由的百科全书
伯克利套接字(Berkeley sockets),也称为BSD Socket。伯克利套接字的应用编程接口(API)是采用C语言的进程间通信的库,经常用在计算机网络间的通信。 BSD Socket的应用编程接口已经是网络套接字的事实上的抽象标准。大多数其他程序语言使用一种相似的编程接口。
BSD Socket作为一种API,允许不同主机或者同一个计算机上的不同进程之间的通信。它支持多种I/O设备和驱动,但是具体的实现是依赖操作系统的。这种接口对于TCP/IP是必不可少的,所以是互联网的基础技术之一。它最初是由加州伯克利大学为Unix系统开发出来的。所有现代的操作系统都都实现了伯克利套接字接口,因为它已经是连接互联网的标准接口了。
目录
|
使用伯克利套接字的系统
由于伯克利套接字是第一个socket,大多数程序员很熟悉它们,所以大量系统把伯克利套接字作为其主要的网络API。 一个不完整的列表如下:
- Windows Sockets (Winsock) ,和Berkeley Sockets很相似,最初是为了便于移植Unix程序。
- Java Sockets
- Python sockets
- 头文件
主要的头文件如下,不同的系统可能具体不同。
<sys/socket.h> BSD socket 核心函数和数据结构。
<netinet/in.h> AF_INET 和AF_INET6 地址家族和他们对应的协议家族 PF_INET 和 PF_INET6。在互联网编程中广泛使用,包括IP地址以及TCP和UDP端口号。
<sys/un.h> PF_UNIX/PF_LOCAL 地址家族。用于运行在一台计算机上的程序间的本地通信,不用在网络中。
<arpa/inet.h> 和IP地址相关的一些函数。
<netdb.h> 把协议名和主机名转化成数字的一些函数。
API函数
这些是伯克利套接字提供的库函数。
-
socket()
创造某种类型的套接字,分配一些系统资源,用返回的整数识别。 -
bind()
一般是用在服务器这边,和一个套接字地址结构相连,比如说是一个特定的本地端口号和一个IP地址。 -
listen()
用在服务器一边,导致一个绑定的TCP套接字进入监听状态。 -
connect()
用在客户机这边,给套接字分配一个空闲的端口号。比如说一个TCP套接字,它会试图建立一个新的TCP连接。 -
accept()
用在服务器这边。从客户机那接受请求试图创造一个新的TCP连接,并把一个套接字和这个连接相联系起来。 -
send()
andrecv()
, orwrite()
andread()
, orsendto()
andrecvfrom()
用来接收和发送数据。 -
close()
关闭连接,系统释放资源。 -
gethostbyname()
andgethostbyaddr()
用来解析主机名和地址。 -
select()
is used to prune a provided list of sockets for those that are ready to read, ready to write, or that have errors. -
poll()
is used to check on the state of a socket in a set of sockets. The set can be tested to see if any socket can be written to, read from or if an error occurred. -
getsockopt()
is used to retrieve the current value of a particular socket option for the specified socket. -
setsockopt()
is used to set a particular socket option for the specified socket.
更多的细节在下面。
socket()
socket()
为通信创造一个端点并返回一个文件描述符。 socket()
由三个参数:
- domain, 确定协议族。例如:
- type, 是下面中的一个:
-
SOCK_STREAM
(可靠的面向连接的服务或者 Stream Sockets) -
SOCK_DGRAM
(数据包服务或者 Datagram Sockets) -
SOCK_SEQPACKET
(可靠的有序的分组服务),或者 -
SOCK_RAW
(网络层的原始协议)。
- protocol 确定实际使用的运输层。最常见的是
IPPROTO_TCP
,IPPROTO_SCTP
,IPPROTO_UDP
,IPPROTO_DCCP
。这些协议是在<netinet/in.h>中定义的。如果domain
和type
已经确定,“0
” 可以用来选择一个默认的协议。
如果出错返回-1,否则返回一个代表文件描述符的整数。
函数原型
<span class="kw4">int</span> socket<span class="br0">(</span><span class="kw4">int</span> domain<span class="sy0">,</span> <span class="kw4">int</span> type<span class="sy0">,</span> <span class="kw4">int</span> protocol<span class="br0">)</span><span class="sy0">;</span>
bind()
bind()
给套接字分配一个地址。当使用 socket()
创造一个套接字时, 只是给定了协议族,并没有分配地址。在套接字能够接受来自其他主机的连接前,必须用bind()给它绑定一个地址。 bind()
由三个参数:
-
sockfd
, 代表socket的文件描述符。 -
my_addr
, 指向sockaddr
结构体的指针,代表要绑定的地址 。 -
addrlen
, 是sockaddr结构体的大小。
Bind()返回0表示成功,错误返回-1。
函数原型
<span class="kw4">int</span> bind<span class="br0">(</span><span class="kw4">int</span> sockfd<span class="sy0">,</span> <span class="kw4">const</span> <span class="kw4">struct</span> sockaddr <span class="sy0">*</span>my_addr<span class="sy0">,</span> socklen_t addrlen<span class="br0">)</span><span class="sy0">;</span>
listen()
一旦一个套接字和一个地址联系之后,listen()
监听到来的连接。但是这只适用于对面向连接的模式,例如 套接字类型是 (SOCK_STREAM
, SOCK_SEQPACKET
)。listen()需要两个参数:
-
sockfd
,一个有效的套接字描述符。 -
backlog
,一个整数,表示一次能够等待的最大连接数目。操作系统通常会对这个值设置上限。
一旦连接被接受,返回0表示成功,错误返回-1。
函数原型:
<span class="kw4">int</span> listen<span class="br0">(</span><span class="kw4">int</span> sockfd<span class="sy0">,</span> <span class="kw4">int</span> backlog<span class="br0">)</span><span class="sy0">;</span>
accept()
当应用程序监听来自其他主机的面对数据流的连接时,通过事件(比如Unix select()系统调用)通知它。必须用accept()
函数初始化连接。 Accept() 为每个连接创立新的套接字并从监听队列中移除这个连接。它使用如下参数:
-
sockfd
,监听的套接字描述符 -
cliaddr
, 指向sockaddr 结构体的指针,客户机地址信息。 -
addrlen
,指向socklen_t
的指针,确定客户机地址结构体的大小 。
返回新的套接字描述符,出错返回-1。进一步的通信必须通过这个套接字。
Datagram 套接字不要求用accept()处理,因为接收方可能用监听套接字立即处理这个请求。
函数原型:
<span class="kw4">int</span> accept<span class="br0">(</span><span class="kw4">int</span> sockfd<span class="sy0">,</span> <span class="kw4">struct</span> sockaddr <span class="sy0">*</span>cliaddr<span class="sy0">,</span> socklen_t <span class="sy0">*</span>addrlen<span class="br0">)</span><span class="sy0">;</span>
connect()
connect()
系统调用为一个套接字设置连接,参数有文件描述符和主机地址。
某些类型的套接字是无连接的,大多数是UDP协议。对于这些套接字,连接时这样的:默认发送和接收数据的主机由给定的地址确定,可以使用 send()和 recv()。 返回-1表示出错,0表示成功。
函数原型:
<span class="kw4">int</span> connect<span class="br0">(</span><span class="kw4">int</span> sockfd<span class="sy0">,</span> <span class="kw4">const</span> <span class="kw4">struct</span> sockaddr <span class="sy0">*</span>serv_addr<span class="sy0">,</span> socklen_t addrlen<span class="br0">)</span><span class="sy0">;</span>
gethostbyname() 和 gethostbyaddr()
gethostbyname()
和 gethostbyaddr()
函数是用来解析主机名和地址的。可能会使用DNS服务或者本地主机上的其他解析机制(例如查询/etc/hosts)。返回一个指向 struct hostent的指针,这个结构体描述一个IP主机。函数使用如下参数:
- name
- addr 指向 struct in_addr的指针,包含主机的地址。
- len 给出 addr的长度,以字节为单位。
- type
出错返回NULL指针,可以通过检查 h_errno 来确定是临时错误还是未知主机。正确则返回一个有效的 struct hostent *。
这些函数并不是伯克利套接字严格的组成部分。这些函数可能是过时了,新函数是 getaddrinfo() and getnameinfo(), 这些新函数是基于addrinfo数据结构。
函数原型:
<span class="kw4">struct</span> hostent <span class="sy0">*</span>gethostbyname<span class="br0">(</span><span class="kw4">const</span> <span class="kw4">char</span> <span class="sy0">*</span>name<span class="br0">)</span><span class="sy0">;</span>
<span class="kw4">struct</span> hostent <span class="sy0">*</span>gethostbyaddr<span class="br0">(</span><span class="kw4">const</span> <span class="kw4">void</span> <span class="sy0">*</span>addr<span class="sy0">,</span> <span class="kw4">int</span> len<span class="sy0">,</span> <span class="kw4">int</span> type<span class="br0">)</span><span class="sy0">;</span>
协议和地址
套接字API是Unix网络的通用接口,允许使用各种网络协议和地址。
下面列出了一些例子,在现在的 Linux 和BSD中一般都已经实现了。
PF_LOCAL, PF_UNIX, PF_FILE
Local to host (pipes and file-domain)
PF_INET IP protocol family
PF_AX25 Amateur Radio AX.25
PF_IPX Novell Internet Protocol
PF_APPLETALK Appletalk DDP
PF_NETROM Amateur radio NetROM
PF_BRIDGE Multiprotocol bridge
PF_ATMPVC ATM PVCs
PF_X25 Reserved for X.25 project
PF_INET6 IP version 6
PF_ROSE Amateur Radio X.25 PLP
PF_DECnet Reserved for DECnet project
PF_NETBEUI Reserved for 802.2LLC project
PF_SECURITY Security callback pseudo AF
PF_KEY PF_KEY key management API
PF_NETLINK, PF_ROUTE
routing API
PF_PACKET Packet family
PF_ASH Ash
PF_ECONET Acorn Econet
PF_ATMSVC ATM SVCs
PF_SNA Linux SNA Project
PF_IRDA IRDA sockets
PF_PPPOX PPPoX sockets
PF_WANPIPE Wanpipe API sockets
PF_BLUETOOTH Bluetooth sockets
使用TCP的服务器客户机举例
服务器
设置一个简单的TCP服务器涉及下列步骤:
- 调用
socket()
建立套接字 - 调用Binding 把套接字绑定到一个监听端口上。在调用
bind()
之前, 程序必须声明一个sockaddr_in
结构体,用memset()清除, and the sin_family (AF_INET), and fill its sin_port (the listening port, innetwork byte order) fields. Converting a short int to network byte order can be done by calling the function htons()
- Preparing the socket to listen for connections (making it a listening socket), with a call to
listen()
. - Accepting incoming connections, via a call to
accept()
. This blocks until an incoming connection is received, and then returns a socket descriptor for the accepted connection. The initial descriptor remains a listening descriptor, andaccept()
can be called again at any time with this socket, until it is closed. - Communicating with the remote host, which can be done through
send()
andrecv()
orwrite()
andread()
. - Eventually closing each socket that was opened, once it is no longer needed, using
close()
. Note that if there were any calls tofork()
, each process must close the sockets it knew about (the kernel keeps track of how many processes have a descriptor open), and two processes should not use the same socket at once.
<span class="coMULTI">/* Server code in C */</span>
<span class="co2">#include <sys/types.h></span>
<span class="co2">#include <sys/socket.h></span>
<span class="co2">#include <netinet/in.h></span>
<span class="co2">#include <arpa/inet.h></span>
<span class="co2">#include <stdio.h></span>
<span class="co2">#include <stdlib.h></span>
<span class="co2">#include <string.h></span>
<span class="co2">#include <unistd.h></span>
<span class="kw4">int</span> main<span class="br0">(</span><span class="kw4">void</span><span class="br0">)</span>
<span class="br0">{</span>
<span class="kw4">struct</span> sockaddr_in stSockAddr<span class="sy0">;</span>
<span class="kw4">int</span> SocketFD <span class="sy0">=</span> socket<span class="br0">(</span>PF_INET<span class="sy0">,</span> SOCK_STREAM<span class="sy0">,</span> IPPROTO_TCP<span class="br0">)</span><span class="sy0">;</span>
<span class="kw1">if</span><span class="br0">(</span><span class="sy0">-</span><span class="nu0">1</span> <span class="sy0">==</span> SocketFD<span class="br0">)</span>
<span class="br0">{</span>
<span class="kw3">perror</span><span class="br0">(</span><span class="st0">"can not create socket"</span><span class="br0">)</span><span class="sy0">;</span>
<span class="kw3">exit</span><span class="br0">(</span>EXIT_FAILURE<span class="br0">)</span><span class="sy0">;</span>
<span class="br0">}</span>
<span class="kw3">memset</span><span class="br0">(</span><span class="sy0">&</span>stSockAddr<span class="sy0">,</span> <span class="nu0">0</span><span class="sy0">,</span> <span class="kw4">sizeof</span><span class="br0">(</span><span class="kw4">struct</span> sockaddr_in<span class="br0">)</span><span class="br0">)</span><span class="sy0">;</span>
stSockAddr.<span class="me1">sin_family</span> <span class="sy0">=</span> AF_INET<span class="sy0">;</span>
stSockAddr.<span class="me1">sin_port</span> <span class="sy0">=</span> htons<span class="br0">(</span><span class="nu0">1100</span><span class="br0">)</span><span class="sy0">;</span>
stSockAddr.<span class="me1">sin_addr</span>.<span class="me1">s_addr</span> <span class="sy0">=</span> INADDR_ANY<span class="sy0">;</span>
<span class="kw1">if</span><span class="br0">(</span><span class="sy0">-</span><span class="nu0">1</span> <span class="sy0">==</span> bind<span class="br0">(</span>SocketFD<span class="sy0">,</span><span class="br0">(</span><span class="kw4">const</span> <span class="kw4">struct</span> sockaddr <span class="sy0">*</span><span class="br0">)</span><span class="sy0">&</span>stSockAddr<span class="sy0">,</span> <span class="kw4">sizeof</span><span class="br0">(</span><span class="kw4">struct</span> sockaddr_in<span class="br0">)</span><span class="br0">)</span><span class="br0">)</span>
<span class="br0">{</span>
<span class="kw3">perror</span><span class="br0">(</span><span class="st0">"error bind failed"</span><span class="br0">)</span><span class="sy0">;</span>
close<span class="br0">(</span>SocketFD<span class="br0">)</span><span class="sy0">;</span>
<span class="kw3">exit</span><span class="br0">(</span>EXIT_FAILURE<span class="br0">)</span><span class="sy0">;</span>
<span class="br0">}</span>
<span class="kw1">if</span><span class="br0">(</span><span class="sy0">-</span><span class="nu0">1</span> <span class="sy0">==</span> listen<span class="br0">(</span>SocketFD<span class="sy0">,</span> <span class="nu0">10</span><span class="br0">)</span><span class="br0">)</span>
<span class="br0">{</span>
<span class="kw3">perror</span><span class="br0">(</span><span class="st0">"error listen failed"</span><span class="br0">)</span><span class="sy0">;</span>
close<span class="br0">(</span>SocketFD<span class="br0">)</span><span class="sy0">;</span>
<span class="kw3">exit</span><span class="br0">(</span>EXIT_FAILURE<span class="br0">)</span><span class="sy0">;</span>
<span class="br0">}</span>
<span class="kw1">for</span><span class="br0">(</span><span class="sy0">;;</span><span class="br0">)</span>
<span class="br0">{</span>
<span class="kw4">int</span> ConnectFD <span class="sy0">=</span> accept<span class="br0">(</span>SocketFD<span class="sy0">,</span> NULL<span class="sy0">,</span> NULL<span class="br0">)</span><span class="sy0">;</span>
<span class="kw1">if</span><span class="br0">(</span><span class="nu0">0</span> <span class="sy0">></span> ConnectFD<span class="br0">)</span>
<span class="br0">{</span>
<span class="kw3">perror</span><span class="br0">(</span><span class="st0">"error accept failed"</span><span class="br0">)</span><span class="sy0">;</span>
close<span class="br0">(</span>SocketFD<span class="br0">)</span><span class="sy0">;</span>
<span class="kw3">exit</span><span class="br0">(</span>EXIT_FAILURE<span class="br0">)</span><span class="sy0">;</span>
<span class="br0">}</span>
<span class="coMULTI">/* perform read write operations ... */</span>
shutdown<span class="br0">(</span>ConnectFD<span class="sy0">,</span> SHUT_RDWR<span class="br0">)</span><span class="sy0">;</span>
close<span class="br0">(</span>ConnectFD<span class="br0">)</span><span class="sy0">;</span>
<span class="br0">}</span>
close<span class="br0">(</span>SocketFD<span class="br0">)</span><span class="sy0">;</span>
<span class="kw1">return</span> <span class="nu0">0</span><span class="sy0">;</span>
<span class="br0">}</span>
客户机
建立一个客户机连接涉及以下步骤:
- 调用
socket()
建立套接字。 - 用
connect()
连接到服务器, passing asockaddr_in
structure with thesin_family
set toAF_INET
,sin_port
set to the port the endpoint is listening (in network byte order), andsin_addr
set to the IP address of the listening server (also in network byte order.) - 用
send()
和recv()
或者write()
和read()
进行通信。 - 用
close()
终止连接。如果调用fork()
, 每个进程都要用close()
。
<span class="coMULTI">/* Client code in C */</span>
<span class="co2">#include <sys/types.h></span>
<span class="co2">#include <sys/socket.h></span>
<span class="co2">#include <netinet/in.h></span>
<span class="co2">#include <arpa/inet.h></span>
<span class="co2">#include <stdio.h></span>
<span class="co2">#include <stdlib.h></span>
<span class="co2">#include <string.h></span>
<span class="co2">#include <unistd.h></span>
<span class="kw4">int</span> main<span class="br0">(</span><span class="kw4">void</span><span class="br0">)</span>
<span class="br0">{</span>
<span class="kw4">struct</span> sockaddr_in stSockAddr<span class="sy0">;</span>
<span class="kw4">int</span> Res<span class="sy0">;</span>
<span class="kw4">int</span> SocketFD <span class="sy0">=</span> socket<span class="br0">(</span>PF_INET<span class="sy0">,</span> SOCK_STREAM<span class="sy0">,</span> IPPROTO_TCP<span class="br0">)</span><span class="sy0">;</span>
<span class="kw1">if</span> <span class="br0">(</span><span class="sy0">-</span><span class="nu0">1</span> <span class="sy0">==</span> SocketFD<span class="br0">)</span>
<span class="br0">{</span>
<span class="kw3">perror</span><span class="br0">(</span><span class="st0">"cannot create socket"</span><span class="br0">)</span><span class="sy0">;</span>
<span class="kw3">exit</span><span class="br0">(</span>EXIT_FAILURE<span class="br0">)</span><span class="sy0">;</span>
<span class="br0">}</span>
<span class="kw3">memset</span><span class="br0">(</span><span class="sy0">&</span>stSockAddr<span class="sy0">,</span> <span class="nu0">0</span><span class="sy0">,</span> <span class="kw4">sizeof</span><span class="br0">(</span><span class="kw4">struct</span> sockaddr_in<span class="br0">)</span><span class="br0">)</span><span class="sy0">;</span>
stSockAddr.<span class="me1">sin_family</span> <span class="sy0">=</span> AF_INET<span class="sy0">;</span>
stSockAddr.<span class="me1">sin_port</span> <span class="sy0">=</span> htons<span class="br0">(</span><span class="nu0">1100</span><span class="br0">)</span><span class="sy0">;</span>
Res <span class="sy0">=</span> inet_pton<span class="br0">(</span>AF_INET<span class="sy0">,</span> <span class="st0">"192.168.1.3"</span><span class="sy0">,</span> <span class="sy0">&</span>stSockAddr.<span class="me1">sin_addr</span><span class="br0">)</span><span class="sy0">;</span>
<span class="kw1">if</span> <span class="br0">(</span><span class="nu0">0</span> <span class="sy0">></span> Res<span class="br0">)</span>
<span class="br0">{</span>
<span class="kw3">perror</span><span class="br0">(</span><span class="st0">"error: first parameter is not a valid address family"</span><span class="br0">)</span><span class="sy0">;</span>
close<span class="br0">(</span>SocketFD<span class="br0">)</span><span class="sy0">;</span>
<span class="kw3">exit</span><span class="br0">(</span>EXIT_FAILURE<span class="br0">)</span><span class="sy0">;</span>
<span class="br0">}</span>
<span class="kw1">else</span> <span class="kw1">if</span> <span class="br0">(</span><span class="nu0">0</span> <span class="sy0">==</span> Res<span class="br0">)</span>
<span class="br0">{</span>
<span class="kw3">perror</span><span class="br0">(</span><span class="st0">"char string (second parameter does not contain valid ipaddress"</span><span class="br0">)</span><span class="sy0">;</span>
close<span class="br0">(</span>SocketFD<span class="br0">)</span><span class="sy0">;</span>
<span class="kw3">exit</span><span class="br0">(</span>EXIT_FAILURE<span class="br0">)</span><span class="sy0">;</span>
<span class="br0">}</span>
<span class="kw1">if</span> <span class="br0">(</span><span class="sy0">-</span><span class="nu0">1</span> <span class="sy0">==</span> connect<span class="br0">(</span>SocketFD<span class="sy0">,</span> <span class="br0">(</span><span class="kw4">const</span> <span class="kw4">struct</span> sockaddr <span class="sy0">*</span><span class="br0">)</span><span class="sy0">&</span>stSockAddr<span class="sy0">,</span> <span class="kw4">sizeof</span><span class="br0">(</span><span class="kw4">struct</span> sockaddr_in<span class="br0">)</span><span class="br0">)</span><span class="br0">)</span>
<span class="br0">{</span>
<span class="kw3">perror</span><span class="br0">(</span><span class="st0">"connect failed"</span><span class="br0">)</span><span class="sy0">;</span>
close<span class="br0">(</span>SocketFD<span class="br0">)</span><span class="sy0">;</span>
<span class="kw3">exit</span><span class="br0">(</span>EXIT_FAILURE<span class="br0">)</span><span class="sy0">;</span>
<span class="br0">}</span>
<span class="coMULTI">/* perform read write operations ... */</span>
shutdown<span class="br0">(</span>SocketFD<span class="sy0">,</span> SHUT_RDWR<span class="br0">)</span><span class="sy0">;</span>
close<span class="br0">(</span>SocketFD<span class="br0">)</span><span class="sy0">;</span>
<span class="kw1">return</span> <span class="nu0">0</span><span class="sy0">;</span>
<span class="br0">}</span>
使用UDP的服务器客户机举例
用户数据报协议(UDP)是一个不保证正确传输的无连接协议。 UDP数据包可能会乱序到达,多次到达或者直接丢失。但是设计的负载比TCP小。
UDP地址空间,也即是UDP端口,和TCP端口是没有关系的。
服务器
Code may set up a UDP server on port 7654 as follows:
<span class="co2">#include <stdio.h></span>
<span class="co2">#include <errno.h></span>
<span class="co2">#include <string.h></span>
<span class="co2">#include <sys/socket.h></span>
<span class="co2">#include <sys/types.h></span>
<span class="co2">#include <netinet/in.h></span>
<span class="co2">#include <unistd.h> /* for close() for socket */ </span>
<span class="co2">#include <stdlib.h></span>
<span class="kw4">int</span> main<span class="br0">(</span><span class="kw4">void</span><span class="br0">)</span>
<span class="br0">{</span>
<span class="kw4">int</span> sock <span class="sy0">=</span> socket<span class="br0">(</span>PF_INET<span class="sy0">,</span> SOCK_DGRAM<span class="sy0">,</span> IPPROTO_UDP<span class="br0">)</span><span class="sy0">;</span>
<span class="kw4">struct</span> sockaddr_in sa<span class="sy0">;</span>
<span class="kw4">char</span> buffer<span class="br0">[</span><span class="nu0">1024</span><span class="br0">]</span><span class="sy0">;</span>
ssize_t recsize<span class="sy0">;</span>
socklen_t fromlen<span class="sy0">;</span>
<span class="kw3">memset</span><span class="br0">(</span><span class="sy0">&</span>sa<span class="sy0">,</span> <span class="nu0">0</span><span class="sy0">,</span> <span class="kw4">sizeof</span><span class="br0">(</span>sa<span class="br0">)</span><span class="br0">)</span><span class="sy0">;</span>
sa.<span class="me1">sin_family</span> <span class="sy0">=</span> AF_INET<span class="sy0">;</span>
sa.<span class="me1">sin_addr</span>.<span class="me1">s_addr</span> <span class="sy0">=</span> INADDR_ANY<span class="sy0">;</span>
sa.<span class="me1">sin_port</span> <span class="sy0">=</span> htons<span class="br0">(</span><span class="nu0">7654</span><span class="br0">)</span><span class="sy0">;</span>
<span class="kw1">if</span> <span class="br0">(</span><span class="sy0">-</span><span class="nu0">1</span> <span class="sy0">==</span> bind<span class="br0">(</span>sock<span class="sy0">,</span><span class="br0">(</span><span class="kw4">struct</span> sockaddr <span class="sy0">*</span><span class="br0">)</span><span class="sy0">&</span>sa<span class="sy0">,</span> <span class="kw4">sizeof</span><span class="br0">(</span><span class="kw4">struct</span> sockaddr<span class="br0">)</span><span class="br0">)</span><span class="br0">)</span>
<span class="br0">{</span>
<span class="kw3">perror</span><span class="br0">(</span><span class="st0">"error bind failed"</span><span class="br0">)</span><span class="sy0">;</span>
close<span class="br0">(</span>sock<span class="br0">)</span><span class="sy0">;</span>
<span class="kw3">exit</span><span class="br0">(</span>EXIT_FAILURE<span class="br0">)</span><span class="sy0">;</span>
<span class="br0">}</span>
<span class="kw1">for</span> <span class="br0">(</span><span class="sy0">;;</span><span class="br0">)</span>
<span class="br0">{</span>
<span class="kw3">printf</span> <span class="br0">(</span><span class="st0">"recv test....<span class="es1">\n</span>"</span><span class="br0">)</span><span class="sy0">;</span>
recsize <span class="sy0">=</span> recvfrom<span class="br0">(</span>sock<span class="sy0">,</span> <span class="br0">(</span><span class="kw4">void</span> <span class="sy0">*</span><span class="br0">)</span>buffer<span class="sy0">,</span> <span class="nu0">1024</span><span class="sy0">,</span> <span class="nu0">0</span><span class="sy0">,</span> <span class="br0">(</span><span class="kw4">struct</span> sockaddr <span class="sy0">*</span><span class="br0">)</span><span class="sy0">&</span>sa<span class="sy0">,</span> <span class="sy0">&</span>fromlen<span class="br0">)</span><span class="sy0">;</span>
<span class="kw1">if</span> <span class="br0">(</span>recsize <span class="sy0"><</span> <span class="nu0">0</span><span class="br0">)</span>
<span class="kw3">fprintf</span><span class="br0">(</span>stderr<span class="sy0">,</span> <span class="st0">"%s<span class="es1">\n</span>"</span><span class="sy0">,</span> <span class="kw3">strerror</span><span class="br0">(</span>errno<span class="br0">)</span><span class="br0">)</span><span class="sy0">;</span>
<span class="kw3">printf</span><span class="br0">(</span><span class="st0">"recsize: %d<span class="es1">\n</span> "</span><span class="sy0">,</span>recsize<span class="br0">)</span><span class="sy0">;</span>
sleep<span class="br0">(</span><span class="nu0">1</span><span class="br0">)</span><span class="sy0">;</span>
<span class="kw3">printf</span><span class="br0">(</span><span class="st0">"datagram: %s<span class="es1">\n</span>"</span><span class="sy0">,</span>buffer<span class="br0">)</span><span class="sy0">;</span>
<span class="br0">}</span>
<span class="br0">}</span>
上面的无限循环用recvfrom()接收给UDP端口7654的数据包。使用如下参数:
- 指向缓存数据指针
- 缓存大小
- 标志
- 地址
- 地址结构体大小
客户机
用UDP数据包发送一个"Hello World!" 给地址127.0.0.1(回环地址),端口 7654 。
<span class="co2">#include <stdlib.h></span>
<span class="co2">#include <stdio.h></span>
<span class="co2">#include <errno.h></span>
<span class="co2">#include <string.h></span>
<span class="co2">#include <sys/socket.h></span>
<span class="co2">#include <sys/types.h></span>
<span class="co2">#include <netinet/in.h></span>
<span class="co2">#include <unistd.h> /* for close() for socket */</span>
<span class="kw4">int</span> main<span class="br0">(</span><span class="kw4">int</span> argc<span class="sy0">,</span> <span class="kw4">char</span> <span class="sy0">*</span>argv<span class="br0">[</span><span class="br0">]</span><span class="br0">)</span>
<span class="br0">{</span>
<span class="kw4">int</span> sock<span class="sy0">;</span>
<span class="kw4">struct</span> sockaddr_in sa<span class="sy0">;</span>
<span class="kw4">int</span> bytes_sent<span class="sy0">,</span> buffer_length<span class="sy0">;</span>
<span class="kw4">char</span> buffer<span class="br0">[</span><span class="nu0">200</span><span class="br0">]</span><span class="sy0">;</span>
buffer_length <span class="sy0">=</span> <span class="kw3">snprintf</span><span class="br0">(</span>buffer<span class="sy0">,</span> <span class="kw4">sizeof</span> buffer<span class="sy0">,</span> <span class="st0">"Hello World!"</span><span class="br0">)</span><span class="sy0">;</span>
sock <span class="sy0">=</span> socket<span class="br0">(</span>PF_INET<span class="sy0">,</span> SOCK_DGRAM<span class="sy0">,</span> IPPROTO_UDP<span class="br0">)</span><span class="sy0">;</span>
<span class="kw1">if</span> <span class="br0">(</span><span class="sy0">-</span><span class="nu0">1</span> <span class="sy0">==</span> sock<span class="br0">)</span> <span class="coMULTI">/* if socket failed to initialize, exit */</span>
<span class="br0">{</span>
<span class="kw3">printf</span><span class="br0">(</span><span class="st0">"Error Creating Socket"</span><span class="br0">)</span><span class="sy0">;</span>
<span class="kw3">exit</span><span class="br0">(</span>EXIT_FAILURE<span class="br0">)</span><span class="sy0">;</span>
<span class="br0">}</span>
<span class="kw3">memset</span><span class="br0">(</span><span class="sy0">&</span>sa<span class="sy0">,</span> <span class="nu0">0</span><span class="sy0">,</span> <span class="kw4">sizeof</span><span class="br0">(</span>sa<span class="br0">)</span><span class="br0">)</span><span class="sy0">;</span>
sa.<span class="me1">sin_family</span> <span class="sy0">=</span> AF_INET<span class="sy0">;</span>
sa.<span class="me1">sin_addr</span>.<span class="me1">s_addr</span> <span class="sy0">=</span> htonl<span class="br0">(</span><span class="nu12">0x7F000001</span><span class="br0">)</span><span class="sy0">;</span>
sa.<span class="me1">sin_port</span> <span class="sy0">=</span> htons<span class="br0">(</span><span class="nu0">7654</span><span class="br0">)</span><span class="sy0">;</span>
bytes_sent <span class="sy0">=</span> sendto<span class="br0">(</span>sock<span class="sy0">,</span> buffer<span class="sy0">,</span> buffer_length<span class="sy0">,</span> <span class="nu0">0</span><span class="sy0">,</span><span class="br0">(</span><span class="kw4">struct</span> sockaddr<span class="sy0">*</span><span class="br0">)</span><span class="sy0">&</span>sa<span class="sy0">,</span> <span class="kw4">sizeof</span> <span class="br0">(</span><span class="kw4">struct</span> sockaddr_in<span class="br0">)</span><span class="br0">)</span><span class="sy0">;</span>
<span class="kw1">if</span> <span class="br0">(</span>bytes_sent <span class="sy0"><</span> <span class="nu0">0</span><span class="br0">)</span>
<span class="kw3">printf</span><span class="br0">(</span><span class="st0">"Error sending packet: %s<span class="es1">\n</span>"</span><span class="sy0">,</span> <span class="kw3">strerror</span><span class="br0">(</span>errno<span class="br0">)</span><span class="br0">)</span><span class="sy0">;</span>
close<span class="br0">(</span>sock<span class="br0">)</span><span class="sy0">;</span> <span class="coMULTI">/* close the socket */</span>
<span class="kw1">return</span> <span class="nu0">0</span><span class="sy0">;</span>
<span class="br0">}</span>
buffer
指定要发送数据的指针, buffer_length
指定缓存内容的大小。