TCP、UDP、广播、多播的客户端服务器代码链接地址为(for free):
tcp代码:http://download.csdn.net/detail/huangminqiang201209/4860661
udp代码:http://download.csdn.net/detail/huangminqiang201209/4860665
广播代码:http://download.csdn.net/detail/huangminqiang201209/4860672
多播代码:http://download.csdn.net/detail/huangminqiang201209/4860719
此文主要还是在于上面的代码,我这段时间因为要构建一个TCP服务器,所以就把socket这块完整的熟悉了一下,以下这些整理的比较随意,还望见谅哦。
TCP
Transmission Control Protocol 传输控制协议TCP是一种面向连接(连接导向)的、可靠的、基于字节流的运输层(Transport layer)通信协议,由IETF的RFC 793说明(specified)。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能,UDP是同一层内另一个重要的传输协议。
在因特网协议族(Internet protocol suite)四层协议中,TCP层是位于IP层之上,应用层之下的传输层。不同主机的应用层之间经常需要可靠的、像管道一样的连接,但是IP层不提供这样的流机制,而是提供不可靠的包交换。
UDP
UDP 是User Datagram Protocol的简称, 中文名是用户数据报协议,是 OSI 参考模型中一种无连接的传输层协议,提供面向事务的简单不可靠信息传送服务,IETF RFC 768是UDP的正式规范。
广播
广播和多播仅应用于UDP,它们对需将报文同时传往多个接收者的应用来说十分重要。TCP是一个面向连接的协议,它意味着分别运行于两主机(由IP地址确定)内的两进程(由端口号确定)间存在一条连接。
考虑包含多个主机的共享信道网络如以太网。每个以太网帧包含源主机和目的主机的以太网地址(48 bit)。通常每个以太网帧仅发往单个目的主机,目的地址指明单个接收接口,因而称为单播(unicast)。在这种方式下,任意两个主机的通信不会干扰网内其他主机(可能引起争夺共享信道的情况除外)。然而,有时一个主机要向网上的所有其他主机发送帧,这就是广播。通过ARP和RARP可以看到这一过程。多播(multicast) 处于单播和广播之间:帧仅传送给属于多播组的多个主机。
多播
多播数据仅由对该数据报感兴趣的接口接收,也就是说,由运行希望参加多播会话应用系统的主机上的接口接收。广播一般局限与局域网,而多播既可用于局域网,也可用于广域网。
IP多播提供两类服务:
1) 向多个目的地址传送数据。有许多向多个接收者传送信息的应用:例如交互式会议系统和向多个接收者分发邮件或新闻。如果不采用多播,目前这些应用大多采用TCP来完成(向每个目的地址传送一个单独的数据复制)。然而,即使使用多播,某些应用可能继续采用TCP来保证它的可靠性。
2) 客户对服务器的请求。例如,无盘工作站需要确定启动引导服务器。目前,这项服务是通过广播来提供的,但是使用多播可降低不提供这项服务主机的负担。
函数
1.socket 函数
指定期望的通信协议类型。
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
int socket(int domain, int type, int protocol);
返回:若成功则为非负描述符,出错则为-1。
参数说明:
domain: 指明协议族,也称为协议域,是一个常值。
AF_INET IPv4 协议
AF_INET6 IPv6 协议
AF_LOCAL/AF_UNIX Unix协议域
AF_ROUTE 路由套接字
AF_KEY 密匙套接字
type: 指明套接字的类型。
SOCK_STREAM 字节流套接字(TCP)
SOCK_DGRAM 数据报套接字(UDP)
SOCK_SEQPACKET 有序分组套接字
SOCK_RAW 原始套接字
protocol: 指明协议类型。一般为0,以选择给定的domain和type组合的系统默认值。
IPPROTO_TCP TCP传输协议
IPPROTO_UDP UDP传输协议
IPPROTO_SCTP SCTP传输协议
函数描述:
socket 函数在成功时返回一个小的非负整数值,与文件描述符类似,我们称它为套接字描述符,简称 sockfd。为了得到这个套接字描述符,我们只是指定了协议族(IPv4、IPv6
或Unix)和套接字类型(字节流、数据报或原始套接字)。我们并没有指定本地跟远程的协议地址。
2.bind 函数
将一个本地协议地址赋予一个套接字。对于网际网协议,协议地址是32位的IPv4地址和128位的IPv6地址与16位的TCP或UDP端口号的组合。bind 函数主要用于服务器端,用来指定本地
主机的哪个网络接口(IP,可以是INADDR_ANY,表示本地主机的任一网络接口)可以接受客户端的请求,和指定端口号(即开启的等待客户来连接的进程)。
#include <sys/socket.h>
int bind(int sockfd, const struct sockaddr *myaddr, socklen_t addrlen);
返回:若成功则为0,出错则为-1。
参数说明:
sockfd: socket 函数返回的套接字描述符。
myaddr、addrlen:指向一个套接字地址结构的指针和该结构的大小。
struct 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同样大小 */
};
函数描述:
对于 TCP ,调用 bind 函数可以指定一个端口号,或指定一个IP地址,也可以两者都指定,还可以两者都不指定。
服务器在启动时捆绑它们众所周知的端口号(如何捆绑?)。如果一个TCP客户或服务器未曾调用bind捆绑一个端口,当调用 connect 或 listen 时,内核就要为相应的套接字选择一个临时端口。
让内核来选择临时端口对于TCP客户来说是正常的,除非应用需要一个预留端口。然而对于TCP服务器来说却极为罕见,因为服务器是通过它们的众所周知的端口号来被大家认识的。
进程可以把一个特定的IP捆绑到它的套接字上,不过这个IP地址必须属于其所在主机的网络接口之一(对于TCP服务器)。对于TCP客户,这就为在该套接字上发送的IP数据报指派了源IP地址(服务器源地址)。对于TCP服务器,这就限定该套接字只接收那些目的地为这个IP地址的客户连接。TCP套接字通常不把IP地址捆绑到它的套接字上。当连接套接字时,内核将根据所用外出网络接口来选择源IP地址,而所用外出端口则取决于到达服务器所需的路径。如果TCP服务器没有把IP地址捆绑到它的套接字上,内核就会把发送的SYN的目的IP地址作为服务器的源IP地址(即服务器IP等于INADDR_ANY的情况)。
实际上客户的源IP地址就是服务器的目的地址,服务器的源IP地址就是客户的目的地址,说到底也就只存在两个IP地址:客户IP跟服务器IP。
3.connec函数
t TCP 客户用 connect 函数来与 TCP 服务器建立连接。
#include <sys/socket.h>
int connect( int sockfd, const struct sockaddr *servaddr, socklen_t addrlen );
返回:若成功则为0,出错则为-1。
参数说明:
sockfd: 由 socket 函数返回的套接字描述符。
servaddr、addrlen:指向一个套接字地址结构的指针和该结构的大小。套接字地址结构必须含有服务器的IP地址和端口号。
函数描述:
客户在调用 connect 函数前并不一定得调用 bind 函数,如果需要的话,内核会确定源P地址,并选择一个临时端口作为源端口。所以在客户进程中的套接字一般只需指明客户所要连接的服务器的IP跟端口号。
如果是 TCP 套接字,调用 connect 函数将激发 TCP 的三路握手。而且仅在连接成功或出错时才返回。其中出错的情况有如下几种:
1-> TCP 客户没有收到 SYN 分节的响应。
2-> TCP 服务器对客户的 SYN 分节的响应是 RST 。
3-> 客户发出的 SYN 分节在某个路由器器上发生了错误。
若 connect 调用失败则该套接字不再可用,必须关闭,我们不能对这样的套接字再次执行 connect 函数。
4.listen 函数
#include <sys/socket.h>
int listen(int sockfd, int backlog);
返回:若成功则为0,出错则为-1。
函数描述:
listen 函数仅由 TCP 服务器调用,它做两件事情。
(1)把一个未连接的套接字(主动)转换成一个被动套接字,指示内核应该接受指向该套接字的连接请求。
(2)backlog 参数规定了内核应该为相应套接字排队的最大连接数。其中内核始终为监听套接字维护两个队列。
(1)未完成连接队列,每个SYN分节对于其中一项:
已由某个客户发出并到达服务器,而服务器正在等待待完成的TCP三路握手过程。这些套接字处于SYN_RCVD状态。
(2)已完成连接队列
每个已完成TCP三路握手过程的客户对应其中一项。这些套接字处于ESTABLISHED状态。
backlog 就是这两个队列和的最大值。
在三路握手完成之后,但在服务器调用 accept 之前到达的数据应由 TCP 服务器排队,最大数据量为相应已连接套接字的接收缓冲区的大小。
5.accept 函数
accept 函数由 TCP 服务器调用,用于从已完成连接队列头返回下一个已完成连接。如果已完成队列为空,那么进程被投入睡眠(假设套接字为默认的阻塞方式)。
#include <sys/socket.h>
int accept(int sockfd ,struct sockaddr *cliaddr, socklen_t *addrlen);
返回:若成功则为非负已连接描述符和对端的IP和端口号,出错则为-1。
参数说明:
cliaddr、addrlen 用来返回已连接的对端进程(客户)的协议地址 。调用前,我们将由 *addrlen 所引用的整数值置为由cliaddr所指的套接字地址结构的长度,返回时,该整数值即为内核存放在该套接字地址机构内的确切字节数。
函数描述:
如果 accept 调用成功,那么其返回值是由内核自动生成的一个全新描述符,代表着与所返回客户的TCP连接。在讨论 accept 函数时,我们称它的第一个参数为监听套接字描述符(由 socket 创建,随后用作bind 和 listen 的第一个参数的描述符),称它的返回值为已连接套接字描述符。区分这两个套接字非常重要。一个服务器通常仅仅创建一个监听套接字,它在服务器的生命期内一直存在。内核为每个服务器进程接受的客户连接创建一个已连接套接字(也就是说对于它的TCP三路握手过程已经完成)。当服务器完成对某个连接客户的服务时,相应的已连接套接字就要被关闭。
6.recv/recvfrom函数
从套接字上接收一个消息。对于recvfrom ,可同时应用于面向连接的和无连接的套接字。recv一般只用在面向连接的套接字,几乎等同于recvfrom,只要将recvfrom的第五个参数设置NULL。如果消息太大,无法完整存放在所提供的缓冲区,根据不同的套接字,多余的字节会丢弃。假如套接字上没有消息可以读取,除了套接字已被设置为非阻塞模式,否则接收调用会等待消息的到来。
#include <sys/types.h>
#include <sys/socket.h>
ssize_t recv(int sock, void *buf, size_t len, int flags);
ssize_t recvfrom(int sock, void *buf, size_t len, int flags, struct sockaddr *from, socklen_t *fromlen);
参数:
sock:索引将要从其接收数据的套接字。
buf:存放消息接收后的缓冲区。
len:buf所指缓冲区的容量。
flags:是以下一个或者多个标志的组合体,可通过or操作连在一起
MSG_DONTWAIT:操作不会被阻塞。
MSG_ERRQUEUE:指示应该从套接字的错误队列上接收错误值,依据不同的协议,错误值以某种辅佐性消息的方式传递进来,使用者应该提供足够大的缓冲区。导致错误的原封包通过msg_iovec作为一般的数据来传递。导致错误的数据报原目标地址作为msg_name被提供。错误以sock_extended_err结构形态被使用,定义如下
#define SO_EE_ORIGIN_NONE 0
#define SO_EE_ORIGIN_LOCAL 1
#define SO_EE_ORIGIN_ICMP 2
#define SO_EE_ORIGIN_ICMP6 3
struct sock_extended_err
{
u_int32_t ee_errno; /* error number */
u_int8_t ee_origin; /* where the error originated */
u_int8_t ee_type; /* type */
u_int8_t ee_code; /* code */
u_int8_t ee_pad;
u_int32_t ee_info; /* additional information */
u_int32_t ee_data; /* other data */
/* More data may follow */
};
MSG_PEEK:指示数据接收后,在接收队列中保留原数据,不将其删除,随后的读操作还可以接收相同的数据。
MSG_TRUNC:返回封包的实际长度,即使它比所提供的缓冲区更长, 只对packet套接字有效。
MSG_WAITALL:要求阻塞操作,直到请求得到完整的满足。然而,如果捕捉到信号,错误或者连接断开发生,或者下次被接收的数据类型不同,仍会返回少于请求量的数据。
MSG_EOR:指示记录的结束,返回的数据完成一个记录。
MSG_TRUNC:指明数据报尾部数据已被丢弃,因为它比所提供的缓冲区需要更多的空间。
MSG_CTRUNC:指明由于缓冲区空间不足,一些控制数据已被丢弃。
MSG_OOB:指示接收到out-of-band数据(即需要优先处理的数据)。
MSG_ERRQUEUE:指示除了来自套接字错误队列的错误外,没有接收到其它数据。
from:指向存放对端地址的区域,如果为NULL,不储存对端地址。
fromlen:作为入口参数,指向存放表示from最大容量的内存单元。作为出口参数,指向存放表示from实际长度的内存单元。
7.send/sendto函数
用于发送消息。send只可用于基于连接的套接字,send 和 write唯一的不同点是标志的存在,当标志为0时,send等同于write。sendto 和 sendmsg既可用于无连接的套接字,也可用于基于连接的套接字。除了套接字设置为非阻塞模式,调用将会阻塞直到数据被发送完。
#include <sys/types.h>
#include <sys/socket.h>
ssize_t send(int sock, const void *buf, size_t len, int flags);
ssize_t sendto(int sock, const void *buf, size_t len, int flags, const struct sockaddr *to, socklen_t tolen);
ssize_t sendmsg(int sock, const struct msghdr *msg, int flags);
参数:
sock:索引将要从其发送数据的套接字。
buf:指向将要发送数据的缓冲区。
len:以上缓冲区的长度。len可以大于sizeof(buf),例如send("123",10)-->则实际发送10字节数据
flags:是以下零个或者多个标志的组合体,可通过or操作连在一起
MSG_DONTROUTE:不要使用网关来发送封包,只发送到直接联网的主机。这个标志主要用于诊断或者路由程序。
MSG_DONTWAIT:操作不会被阻塞。
MSG_EOR:终止一个记录。
MSG_MORE:调用者有更多的数据需要发送。
MSG_NOSIGNAL:当另一端终止连接时,请求在基于流的错误套接字上不要发送SIGPIPE信号。
MSG_OOB:发送out-of-band数据(需要优先处理的数据),同时现行协议必须支持此种操作。
to:指向存放接收端地址的区域,可以为NULL。
tolen:以上内存区的长度,可以为0。
msg:指向存放发送消息头的内存缓冲,结构形态如下
struct msghdr {
void *msg_name;
socklen_t msg_namelen;
struct iovec *msg_iov;
size_t msg_iovlen;
void *msg_control;
socklen_t msg_controllen;
int msg_flags;
};
可能用到的数据结构有
struct cmsghdr {
socklen_t cmsg_len;
int cmsg_level;
int cmsg_type;
};
8.getsockname 函数
获取一个套接口的本地名字。
#include <sys/socket.h>
int getsockname(int sockfd, struct sockaddr *localaddr, socklen_t *addrlen);
返回:成功则不返回,出错则为-1。
参数:
sockfd:标识一个已捆绑套接口的描述字。
localaddr:接收套接口的地址(名字)。
addrlen:名字缓冲区长度。
函数描述:
getsockname()函数用于获取一个套接字的名字。它用于一个已捆绑或已连接套接字sockfd,本地地址将被返回。本调用特别适用于如下情况:未调用bind()就调用了connect(),这时唯有getsockname()调用可以获知系统内定的本地地址。在返回时,namelen参数包含了名字的实际字节数。
若一个套接字与INADDR_ANY捆绑,也就是说该套接字可以用任意主机的地址,此时除非调用connect()或accept()来连接,否则getsockname()将不会返回主机IP地址的任何信息。除非套接字被连接,WINDOWS套接字应用程序不应假设IP地址会从INADDR_ANY变成其他地址。这是因为对于多个主机环境下,除非套接字被连接,否则该套接字所用的IP地址是不可知的。
9.getpeername 函数函数
获取与套接口相连的端地址。
int getpeername(int sockfd, struct sockaddr *peeraddr, socklen_t *addrlen);
返回:成功则不返回,出错则为-1。
函数描述:
getpeername()函数用于从端口sockfd中获取与它捆绑的端口名,并把它存放在sockaddr类型的name结构中。它适用于数据报或流类套接口。
10.setsockopt/getsockopt函数
设置/获取套接口选项
#include <winsock.h>
int setsockopt(int sockfd, int level, int optname, void *optval, int optlen);
int getsockopt(int sockfd, int level, int optname, void *optval, int *optlen) ;
参数:
s:标识一个套接口的描述字。
level:选项定义的层次;目前仅支持SOL_SOCKET和IPPROTO_TCP层次。
optname:需设置的选项。
optval:指针,指向存放选项值的缓冲区。
optlen:optval缓冲区的长度。
注释:
setsockopt()函数用于任意类型、任意状态套接口的设置选项值。尽管在不同协议层上存在选项,但本函数仅定义了最高的“套接口”层次上的选项。选项影响套接口的操作,诸如加急数据是否在普通数据流中接收,广播数据是否可以从套接口发送等等。