伯克利套接字(BSD Socket)
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 thesin_family(AF_INET), and fill itssin_port(the listening port, in network byte order) fields. Converting ashort intto 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_instructure with thesin_familyset toAF_INET,sin_portset to the port the endpoint is listening (in network byte order), andsin_addrset 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)进行拼接和反转,所以 ...
随机推荐
- 小程序接口无法传递session校验验证码
今天在写接口的时候发现一个问题,我用apiaaz测试一切正常,但是从小程序接口请求验证码,一直验证失败. 最开始用的图形验证码,查阅了不少资料,最后怀疑是cookie的问题,解决无果,换成了短信验证码 ...
- apache添加ssl协议实现用户认证
目标 1对服务器的访问由http改为https, 2仅有证书的客户端可以访问服务器, 3.通过服务器端的配置,可以停用某个客户端的证书. 一 Apache服务器相关配置: 在../apache/con ...
- Replication:distribution 中一直在运行 waitfor delay @strdelaytime 语句
Replication 自动创建来一个 Job:Replication monitoring refresher for distribution,这个Agent执行一个sp: dbo.sp_repl ...
- C# yield checked,unchecked lock语句(C#学习笔记04)
特殊语句 yield语句 yield用于终止迭代 只能使用在返回类型必须为 IEnumerable.IEnumerable<T>.IEnumerator 或 IEnumerator< ...
- Java自学-异常处理 Exception
Java 异常 Exception 异常定义: 导致程序的正常流程被中断的事件,叫做异常 步骤 1 : 文件不存在异常 比如要打开d盘的LOL.exe文件,这个文件是有可能不存在的 Java中通过 n ...
- HTML5实现无刷新修改URL
前言 今天在做一个vue的搜索功能,需要从搜索结果页面跳转到细节页面,然后点击返回还能返回到刚刚的结果页面,如果只用window.history.go(-1)当然会重新刷新搜索页面,当然是不行的. 我 ...
- layui 表格中实现照片预览,点击查看原图
人员表格中实现照片预览,并且可点击放大.查看原图 <table id="dutyInfoTable" class="layui-hide">< ...
- 从 Vue 的视角学 React(四)—— 组件传参
组件化开发的时候,参数传递是非常关键的环节 哪些参数放在组件内部管理,哪些参数由父组件传入,哪些状态需要反馈给父组件,都需要在设计组件的时候想清楚 但实现这些交互的基础,是明白组件之间参数传递的方式, ...
- Qt NetWork即时通讯网络聊天室(基于TCP)
本文使用QT的网络模块来创建一个网络聊天室程序,主要包括以下功能: 1.基于TCP的可靠连接(QTcpServer.QTcpSocket) 2.一个服务器,多个客户端 3.服务器接收到某个客户端的请求 ...
- 解决apscheduler报错:Run time of job …… next run at: ……)” was missed by
在Django中使用apscheduler django_apscheduler 实现定时任务, 来完成数据拉取. 一段时间后发现数据量对不上,遂查日志 发现报错如下: Run time of job ...