http://blog.csdn.net/blueman2012/article/details/6693605#socket.28.29
伯克利套接字(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,否则返回一个代表文件描述符的整数。
- 函数原型
int socket(int domain, int type, int protocol);
bind()
bind()
给套接字分配一个地址。当使用 socket()
创造一个套接字时, 只是给定了协议族,并没有分配地址。在套接字能够接受来自其他主机的连接前,必须用bind()给它绑定一个地址。 bind()
由三个参数:
-
sockfd
, 代表socket的文件描述符。 -
my_addr
, 指向sockaddr
结构体的指针,代表要绑定的地址 。 -
addrlen
, 是sockaddr结构体的大小。
Bind()返回0表示成功,错误返回-1。
- 函数原型
int bind(int sockfd, const struct sockaddr *my_addr, socklen_t addrlen);
listen()
一旦一个套接字和一个地址联系之后,listen()
监听到来的连接。但是这只适用于对面向连接的模式,例如 套接字类型是 (SOCK_STREAM
, SOCK_SEQPACKET
)。listen()需要两个参数:
-
sockfd
,一个有效的套接字描述符。 -
backlog
,一个整数,表示一次能够等待的最大连接数目。操作系统通常会对这个值设置上限。
一旦连接被接受,返回0表示成功,错误返回-1。
- 函数原型:
int listen(int sockfd, int backlog);
accept()
当应用程序监听来自其他主机的面对数据流的连接时,通过事件(比如Unix select()系统调用)通知它。必须用 accept()
函数初始化连接。 Accept() 为每个连接创立新的套接字并从监听队列中移除这个连接。它使用如下参数:
-
sockfd
,监听的套接字描述符 -
cliaddr
, 指向sockaddr 结构体的指针,客户机地址信息。 -
addrlen
,指向socklen_t
的指针,确定客户机地址结构体的大小 。
返回新的套接字描述符,出错返回-1。进一步的通信必须通过这个套接字。
Datagram 套接字不要求用accept()处理,因为接收方可能用监听套接字立即处理这个请求。
- 函数原型:
int accept(int sockfd, struct sockaddr *cliaddr, socklen_t *addrlen);
connect()
connect()
系统调用为一个套接字设置连接,参数有文件描述符和主机地址。
某些类型的套接字是无连接的,大多数是UDP协议。对于这些套接字,连接时这样的:默认发送和接收数据的主机由给定的地址确定,可以使用 send()和 recv()。 返回-1表示出错,0表示成功。
- 函数原型:
int connect(int sockfd, const struct sockaddr *serv_addr, socklen_t addrlen);
gethostbyname() 和 gethostbyaddr()
gethostbyname()
和 gethostbyaddr()
函数是用来解析主机名和地址的。可能会使用DNS服务或者本地主机上的其他解析机制(例如查询/etc/hosts)。返回一个指向 struct hostent的指针,这个结构体描述一个IP主机。函数使用如下参数:
- name 指定主机名。例如 www.wikipedia.org
- addr 指向 struct in_addr的指针,包含主机的地址。
- len 给出 addr的长度,以字节为单位。
- type 指定地址族类型 (比如 AF_INET)。
出错返回NULL指针,可以通过检查 h_errno 来确定是临时错误还是未知主机。正确则返回一个有效的 struct hostent *。
这些函数并不是伯克利套接字严格的组成部分。这些函数可能是过时了,新函数是 getaddrinfo() and getnameinfo(), 这些新函数是基于addrinfo数据结构。
- 函数原型:
struct hostent *gethostbyname(const char *name);
struct hostent *gethostbyaddr(const void *addr, int len, int type);
协议和地址
套接字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 itssin_port
(the listening port, in network byte order) fields. Converting ashort int
to network byte order can be done by calling the functionhtons()
(host to network short). - 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.
/* Server code in C */ #include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h> int main(void)
{
struct sockaddr_in stSockAddr;
int SocketFD = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); if(-1 == SocketFD)
{
perror("can not create socket");
exit(EXIT_FAILURE);
} memset(&stSockAddr, 0, sizeof(struct sockaddr_in)); stSockAddr.sin_family = AF_INET;
stSockAddr.sin_port = htons(1100);
stSockAddr.sin_addr.s_addr = INADDR_ANY; if(-1 == bind(SocketFD,(const struct sockaddr *)&stSockAddr, sizeof(struct sockaddr_in)))
{
perror("error bind failed");
close(SocketFD);
exit(EXIT_FAILURE);
} if(-1 == listen(SocketFD, 10))
{
perror("error listen failed");
close(SocketFD);
exit(EXIT_FAILURE);
} for(;;)
{
int ConnectFD = accept(SocketFD, NULL, NULL); if(0 > ConnectFD)
{
perror("error accept failed");
close(SocketFD);
exit(EXIT_FAILURE);
} /* perform read write operations ... */ shutdown(ConnectFD, SHUT_RDWR); close(ConnectFD);
} close(SocketFD);
return 0;
}
客户机
建立一个客户机连接涉及以下步骤:
- 调用
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()
。
/* Client code in C */ #include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h> int main(void)
{
struct sockaddr_in stSockAddr;
int Res;
int SocketFD = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); if (-1 == SocketFD)
{
perror("cannot create socket");
exit(EXIT_FAILURE);
} memset(&stSockAddr, 0, sizeof(struct sockaddr_in)); stSockAddr.sin_family = AF_INET;
stSockAddr.sin_port = htons(1100);
Res = inet_pton(AF_INET, "192.168.1.3", &stSockAddr.sin_addr); if (0 > Res)
{
perror("error: first parameter is not a valid address family");
close(SocketFD);
exit(EXIT_FAILURE);
}
else if (0 == Res)
{
perror("char string (second parameter does not contain valid ipaddress");
close(SocketFD);
exit(EXIT_FAILURE);
} if (-1 == connect(SocketFD, (const struct sockaddr *)&stSockAddr, sizeof(struct sockaddr_in)))
{
perror("connect failed");
close(SocketFD);
exit(EXIT_FAILURE);
} /* perform read write operations ... */ shutdown(SocketFD, SHUT_RDWR); close(SocketFD);
return 0;
}
使用UDP的服务器客户机举例
用户数据报协议(UDP)是一个不保证正确传输的无连接协议。 UDP数据包可能会乱序到达,多次到达或者直接丢失。但是设计的负载比TCP小。
UDP地址空间,也即是UDP端口,和TCP端口是没有关系的。
服务器
Code may set up a UDP server on port 7654 as follows:
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <unistd.h> /* for close() for socket */
#include <stdlib.h> int main(void)
{
int sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
struct sockaddr_in sa;
char buffer[1024];
ssize_t recsize;
socklen_t fromlen; memset(&sa, 0, sizeof(sa));
sa.sin_family = AF_INET;
sa.sin_addr.s_addr = INADDR_ANY;
sa.sin_port = htons(7654); if (-1 == bind(sock,(struct sockaddr *)&sa, sizeof(struct sockaddr)))
{
perror("error bind failed");
close(sock);
exit(EXIT_FAILURE);
} for (;;)
{
printf ("recv test....\n");
recsize = recvfrom(sock, (void *)buffer, 1024, 0, (struct sockaddr *)&sa, &fromlen);
if (recsize < 0)
fprintf(stderr, "%s\n", strerror(errno));
printf("recsize: %d\n ",recsize);
sleep(1);
printf("datagram: %s\n",buffer);
}
}
上面的无限循环用recvfrom()接收给UDP端口7654的数据包。使用如下参数:
- 指向缓存数据指针
- 缓存大小
- 标志
- 地址
- 地址结构体大小
客户机
用UDP数据包发送一个"Hello World!" 给地址127.0.0.1(回环地址),端口 7654 。
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <unistd.h> /* for close() for socket */ int main(int argc, char *argv[])
{
int sock;
struct sockaddr_in sa;
int bytes_sent, buffer_length;
char buffer[200]; buffer_length = snprintf(buffer, sizeof buffer, "Hello World!"); sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (-1 == sock) /* if socket failed to initialize, exit */
{
printf("Error Creating Socket");
exit(EXIT_FAILURE);
} memset(&sa, 0, sizeof(sa));
sa.sin_family = AF_INET;
sa.sin_addr.s_addr = htonl(0x7F000001);
sa.sin_port = htons(7654); bytes_sent = sendto(sock, buffer, buffer_length, 0,(struct sockaddr*)&sa, sizeof (struct sockaddr_in));
if (bytes_sent < 0)
printf("Error sending packet: %s\n", strerror(errno)); close(sock); /* close the socket */
return 0;
}
buffer
指定要发送数据的指针, buffer_length
指定缓存内容的大小。
伯克利套接字(BSD Socket)的更多相关文章
-
《Python》网络编程之客户端/服务端框架、套接字(socket)初使用
一.软件开发的机构 我们了解的涉及到两个程序之间通讯的应用大致可以分为两种: 第一种是应用类:QQ.微信.网盘等这一类是属于需要安装的桌面应用 第二种是web类:比如百度.知乎.博客园等使用浏览器访问 ...
-
使用TCP/IP的套接字(Socket)进行通信
http://www.cnblogs.com/mengdd/archive/2013/03/10/2952616.html 使用TCP/IP的套接字(Socket)进行通信 套接字Socket的引入 ...
-
Java 网络编程(五) 使用TCP/IP的套接字(Socket)进行通信
链接地址:http://www.cnblogs.com/mengdd/archive/2013/03/10/2952616.html 使用TCP/IP的套接字(Socket)进行通信 套接字Socke ...
-
八、套接字(Socket)
demo 一个连接由它的两个端点标识,这样的端点称为套接 套接字是支持TCP/IP协议的网络通信的基本操作单元. 可以将套接字看作不同主机间的进程进行双向通信的端点. 上图连接1的一对套接字为: (1 ...
-
什么是网络套接字(Socket)?
什么是网络套接字(Socket)?一时还真不好回答,而且网络上也有各种解释,莫衷一是.下文将以本人所查阅到的资料来说明一下什么是Socket. Socket定义 Socket在*的定义: A n ...
-
java套接字(socket)实例
客户端socket 流程: 1.连接远程主机 2.发送数据 3.接收数据 4.关闭流与socket连接 实例: import java.io.*; import java.net.Socket; im ...
-
Linux网络协议栈(二)——套接字缓存(socket buffer)
Linux网络核心数据结构是套接字缓存(socket buffer),简称skb.它代表一个要发送或处理的报文,并贯穿于整个协议栈.1. 套接字缓存skb由两部分组成:(1) 报文数据:它 ...
-
02_套接字编程(socket抽象层)
1.套接字概述 1.套接概述: 套接是进行网络通信的一种手段(socket) 2.套接字分类: 流式套接字(SOCK_STREAM): 传输层基于tcp协议进行通信 数 ...
-
03、Windows Phone 套接字(Socket)实战之WP客户端设计
因为 PC 端和 WP 端进行通信时,采用的自定义的协议,所以也需要定义 DataType 类来判断 通信数据的类型,并且把数据的描述信息(head) 和数据的实际内容(body)进行拼接和反转,所以 ...
随机推荐
-
Linux中的ps命令
Linux中ps命令用来列出系统中当前运行的那些进程. 使用格式:ps 参数 如:ps -A 通过man ps可以获得ps的详细参数用法 -A 显示所有进程信息 c 列出程序时,显示每个程序真正的 ...
-
C# Webservice 解决在运行配置文件中指定的扩展时出现异常。 --->; System.Web.HttpException: 超过了最大请求长度问
摘自: http://blog.csdn.net/gulijiang2008/article/details/4482993 请在服务器端配置 方法一: 在通过WebService处理大数据量数据时出 ...
-
document.getElementById和document.querySelector的区别
zepto中的$(".111")出错,jQuery中$(".111")不出错的原因: zepto用document.querySelector实现,jQuery ...
-
ubuntu系统mysql.h no such file or directory
在Ubuntu系统中,你已经安装了mysql,即你使用sudo apt-get install mysql-server mysql-client然而使用C语言访问mysql数据库时,却发现出现了如下 ...
-
9月1日,请记得备好名片来PechaKucha Night和大家“闲聊” | Hi!设计
9月1日,请记得备好名片来PechaKucha Night和大家"闲聊" | Hi!设计 9月1日,请记得来PechaKucha Night和大家"闲聊"
-
摇滚吧HTML5!有声前端交互!(Hello, Jsonic!)
软件工程师们摆弄1和0编写他们的乌托邦,音乐人门把玩12平均律上的音符构筑他们的伊甸园.最近,我偶然看了<蓝色骨头>这部电影,片中的男主角是位黑客,同时又兼具音乐创作的才华.在现实生活中, ...
-
201521123070 《JAVA程序设计》第9周学习总结
1. 本章学习总结 1.1 以你喜欢的方式(思维导图或其他)归纳总结异常相关内容. 2. 书面作业 本次PTA作业题集异常 Q1. 常用异常 题目5-1 1.1 截图你的提交结果(出现学号) 1.2 ...
-
【IOS 开发】Object - C 面向对象 - 类 , 对象 , 成员变量 , 成员方法
. 一. 类定义 类定义需要实现两部分 : -- 接口部分 : 定义类的成员变量和方法, 方法是抽象的, 在头文件中定义; -- 实现部分 : 引入接口部分的头文件, 实现抽象方法; 1. 接口部分定 ...
-
Wsus Content内容误删处理
问题:在wsus content文件夹下误删除文件,需要重新下载文件解决方法:打开cmdcd C:\Program Files\Update Services\Tools\.\wsusutil.exe ...
-
mybatis:三种参数传递(转载)
转载自:https://www.2cto.com/database/201409/338155.html 第一种方案 DAO层的函数方法 Public User selectUser(String n ...