10、套接字socket的编程(学习笔记)

来源:互联网 发布:sql的dcl 编辑:程序博客网 时间:2024/05/29 19:22

1、套接字

socket是最通用的网络通信应用程序的开发接口。linux的网络编程就是套接字编程。

套接字编程的头文件:

#include <sys/types.h>

#include <socket.h>

#include <netinet/in.h>

2、套接字地址结构

1)套接字地址结构:

struct sockaddr{                          //不用此结构体编程

unsigned short  sa_family;//地址类型,AF_XXX

char               sa_data[14];//14字节地址协议

}

sa_family表示套接字协议族类型,对应的TCP/IP协议,该值为AF_INET。

sa_data存储具体的协议地址,被定义成14字节是因为有的协议族使用较长的地址格式。

*一般在编程中并不对该结构体进行操作,而是使用一个与它等价的数据结构:

struct sockaddr_in{

unsigned  longsin_family;//地址类型TCP/IP编程只能是AF_INET

unsigned  short intsin_port;//端口号

struct  in_addrsin_addr;//IP地址.32位的IP地址

unsigned charsin_zero[8];//填充字节,一般赋值为0

};

注:

struct in_addr{

unsigned longs_addr;

};

2)设置地址信息代码:TCP/IP编程

struct sockaddr_in sock;

sock.sin_family = AF_INET;

sock.sin_port = htons(80);

sock.sin_addr.s_addr = inet_addr("211.203.13.132");

memset(sock.sin_zero, 0, sizeof(sock.sin_zero));

3、创建套接字

socket函数用来创建套接字:

函数原型:int  socket(int domain,  int type,  int protocol);

domain:创建套接字所使用的协议族;

type:套接字类型;

protocol:常设置为0,表示通过参数domain指定的协议族和type的类型来确定使用的协议。当创建原始套接字时,系统无法唯一确定协议,此时需要用该参数来指定使用的协议。

返回值:成功返回一个新创建的套接字,否则返回-1.

常用的协议族:AF_UNIX,创建只在本机内的套接字;AF_INET,IPv4 TCP/IP协议;AF_INET6,IPv6协议;SOCK_STREAM,创建TCP套接字;SOCK_DGRAM,创建UDP数据报套接字;SOCK_RAM,创建原始套接字.

函数用法示例:

//创建一个TCP套接字

int   sock_fd;   //  4中有应用

sock_fd = socket(AF_INET,  SOCK_STREAM,  0);

4、在套接字上创建连接

函数connect用来在一个指定的套接字上创建一个连接。connect函数用于向服务器发出连接请求,服务器的IP地址和端口号由参数serv_addr指定。

函数原型:int  connect(int sockfd,  const struct  sockaddr *serv_addr,  socklen_t  addrlen);

sockfd:是一个由函数socket()创建的套接字。

serv_addr:是一个地址结构。

addrlen:为参数serv_addr的长度。

函数用法示例:

struct  sockaddr_in serv_addr;

...

/*inet_aton()函数将一个字符串转换成一个网络地址,并把该网络地址赋值给第二个参数*/

if(inet_aton("163.125.252.12",  &serv_addr.sin_addr) < 0)

{错误提示}

if(connect(sock_fd,  (struct sockaddr *)&serv_addr,  sizeof(struct sockaddr_in)) < 0)//sock_fd在上面有定义

{错误提示}

5、绑定套接字(服务器端)

函数bind将一个套接字与某个端口绑定在一起(服务器端应用的函数)。在客户机/服务器模型中,服务器端的IP地址和端口号一般是固定的,在服务器的程序中使用bind函数将一个套接字和某个端口绑定在一起。

函数原型:int  bind(int  sockfd,  struct sockaddr  *my_addr,  socklen_t  addrlen);

sockfd:函数socket创建的套接字。

my_addr:指定了sockfd将绑定到的本地地址。

addrlen:参数my_addr的长度。

返回值:成功返回0,否则-1.

函数用法示例:

struct  sockaddr_in serv_addr;

...

if(bind(sock_fd,  (struct sockaddr *)&serv_addr,  sizeof(struct sockaddr_in)) < 0)

{错误提示}

6、套接字 监听(等待请求连接)

函数socket创建的套接字可以用来主动请求连接到某个服务器(connect函数),在服务器端,通常在某个端口监听等待来自客户端的连接请求。

流程:服务器 socket创建套接字----bind函数将套接字绑定到某个端口-----listen将该套接字转化为监听套接字,等待客户端的连接请求。

函数原型:int  listen(int  sockfd, int  backlog);

sockfd:一个已绑定而未被连接的套接字描述符。

backlog:指定该连接队列的最大长度。超过最大长度之后的连接请求被服务器拒绝。

返回值:成功返回0,否则-1.

函数用法示例:

#define  LISTEN_MAX   10//定义请求连接队列的长度

...

if(listen(sock_fd,  LISTEN_MAX) < 0)

{错误提示}

7、接收一个连接请求

函数accept用来接收一个连接请求。该函数只能对面向连接的套接字使用。

函数原型:int  accept(int sockfd,  struct sockaddr *addr,  socklen_t  *addrlen);

sockfd:由函数socket创建,经函数bind绑定到本地的某一端口上,然后通过函数listen转化而来的监听套接字。

addr:用来保存发起连接请求的主机地址和端口。

addrlen:是addr所指向的结构体的大小。

返回值:成功,将创建一个新的套接字,并为这个新的套接字分配一个套接字描述符,并返回这个新的套接字描述符。进程可以利用这个新的套接字描述符与客户端交换数据。

函数用法示例:

int   client_len;

struct sockaddr_in client_addr;

...

client_len = sizeof(struct sockaddr_in);

if(accept(sock_fd,  ()&client_addr, &client_len) < 0)

{错误提示}

8、TCP套接字的数据传输

1)发送数据

函数send用来在TCP套接字上发送数据,该函数只能对处于连接状态的套接字使用。

函数原型:ssize_t  send(int sockfd,  const void *msg,  size_t len, int flags);

sockfd:为已建立好连接的套接字描述符,即accept函数的返回值。

msg:指向存放待发送数据的缓冲区。

len:待发送数据的长度。

flags:控制选项。一般设置为0,或其他值。取值:MSG_OOB,支持带外数据;MSG_DONTROUTE,忽略下层协议的路由设置。

返回值:成功返回实际发送数据的字节数,否则-1.

2)接收数据

函数recv用来在TCP套接字上接收数据。

函数原型:ssize_t  recv(int  sockfd, void *buf,  size_t  len, int  flags);

sockfd:指定的套接字描述符

buf:指定的缓冲区

len:缓冲区长度

flags:控制选项,一般为0。其他:MSG_OOB,MSG_PEEK,MSG_WAITALL。

返回值:成功返回接收到的数据字节数,否则-1.

9、UDP套接字的数据传输(不需要套接字处于连接状态)

1)发送数据

ssize_t  sendto(int s,  const void* msg,  size_t len,  int  flags,  const  struct sockaddr *to,  socklen_t  tolen);

...(同send)

to:指定目的地址

tolen:目的地址长度

返回值:成功返回发送数据的字节数,否则-1

2)接收数据

ssize_t   recvfrom(int  sockfd, void *buf,  size_t  len, int  flags,  struct sockaddr *from,  socklen_t  fromlen);

...

from:保存数据的源地址

fromlen:from的实际大小

10、关闭套接字

头文件:#include <unistd.h>

1)函数close用来关闭一个套接字描述符

函数原型:int  close(int  fd);

fd:一个套接字的描述符

返回值:成功0,否则-1.

2)函数shutdown也用来关闭一个套接字描述符

函数原型:int  shutdown(int  sockfd,  int  how);

sockfd:待关闭的套接字描述符

how:指定关闭的方式。SHUT_RD,SHUT_WR,SHUT_RDWR(读写通道都关闭)。

返回值:成功0,否则-1.

11、系统调用函数

1)字节顺序

大端格式:高字节数据存储在低地址上,低字节数据存储在高地址上(高低)。小端格式,反之(高高,低低)。

TCP/IP协议规定在网络上必须采用网络字节顺序(大端格式)

2)字节顺序转换函数

char型数据可以不用考虑大端小端,如缓冲区数据,非char型的数据需要在数据发送前转换成大端格式,数据接收后在转换成符合主机接收的存储模式。

函数原型:

#include <netinet/in.h>

uint32_t  htonl(uint32_t  hostlong );//how to network long(htonl),将主机unsigned int型数据转换成网络字节顺序。

uint16_t  htons(uint16_t  hostshort );//将主机unsigned short型数据转换成网络字节顺序。

uint32_t  ntohl(uint32_t  hostlong ); //取反

uint16_t  ntohs(uint32_t  hostlong );//取反

3)网络地址格式转换(inet系列函数)

常使用的网络地址是字符串形式的,如“169.126.254.124”,数据通信时使用的是二进制形式且为网络字节顺序的IP地址,如0x952f155.

函数原型:

#include  <sys/socket.h>

#include  <netinet/in.h>

#include  <arpa/inet.h>

inet_aton();//将字符串形式的IP地址转换成二进制的网络字节顺序地址。

inet_network();//将字符串形式的网络地址,转换为主机字节顺序的二进制IP地址。

inet_ntoa();//将网络字节顺序的二进制IP地址,转换为以“.”分割的字符串形式。

inet_makeaddr();//把两个地址组合成一个网络地址。

inet_lnaof();//取出主机地址

inet_netof();//取出网络地址

4)多路复用方法

C/S模型中,服务器需要同时处理多个客户端连接请求,此时需要用多路复用。

方法:1、非阻塞方式套接字。2、服务器进程不主动询问套接字状态,而是向系统登记希望监视的套接字,然后租塞。

第二种方法

函数原型:int  select(int  n, fd_set  *readfds,  fd_set *writefds, fd_set *exceptfds,  struct timeval *timeout);

n:需要监控的文件描述符个数

readfds:监视可读文件描述符集合,当这个集合中有一个文件描述符到达时,系统会调用select函数。

writefds:监视可写文件描述符集合。

exceptfds:监视异常文件描述符集合。

select:指定阻塞时间。

注:相关宏定义

FD_CLR(int fd,  fd_set  *set);//将文件描述符fd从文件描述符集合set中删除。

FD_ISSET(int fd, fd_set *set);//测试fd是否在set中。

FD_SET(int fd, fd_set *set);//在文件描述符集合set中增加文件描述符fd。

FD_ZERO(fd_set *set);//将文件描述符集合set清空。

计时结构体:

struct  timeval{

long tv_sec;//seconds

long tv_usec;//microseconds

12、getsockopt()

套接字创建以后,就可以利用它来传输数据,但有时可能对套接字的工作方式有特殊要求,此时用此函数来控制套接字的属性。

int getsockopt(int sockfd, int level, int optname, void *optval, socklen_t *optlen);

        sockfd:一个标识套接口的描述字。

        level:选项定义的层次。支持的层次仅有SOL_SOCKET和IPPROTO_TCP。

       optname:需获取的套接口选项。

       optval:指针,指向存放所获得选项值的缓冲区。

       optlen:指针,指向optval缓冲区的长度值。

13、setsockopt()

系统中,如果一个socket绑定一个端口,该socket正常关闭或程序异常退出后的一段时间内,该端口依然保持原来的绑定状态,其他端口无法绑定该端口,如果设置了该选项则可以避免这个问题。

int PASCAL FAR setsockopt(SOCKET s,int level,int optname,const char FAR *optval,int optlen);

        s:标识一个套接字的描述符。

level:选项定义的层次;目前仅支持SOL_SOCKET和IPPROTO_TCP层次。

optname:需设置的选项。

optval:指针,指向存放选项值的缓冲区

optlen:optval缓冲区长度。




0 0
原创粉丝点击