linux socket网络编程(二)

1、套接字的基本结构

1)、struct sockaddr
这个结构用来存储套接字地址。
数据定义:

struct sockaddr {
	sa_family_t	sa_family;	/* address family, AF_xxx	*/
	char		sa_data[14];	/* 14 bytes of protocol address	*/
};
sa_family 一般来说,都是“AFINET”。
sa_data 包含了一些远程电脑的地址、端口和套接字的数目,它里面的数据是混杂在一切的。

为了处理struct sockaddr,linux内核建立了另外一个相似的结构体struct sockaddr_in:

struct sockaddr_in {
  sa_family_t		sin_family;	/* Address family		*/
  __be16		sin_port;	/* Port number			*/
  struct in_addr	sin_addr;	/* Internet address		*/

  unsigned char		__pad[__SOCK_SIZE__ - sizeof(short int) - sizeof(unsigned short int) - sizeof(struct in_addr)];
};
这个结构提供了方便的手段来访问socket address(struct sockaddr)结构中的每一个元素。注意__pad[] 是为了是两个结构在内存中具有相同的尺寸,使用sockaddr_in 的时候要把__pad 全部设成零值(使用bzero()或memset()函数)。而且,有一点很重要,就是一个指向struct sockaddr_in 的指针可以声明指向一个sturct sockaddr 的结构。所以虽然
socket() 函数需要一个structaddr * ,你也可以给他一个sockaddr_in * 。注意在structsockaddr_in 中,sin_family 相当于在 struct sockaddr 中的sa_family,需要设“AF_INET”。最后一定要保证sin_port 和sin_addr 必须是网络字节顺序。
2)、struct in_addr

struct in_addr {
	__be32	s_addr;
};
如果你声明了一个inadd 作为一个struct sockaddr_in 的结构, 那么“inadd.sin_addr.s_addr”就是4 个字节的IP 地址(按网络字节顺序排放)。
2、基本转换函数

        前面多次提到了网络字节顺序的问题。那么什么是网络字节顺序呢?它有什么特殊的作用。

1)、网络字节顺序

        每一款CPU都有对变量字节存储的顺序(大端or小端),而网络传输的数据大家是一定要统一顺序的。所以对内部的字节表示顺序和网络字节顺序不同的机器需要对数据进行一定的转换。

        linux内部已经为我们准备了相关的字节顺序转换函数:

(1)、htons()——“Host to Network Short”主机字节顺序转换为网络字节顺序(对无符号短型进行操作4 bytes)
(2)、htonl()——“Host to Network Long” 主机字节顺序转换为网络字节顺序(对无符号长型进行操作8 bytes)
(3)、ntohs()——“Network to Host Short “ 网络字节顺序转换为主机字节顺序(对无符号短型进行操作4 bytes)
(4)、ntohl()——“Network to Host Long “ 网络字节顺序转换为主机字节顺序(对无符号长型进行操作8 bytes)
(5)、inet_addr() 返回的地址已经是网络字节顺序了,你没有必要再去调用htonl()
(6)、inet_ntoa() 使用struct in_addr 作为一个参数,不是一个长整型值。
(7)、inet_ntoa() 返回一个字符指针,它指向一个定义在函数inet_ntoa() 中的static 类型字符串。所以每次你调用inet_ntoa(),都会改变最后一次调用inet_ntoa() 函数时得到的结果。
3、基本套接字调用

        在面向连接的通讯中服务器和客户机在交换数据之前先要建立一个连接.再不连接通讯中数据被作为信息的一部分被交换.无论那一种方式,服务器总是最先启动,把自己绑定(Banding)在一个套接字上,然后侦听信息.服务器究竟怎样试图去侦听就得依靠你编程所设定的连接的类型了。
你需要了解的一些系统调用:

 socket()
 bind()
 connect()
 listen()
 accept()
 send()
 recv()
 sendto()
 recvfrom()
 close()
 shutdown()
 setsockopt()
 getsockopt()
 getpeername()
 getsockname()
 gethostbyname()
 gethostbyaddr()
 getprotobyname()
 fcntl()
(1)、socket() 函数
取得套接字描述符(在linux 系统中,任何东西都是一个文件),socket 函数的定义是下面这样子的:
#include <sys/types.h>
#include <sys/socket.h>
int socket(int domain , int type , int protocol);

domain表示socket的协议,一般采用IPv4需要被设置为“AF_INET”。

type是需要解决socket 是什么类型,“SOCK_STREAM”或是“SOCK_DGRAM”。

protocol一般设置为0。

socket()函数只是简单的返回一个你以后可以使用的套接字描述符。如果发生错误,socket()函数返回–1 。全局变量errno 将被设置为错误代码。

(2)、bind() 函数

bind()函数可以帮助你指定一个套接字使用的端口。当你使用socket() 函数得到一个套接字描述符,你也许需要将socket 绑定上一个你的机器上的端口。

#include <sys/types.h>
#include <sys/socket.h>
int bind (int sockfd , struct sockaddr *my_addr , int addrlen) ;

sockfd: 是由socket()函数返回的套接字描述符。
my_addr: 是一个指向struct sockaddr 的指针,包含有关你的地址的信息:名称、端口和IP 地址。
addrlen: 可以设置为sizeof(struct sockaddr)。

当bind()函数调用错误的时候,它也是返回–1 作为错误发生的标志。errn 的值为错误代码。
(3)、connect()函数

假设是一个Telnet 应用程序。你建立一个套接字描述符。你遵从命令,调用了socket()。然后,使用者告诉你连接到“166.111.69.52”的23 端口(标准的Telnet 端口)⋯⋯你应该怎么做呢?Telnet 应用程序,你现在正在阅读的就是套接字的进行网络连接部分:connect()。
connect() 函数的定义是这样的:

#include <sys/types.h>
#include <sys/socket.h>
int connect (int sockfd, struct sockaddr *serv_addr, int addrlen);
sockfd :套接字文件描述符,由socket()函数返回的。
serv_addr: 是一个存储远程计算机的IP 地址和端口信息的结构。
addrlen: 应该是sizeof(struct sockaddr)。

如果发生了错误(比如无法连接到远程主机,或是远程主机的指定端口无法进行连接等)它将会返回错误值–1 。全局变量 errno将会存储错误代码。
(4)、listen() 函数

listen()函数是等待别人连接,进行系统侦听请求的函数。当有人连接你的时候,你有两步需要做:通过listen()函数等待连接请求,然后使用accept()函数来处理。

#include <sys/socket.h>
int listen(int sockfd, int backlog);
sockfd: 是一个套接字描述符,由socket()系统调用获得。
backlog: 是未经过处理的连接请求队列可以容纳的最大数目。

listen()如果返回–1 ,那么说明在listen()的执行过程中发生了错误。全局变量errno 中存储了错误代码。

(5)、accept()函数

函数accept()有一些难懂。当调用它的时候,大致过程是下面这样的:
1)、有人从很远很远的地方尝试调用 connect()来连接你的机器上的某个端口(当然是你已经在listen()的)。
2)、他的连接将被 listen 加入等待队列等待accept()函数的调用(加入等待队列的最多数目由调用listen()函数的第二个参数backlog 来决定)。
3)、你调用 accept()函数,告诉他你准备连接。
4)、accept()函数将回返回一个新的套接字描述符。

#include <sys/socket.h>
int accept(int sockfd, void *addr, int *addrlen);
sockfd: 是正在listen() 的一个套接字描述符。
addr: 一般是一个指向struct sockaddr_in 结构的指针;里面存储着远程连接过来的计算机的信息(比如远程计算机的IP 地址和端口)。
addrlen: 是一个本地的整型数值,在它的地址传给accept() 前它的值应该是sizeof(struct sockaddr_in);accept()不会在addr 中存储多余addrlen bytes 大小的数据。如果accept()函数在addr 中存储的数据量不足addrlen,则accept()函数会改变addrlen 的值来反应这个情况。

如果调用accept()失败的话,accept()函数会返回–1 来表明调用失败,同时全局变量errno 将会存储错误代码。
(6)、send()、recv()函数

这两个函数是最基本的,通过连接的套接字流进行通讯的函数。

#include <sys/types.h>
#include <sys/socket.h>
int send(int sockfd, const void *msg, int len, int flags);
sockfd: 是代表你与远程程序连接的套接字描述符。
msg: 是一个指针,指向你想发送的信息的地址。
len: 是你想发送信息的长度。
flags: 发送标记。一般都设为0
,send()函数如果发生错误,则返回–1 ,错误代码存储在全局变量errno 中。

#include <sys/types.h>
#include <sys/socket.h>
int recv(int sockfd, void *buf, int len, unsigned int flags);
sockfd: 是你要读取数据的套接字描述符。
buf: 是一个指针,指向你能存储数据的内存缓存区域。
len: 是缓存区的最大尺寸。
flags: 是recv() 函数的一个标志,一般都为0
recv() 返回它所真正收到的数据的长度。(也就是存到buf 中数据的长度)。如果返回–1 则代表发生了错误(比如网络以外中断、对方关闭了套接字连接等),全局变量errno 里面存储了错误代码。

(7)、sendto() 和recvfrom() 函数

这两个函数是进行无连接的UDP 通讯时使用的。

#include <sys/types.h>
#include <sys/socket.h>
int sendto(int sockfd, const void *msg, int len, unsigned int flags,const struct sockaddr *to, int tolen);
msg: 是一个指针,指向你想发送的信息的地址。
len: 是你想发送信息的长度。
flags: 发送标记。一般都设为0。(你可以查看send 的man pages 来获得其他的参数值并且明白各个参数所代表的含义)
to: 是一个指向struct sockaddr 结构的指针,里面包含了远程主机的IP 地址和端口数据。
tolen: 只是指出了struct sockaddr 在内存中的大小sizeof(struct sockaddr)。
sendto()返回它所真正发送的字节数(当然也和send()一样,它所真正发送的字节数可能小于你所给它的数据的字节数)。当它发生错误的时候,也是返回 –1 ,同时全局变量errno 存储了错误代码。

#include <sys/types.h>
#include <sys/socket.h>
int recvfrom(int sockfd, void *buf, int len, unsigned int flags,struct sockaddr *from, int *fromlen);
buf: 是一个指针,指向你能存储数据的内存缓存区域。
len: 是缓存区的最大尺寸。
flags: 是recv() 函数的一个标志,一般都为0 (具体的其他数值和含义请参考recv()的man pages)。
from: 是一个本地指针,指向一个struct sockaddr 的结构(里面存有源IP 地址和端口数).
fromlen: 是一个指向一个int 型数据的指针,它的大小应该是sizeof ( struct sockaddr).当函数返回的时候,formlen 指向的数据是form 指向的struct sockaddr 的实际大小.
如果发生了错误,它就返回–1 ,全局变量errno存储了错误代码。

(8)、close()和shutdown()函数

程序进行网络传输完毕后,你需要关闭这个套接字描述符所表示的连接。实现这个非常简单,只需要使用标准的关闭文件的函数:close()。

close(sockfd);
执行close()之后,套接字将不会在允许进行读操作和写操作。任何有关对套接字描述符进行读和写的操作都会接收到一个错误。

#include <sys/socket.h>
int shutdown(int sockfd, int how);
sockfd: 是一个你所想关闭的套接字描述符.
how: 可以取下面的值。0 表示不允许以后数据的接收操;1 表示不允许以后数据的发送操作;2 表示和close()一样,不允许以后的任何操作(包括接收,发送数据)
shutdown() 如果执行成功将返回0,如果在调用过程中发生了错误,它将返回–1,全局变量errno 中存储了错误代码.

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值