udp中socket的用法.docx
《udp中socket的用法.docx》由会员分享,可在线阅读,更多相关《udp中socket的用法.docx(18页珍藏版)》请在冰豆网上搜索。
udp中socket的用法
struct sockaddr
{
unsigned short sa_family;
char sa_data[14];
};
上面是通用的socket地址,此数据结构用做bind、connect、recvfrom、sendto等函数的参数,指明地址信息。
具体到Internet socket,用下面的结构,二者可以进行类型转换 。
struct sockaddr_in
{
short int sin_family;
unsigned short int sin_port;
struct in_addr sin_addr;
unsigned char sin_zero[8];
};
struct in_addr就是32位IP地址。
struct in_addr {
union
{ struct{u_chars_b1,s_b2,s_b3,s_b4;}S_un_b;
struct{u_shorts_w1,s_w2;}S_un_w;
u_longS_addr;
}S_un;
#defines_addr S_un.S_addr
};
in_addr()是将一个点分制的IP地址(如192.168.0.1)转换为上述结构中需要的32位IP地址(0xC0A80001)。
通常的用法是:
int sockfd;
struct sockaddr_in my_addr;
sockfd = socket(AF_INET, SOCK_STREAM, 0);
my_addr.sin_family = AF_INET;
my_addr.sin_port = htons(MYPORT);
my_addr.sin_addr.s_addr = inet_addr("192.168.0.1");
bzero(&(my_addr.sin_zero), 8);
bind(sockfd, (struct sockaddr *)&my_addr, sizeof(struct sockaddr));
sa_family是地址家族,一般都是“AF_xxx”的形式。
通常大多用的是都是AF_INET,代表TCP/IP协议族。
sa_data是14字节协议地址。
sin_family指代协议族,在socket编程中只能是AF_INET
sin_port存储端口号(使用网络字节顺序)
sin_addr存储IP地址,使用in_addr这个数据结构
sin_zero是为了让sockaddr与sockaddr_in两个数据结构保持大小相同而保留的空字节。
sockaddr_in和sockaddr是并列的结构,指向sockaddr_in的结构体的指针也可以指向sockaddr的结构体,并代替它。
也就是说,你可以使用sockaddr_in建立你所需要的信息,然后用进行类型转换就可以了bzero((char*)&mysock,sizeof(mysock));
sin_zero初始值应该使用函数bzero()来全部置零。
最典型的源、目的节点socket定义对于源、目的地址和源、目的地址端口,需要建立两个socket变量cliaddr绑定源地址和源端口。
servaddr用于connect和sendto的设定目的地址和目的端口。
structsockaddr_inservaddr,cliaddr;
create_socket(char*server_addr_string,unsignedintserver_port)
{
源socket赋值
bzero(&cliaddr,sizeof(cliaddr));
cliaddr.sin_family=AF_INET;
通常TCP/UDP协议源地址和端口都是随机的
cliaddr.sin_addr.s_addr=htons(INADDR_ANY);
cliaddr.sin_port=htons(0);
目的socket赋值
bzero(&servaddr,sizeof(servaddr));
servaddr.sin_family=AF_INET;
inet_aton(server_addr_string,&servaddr.sin_addr);
servaddr.sin_port=htons(server_port);
}
NBO,HBO二者转换
inet_addr() 将字符串点数格式地址转化成无符号长整型(unsignedlongs_addrs_addr;)
inet_aton() 将字符串点数格式地址转化成NBO
inet_ntoa() 将NBO地址转化成字符串点数格式
htons() "HosttoNetworkShort"
htonl() "HosttoNetworkLong"
ntohs() "NetworktoHostShort"
ntohl() "NetworktoHostLong"
常用的是htons(),inet_addr()正好对应结构体的端口类型和地址类型
三种给socket赋值地址的方法
inet_aton(server_addr_string,&myaddr.sin_addr);
myaddr.sin_addr.s_addr=inet_addr("132.241.5.10");
myaddr.sin_addr.s_addr=htons(INADDR_ANY);
两种给socket赋值端口的方法
#defineMYPORT3490
myaddr.sin_port=htons(MYPORT);
0(随机端口)
myaddr.sin_port=htons(0);
3.1创建套接字──socket()
应用程序在使用套接字前,首先必须拥有一个套接字,系统调用socket()向应用程序提供创建套接字的手段
3.2 指定本地地址──bind()
当一个套接字用socket()创建后,存在一个名字空间(地址族),但它没有被命名。
bind()将套接字地址(包括本地主机地址和本地端口地址)与所创建的套接字号联系起来,即将名字赋予套接字,以指定本地半相关。
3.3 建立套接字连接──connect()与accept()
这两个系统调用用于完成一个完整相关的建立,其中connect()用于建立连接。
无连接的套接字进程也可以调用connect(),但这时在进程之间没有实际的报文交换,调用将从本地操作系统直接返回。
这样做的优点是程序员不必为每一数据指定目的地址,而且如果收到的一个数据报,其目的端口未与任何套接字建立“连接”,便能判断该端靠纪纪可操作。
而accept()用于使服务器等待来自某客户进程的实际连接
3.4 监听连接──listen()
此调用用于面向连接服务器,表明它愿意接收连接。
listen()需在accept()之前调用
3.5 数据传输──send()与recv()
当一个连接建立以后,就可以传输数据了。
常用的系统调用有send()和recv()。
3.6 输入/输出多路复用──select()
select()调用用来检测一个或多个套接字的状态。
对每一个套接字来说,这个调用可以请求读、写或错误状态方面的信息。
请求给定状态的套接字集合由一个fd_set结构指示。
在返回时,此结构被更新,以反映那些满足特定条件的套接字的子集,同时, select()调用返回满足条件的套接字的数目
3.7 关闭套接字──closesocket()
closesocket()关闭套接字s,并释放分配给该套接字的资源;如果s涉及一个打开的TCP连接,则该连接被释放。
/* 建立套接字 */
Socket()
sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock < 0) {
perror(“opening stream socket”);
exit
(1);
}
/*设置套接口的选项。
*/
Setsockopt()
通讯的基石是套接口,一个套接口是通讯的一端。
在这一端上你可以找到与其对应的一个名字。
一个正在被使用的套接口都有它的类型和与其相关的进程。
套接口存在于通讯域中。
通讯域是为了处理一般的线程通过套接口通讯而引进的一种抽象概念。
intPASCALFARsetsockopt(SOCKETs,
intlevel,
intoptname,
constcharFAR*optval,
intoptlen);
s:
标识一个套接口的描述字。
level:
选项定义的层次;目前仅支持SOL_SOCKET和IPPROTO_TCP层次。
optname:
需设置的选项。
optval:
指针,指向存放选项值的缓冲区。
optlen:
optval缓冲区长度。
有两种套接口的选项:
一种是布尔型选项,允许或禁止一种特性;另一种是整形或结构选项。
允许一个布尔型选项,则将optval指向非零整形数;禁止一个选项optval指向一个等于零的整形数。
对于布尔型选项,optlen应等于sizeof(int);对其他选项,optval指向包含所需选项的整形数或结构,而optlen则为整形数或结构的长度
若无错误发生,setsockopt()返回0。
否则的话,返回SOCKET_ERROR错误
缺省条件下,一个套接口不能与一个已在使用中的本地地址捆绑(参见bind())。
但有时会需要“重用”地址。
因为每一个连接都由本地地址和远端地址的组合唯一确定,所以只要远端地址不同,两个套接口与一个地址捆绑并无大碍。
为了通知WINDOWS套接口实现不要因为一个地址已被一个套接口使用就不让它与另一个套接口捆绑,应用程序可在bind()调用前先设置SO_REUSEADDR选项。
请注意仅在bind()调用时该选项才被解释;故此无需(但也无害)将一个不会共用地址的套接口设置该选项,或者在bind()对这个或其他套接口无影响情况下设置或清除这一选项。
一个应用程序可以通过打开SO_KEEPALIVE选项,使得WINDOWS套接口实现在TCP连接情况下允许使用“保持活动”包。
一个WINDOWS套接口实现并不是必需支持“保持活动”,但是如果支持的话,具体的语义将与实现有关如果有关连接由于“保持活动”而失效,则进行中的任何对该套接口的调用都将以WSAENETRESET错误返回,后续的任何调用将以WSAENOTCONN错误返回。
setsockopt()支持下列选项。
其中“类型”表明optval所指数据的类型。
选项类型意义
SO_BROADCASTBOOL允许套接口传送广播信息。
SO_DEBUGBOOL记录调试信息。
SO_DONTLINERBOOL不要因为数据未发送就阻塞关闭操作。
设置本选项相当于将SO_LINGER的l_onoff元素置为零。
SO_DONTROUTEBOOL禁止选径;直接传送。
SO_KEEPALIVEBOOL发送“保持活动”包。
SO_LINGERstructlingerFAR*如关闭时有未发送数据,则逗留。
SO_OOBINLINEBOOL在常规数据流中接收带外数据。
SO_RCVBUFint为接收确定缓冲区大小。
SO_REUSEADDRBOOL允许套接口和一个已在使用中的地址捆绑(参见bind())。
SO_SNDBUFint指定发送缓冲区大小。
TCP_NODELAYBOOL禁止发送合并的Nagle算法。
bind()将一本地地址与一套接口捆绑。
本函数适用于未连接的数据报或流类套接口,在connect()或listen()调用前使用。
当用socket()创建套接口后,它便存在于一个名字空间(地址族)中,但并未赋名。
bind()函数通过给一个未命名套接口分配一个本地名字来为套接口建立本地捆绑(主机地址/端口号)。
将一本地地址与一套接口捆绑。
#include
intPASCALFARbind(SOCKETs,conststructsockaddrFAR*name,intnamelen);
s:
标识一未捆绑套接口的描述字。
name:
赋予套接口的地址。
namelen:
name名字的长度。
如无错误发生,则bind()返回0。
否则的话,将返回SOCKET_ERROR,应用程序可通过WSAGetLastError()获取相应错误代码。
Recvfrom()
recvfrom函数(经socket接收数据):
函数原型:
intrecvfrom(
intsockfd,
void*buf,
intlen,
unsignedintflags,
structsockaddr*from,
int*fromlen);
函数说明:
recv()用来接收远程主机经指定的socket传来的数据,
并把数据传到由参数buf指向的内存空间,参数len为可接收数据的最大长度.
参数flags一般设0,
其他数值定义参考recv().
参数from用来指定欲传送的网络地址,结构sockaddr请参考bind()函数.
参数fromlen为sockaddr的结构长度.
返回值:
成功则返回接收到的字符数,失败返回-1.
inet_ntoa_b( )
inet_ntoa_b( )-convertannetworkaddresstodotnotation,storeitinabuffer
voidinet_ntoa_b
(
structin_addrinetAddress,/*inetaddress*/
char*pString/*wheretoreturnASCIIstring*/
)
ThisroutineconvertsanInternetaddressinnetworkformattodotteddecimalnotation.
ThisroutineisidenticaltotheUNIXinet_ntoa( )routineexceptthatyoumustprovideabufferofsizeINET_ADDR_LEN.
ntohs()简述:
将一个无符号短整形数从网络字节顺序转换为主机字节顺序。
#include
u_shortPASCALFARntohs(
u_shortnetshort);
netshort:
一个以网络字节顺序表达的16位数。
本函数将一个16位数由网络字节顺序转换为主机字节顺序。
返回值:
ntohs()返回一个以主机字节顺序表达的数。
inet_ntoa()
将网络地址转换成“.”点隔的字符串格式。
#include
charFAR*PASCALFARinet_ntoa(structin_addrin);
in:
一个表示Internet主机地址的结构。
本函数将一个用in参数所表示的Internet地址结构转换成以“.”间隔的诸如“a.b.c.d”的字符串形式。
请注意inet_ntoa()返回的字符串存放在WINDOWS套接口实现所分配的内存中。
应用程序不应假设该内存是如何分配的。
在同一个线程的下一个WINDOWS套接口调用前,数据将保证是有效。
inet_addr()
功能:
字符串转地址
inet_addr(conststructFAR*cp);
cp:
一个以Internet标准“.”间隔的字符串。
本函数解释cp参数中的字符串,这个字符串用Internet的“.”间隔格式表示一个数字的Internet地址。
返回值可用作Internet地址。
所有Internet地址以网络字节顺序返回(字节从左到右排列)。
htons()
u_shortPASCALFARhtons(u_shorthostshort);
htons的功能:
将一个无符号短整型数值转换为网络字节序,即大端模式(big-endian)参数u_shorthostshort:
16位无符号整数返回值:
TCP/IP网络字节顺序.htons是把你机器上的整数转换成“网络字节序”,网络字节序是big-endian,也就是整数的高位字节存放在内存的低地址处。
而我们常用的x86CPU(intel,AMD)电脑是little-endian,也就是整数的低位字节放在内存的低字节处。
sendto()
intPASCALFARsendto(SOCKETs,constcharFAR*buf,
intlen,
intflags,
conststructsockaddrFAR*to,
inttolen);
s:
一个标识套接口的描述字。
buf:
包含待发送数据的缓冲区。
len:
buf缓冲区中数据的长度。
flags:
调用方式标志位。
to:
(可选)指针,指向目的套接口的地址。
tolen:
to所指地址的长度。
请注意成功地完成sendto()调用并不意味着数据传送到达。
sendto()函数主要用于SOCK_DGRAM类型套接口向to参数指定端的套接口发送数据报。
对于SOCK_STREAM类型套接口,to和tolen参数被忽略;这种情况下sendto()等价于send()。
为了发送广播数据(仅适用于SOCK_DGRAM),in参数所含地址应该把特定的IP地址INADDR_BROADCAST(winsock.h中有定义)和终端地址结合起来构造。
通常建议一个广播数据报的大小不要大到以致产生碎片,也就是说数据报的数据部分(包括头)不超过512字节。
若无错误发生,send()返回所发送数据的总数(请注意这个数字可能小于len中所规定的大小)。
否则的话,返回SOCKET_ERROR错误,应用程序可通过WSAGetLastError()获取相应错误代码。
UDP协议的主要作用是将网络数据流量压缩成数据包的形式。
一个典型的数据包就是一个二进制数据的传输单位。
每一个数据包的前8个字节用来包含报头信息,剩余字节则用来包含具体的传输数据。
数据报的长度是指包括报头和数据部分在内的总字节数。
因为报头的长度是固定的,所以该域主要被用来计算可变长度的数据部分(又称为数据负载)。
UDP协议使用报头中的校验值来保证数据的安全。
校验值首先在数据发送方通过特殊的算法计算得出,在传递到接收方之后,还需要再重新计算。
如果某个数据报在传输过程中被第三方篡改或者由于线路噪音等原因受到损坏,发送和接收方的校验计算值将不会相符,由此UDP协议可以检测是否出错。
这与TCP协议是不同的,后者要求必须具有校验值。
UDP和TCP协议的主要区别是两者在如何实现信息的可靠传递方面不同。
TCP协议中包含了专门的传递保证机制,当数据接收方收到发送方传来的信息时,会自动向发送方发出确认消息;发送方只有在接收到该确认消息之后才继续传送其它信息,否则将一直等待直到收到确认信息为止。
与TCP不同,UDP协议并不提供数据传送的保证机制。
如果在从发送方到接收方的传递过程中出现数据报的丢失,协议本身并不能做出任何检测或提示。
因此,通常人们把UDP协议称为不可靠的传输协议。
UDP具有TCP所望尘莫及的速度优势。
虽然TCP协议中植入了各种安全保障功能,但是在实际执行的过程中会占用大量的系统开销,无疑使速度受到严重的影响。
反观UDP由于排除了信息可靠传递机制,将安全和排序等功能移交给上层应用来完成,极大降低了执行时间,使速度得到了保证。
UDP程序:
包括server和client两部分
1、编写UDPServer程序的步骤
(1)使用socket()来建立一个UDPsocket,第二个参数为SOCK_DGRAM。
(2)初始化sockaddr_in结构的变量,并赋值。
sockaddr_in结构定义:
structin_addr
{
in_addr_ts_addr;
};
结构体in_addr用来表示一个32位的IPv4地址.in_addr_t一般为32位的unsignedlong.其中每8位代表一个IP地址位中的一个数值.例如192.168.3.144记为0xc0a80390,其中c0为192,a8为168,03为3,90为144。
structsockaddr_in{
uint8_tsin_len;
sa_family_tsin_family;
in_port_tsin_port;
structin_addrsin_addr;
charsin_zero[8]; };
(3)使用bind()把上面的socket和定义的IP地址和端口绑定。
这里检查bind()是否执行成功,如果有错误就退出。
这样可以防止服务程序重复运行的问题。
(4)进入无限循环程序,使用recvfrom()进入等待状态,直到接收到客户程序发送的数据,就处理收到的数据,并向客户程序发送反馈。
这里是直接把收到的数据发回给客户程序。
udpserv.c程序内容
#include
#defineMAXLINE80
#defineSERV_PORT8888
voiddo_echo(intsockfd,structsockaddr*pcliaddr,socklen_tclilen)
{
intn;
socklen_tlen;
charmesg[MAXLINE];
for(;;)
{
len=clilen;/*waitingforreceivedata*/
n=recvfrom(sockfd,mesg,MAXLINE,0,pcliaddr,&len);
sendto(sockfd,mesg,n,0,pcliaddr,le