- 论坛徽章:
- 0
|
一、套接子的理解
套接字是一种使用标准UNIX文件描述符(file
descriptor)与其他程序通信的方式,可、套接字可以
看作是处于不同主机之间的两个程序的连接端点,一方面程序将要传输的信息写入套接字,另一方面
则通过读取套接字的数据来获得传输的信息。在使用套接字之前,先来了解下面的数据结构。
二、套接字编程相关的数据结构
1、表示套接口的数据结构
struct socket
套接口是由 socket
数据结构代表的,形式如下:
struct
socket
{
socket_state
state;
/* 指明套接口的连接状态,一个套接口的连接状态可以有以下几种
套接口是空闲的,还没有进行相应的端口及地址的绑定;还没有连接;
正在连接中;已经连接;正在解除连接。
*/
unsigned
long flags;
struct
proto_ops
ops; /* 指明可对套接口进行的各种操作
*/
struct
inode inode;
/* 指向 sockfs
文件系统中的相应 inode
*/
struct
fasync_struct *fasync_list; /* Asynchronous wake up list */
struct
file *file;
/* 指向 sockfs
文件系统*/
struct
sock
sk; /* 任何协议族都有其特定的套接口特性,
该域就指向特定协议族的套接口对
象。
*/
wait_queue_head_t
wait;
short
type;
unsigned
char passcred;
};
2、描述套接口通用地址的数据结构
struct sockaddr
在 bind、connect
等系统调用中,特定于协议的套接口地址结构指针都要强制转换成
该通用的套接口地址结构指针。结构形式如下:
struct
sockaddr {
sa_family_t
sa_family; /* 用于指定地址族,如果是TCP/IP通信,该值取PF_INET*/
char
sa_data[14]; /* 用于保存套接字的IP地址和端口信息
*/
};
3、描述因特网地址结构的数据结构
struct sockaddr_in
其数据结构与sockaddr类似定义如下:
struct
sockaddr_in
{
short
int sin_family;/*用于指定地址族*/
unsigned
short int sin_port;/*套接子通信的端口*/
struct
in_addr sin_addr;/*通信的IP地址*/
unsigned
char sin_zero[8];/*用以填充0,保持与struct
sockaddr大小相同*/
};
由于sockaddr和sockaddr_in的数据结构是一样的指向struct
sockaddr_in的指针可以通过强制
转换,转换成指向sockaddr结构的指针。
三、套接口编程的几个重要步骤及相关常用函数介绍:
1、创建套接口,由系统调用
socket 实现:
int
socket(int domain, int type,int protocol);
domain:说明我们网络程序所在的主机采用的通讯协族(AF_UNIX和AF_INET等).
AF_UNIX只能够用
于单一的Unix系统进程间通信,而AF_INET是针对Internet的,因而可以允许在远程
主机之间通信
(当我们
man socket时发现
domain可选项是
PF_*而不是AF_*,因为glibc是posix的实现
所以用PF代替了AF,不过我们都可以使用的).
type:我们网络程序所采用的通讯协议(SOCK_STREAM,SOCK_DGRAM等)
SOCK_STREAM表明我们用
的是TCP协议,这样会提供按顺序的,可靠,双向,面向连接的比特流.
SOCK_DGRAM 表明我们用的是
UDP协议,这样只会提供定长的,不可靠,无连接的通信.
protocol:由于我们指定了type,所以这个地方我们一般只要用0来代替就可以了
socket为网络通讯
做基本的准备.成功时返回文件描述符,失败时返回-1,看errno可知道出错的详细情况
socket系统调用返回一个文件描述符,他在许多方面都类似于底层的文件描述符。当这个套接字连接
到另一断的套接字后,就可以调用read和write系统调用,通过这个文件描述符来在套接字上发送
和接受数据了,close系统调用用于结束套接字连接。
2、绑定地址
根据传输层协议(TCP、UDP)的不同,客户机及服务器的处理方式也有很大不同。但是,不管通信双方
使用何种传输协议,都需要一种标识自己的机制。
通信双方一般由两个方面标识:地址和端口号(通常,一个
IP 地址和一个端口号常常被称为一个套接
口)。根据地址可以寻址到主机,根据端口号则可以寻址到主机提供特定服务的进程,实际上,一个特
定的端口号代表了一个提供特定服务的进程。
int
bind(int sockfd, struct sockaddr *my_addr, int addrlen);
sockfd:是由socket调用返回的文件描述符.
addrlen:是sockaddr结构的长度.
my_addr:是一个指向sockaddr的指针.
在中有 sockaddr的定义
struct
sockaddr {
unisgned
short as_family;
char
sa_data[14];
};
不过由于系统的兼容性,我们一般不用这个头文件,而使用另外一个结构(struct
sockaddr_in) 来
代替.在上面有sockaddr_in的定义
struct
sockaddr_in {
unsigned
short sin_family;
unsigned
short int sin_port;
struct
in_addr sin_addr;
unsigned
char sin_zero[8];
};
我们主要使用Internet所以sin_family一般为AF_INET,sin_addr设置为INADDR_ANY表示可以
和任何的主机通信,sin_port是我们要监听的端口号.sin_zero[8]是用来填充的.
Bind将本地的
端口同socket返回的文件描述符捆绑在一起.成功是返回0,失败的情况和socket一样
3、监听(由服务器端发起)
int
listen(int sockfd,int backlog);
sockfd:是bind后的文件描述符.
backlog:设置请求排队的最大长度.当有多个客户端程序和服务端相连时,
使用这个表示可以介绍的
排队长度.
listen函数将bind的文件描述符变为监听套接字.返回的情况和bind一样.
4、请求建立连接(由
TCP 客户发起)
对于采用面向连接的传输协议
TCP
实现通信来说,一个比较重要的步骤就是通信双方建立连接(如果采
用 udp
传输协议则不需要),由系统调用
connect()完成:
int
connect(int sockfd, struct sockaddr * serv_addr,int addrlen);
sockfd:socket返回的文件描述符.
serv_addr:储存了服务器端的连接信息.其中sin_add是服务端的地址
addrlen:serv_addr的长度
connect函数是客户端用来同服务端连接的.成功时返回0,sockfd是同
服务端通讯的文件描述符
失败时返回-1.
与 connect()相对应,在服务器端,通过系统调用
listen(),指定服务器端的套接口为监听套
接口,监听每一个向服务器套接口发出的连接请求,并通过握手机制建立连接。内核为
listen()维护两
个队列:已完成连接队列和未完成连接队列。
5、接受连接请求(由
TCP 服务器端发起)
服
务器端通过监听套接口,为所有连接请求建立了两个队列:已完成连接队列和未完成连接队列(每个
监听套接口都对应这样两个队列,当然,一般服务器只有一个监
听套接口)。通过
accept()调用,服
务器将在监听套接口的已连接队列头中,返回用于代表当前连接的套接口描述字。
int
accept(int sockfd, struct sockaddr *addr,int *addrlen);
sockfd:是listen后的文件描述符.
addr,addrlen是用来给客户端的程序填写的,服务器端只要传递指针就可以了.
bind,listen和
accept是服务器端用的函数,accept调用时,服务器端的程序会一直阻塞到有一个
客户程序发出了
连接.
accept成功时返回最后的服务器端的文件描述符,这个时候服务器端可以向该描述符写信息了.
失败时返回-1.
对于采用 TCP
传输协议进行通信的服务器和客户机来说,一定要经过客户请求建立连接,服务器接
受连接请求这一过程;而对采用
UDP 传输协议的通信双方则不需要这一步骤。
6、通信
客户机可以通过套接口接收服务器传过来的数据,也可以通过套接口向服务器发送数据。前面所有的准
备工作(创建套接口、绑定等操作)都是为这一步骤准备的。
常用的从套接口中接收数据的调用有:recv、recvfrom、recvmsg
等,常用的向套接口中发送数据的
调用有 send、sendto、sendmsg
等。
//recv从指定套接字中获取发送的消息
ssize_t
recv(int s//指定接收端套接字描述符
,
void *
buf//指明一个缓冲区,该缓冲区用来存放recv函数接收到的数据
,
size_t
len/指明buf的长度
,
int
flags)
//一般置0
//
recvfrom 用于接收套接字发送来的信息
ssize_t
recvfrom(int s //套接字文件描述符
,
void *buf //指向接收到信息的指针
,
size_t
len //内存空间的最大长度
,
int
flags //控制参数,用于控制是否接收代外数据
,
struct sockaddr *
from //指向发送方地址的指针
,
socklen_t *
fromlen)
//指向发送方地址大小的指针
//send用于将信息发送到指定的套接字文件描述符中
ssize_t
send(int s//指定发送端套接字描述符
,const
void *buf//指明一个存放应用程序要发送数据的缓冲区;
,
size_t
len//指明实际要发送的数据的字节数
,
int
flags)//一般置0
//
sendto用于发送信息给指定的主机,当用于面向通信的的连接时to和tolen将被忽略
ssize_t
sendto(int s //套接字文件描述符
,
const void *
buf //指向要发送信息地址空间的指针
,
size_t
len //要发送的字节
,
int
flags // 控制参数,用于控制是否接收数据以及是否预览报文
,const struct sockaddr
*
to
,
socklen_t
tolen)
7、关闭套接字
close(sockfd);
本文来自ChinaUnix博客,如果查看原文请点:http://blog.chinaunix.net/u2/74012/showart_1131554.html |
|