网络中的进程是通过套接字socket来通信的
网络中的进程是通过套接字socket来通信的
下面介绍socket相关的函数:
一、服务器端
(1) socket函数原型:
int socket(int domain, int type, int protocol);
地址族、地址域:domain
常用的协议族有,AF_INET(ipv4)、AF_INET6(ipv6)、
常用的socket类型:type
1.SOCK_STREAM 这个协议是按照顺序的、可靠的、数据完整的基于字节流的连接。这是一个使用最多的socket类型,这个socket是使用TCP来进行传输。
2.SOCK_DGRAM 这个协议是无连接的、固定长度的传输调用。该协议是不可靠的,使用UDP来进行它的连接。
3.SOCK_RAW 这个socket类型提供单一的网络访问,这个socket类型使用ICMP公共协议。
公共协议:protocol
常用的公共协议有,IPPROTO_TCP、IPPTOTO_UDP、IPPROTO_SCTP、IPPROTO_TIPC等,它们分别对应TCP传输协议、UDP传输协议、STCP传输协议、TIPC传输协议。默认为0,即选择IPPROTO_TCP。
返回值:
socket返回的值是一个int型文件描述字,0,1,2分别表示标准输入、标准输出、标准错误。所以其他打开的文件描述符都会大于2, 错误时就返回 -1。
例如: ret = socket(AF_INET,SOCK_STREAM, IPPROTO_TCP;
Printf(“ret=%d\n”, ret);
(2) bind函数原型:
int bind(intsockfd, const struct sockaddr *addr, socklen_t addrlen)
当一个套接字用socket()创建后,存在一个名字空间(地址族),但它没有被命名。bind()将套接字地址(本地主机地址和本地端口地址)与所创建的套接字号联系起来,即将名字赋予套接字,以指定本地半相关。如果没有错误发生,bind()返回0。否则返回SOCKET_ERROR。
sockfd :
是socket描述字,它是通过socket()函数创建了,唯一标识一个socket,故这里写socket()函数的返回值。bind()函数就是将给这个描述字绑定一个名字。
addr:
一个const struct sockaddr*指针,指向要绑定给sockfd(socket描述字)的协议地址。
ipv4对应的地址是: sockaddr_in;ipv6对应的地址是: sockaddr_in6;
struct sockaddr_in {
sa_family_t sin_family; /* //指代协议簇,TCP套接字编程为AF_INET */
in_port_t sin_port; /* 存储端口(网络字节序),一个16位的无符号整数*/
struct in_addr sin_addr; /* 存储IP地址,是一个in_addr结构体internet address */
};
struct in_addr {/* Internet address. */
uint32_t s_addr; /*网络字节序*/
};
addrlen:对应的是地址的长度。
例如: bind (ret, sockaddr_in,20)//通过此函数,就将socket函数所返回的描述字与地址绑定。
网络字节序为大端字节序,由于TCP/IP首部中所有的二进制整数在网络中传输时都要求网络字节序,所以都已大端模式来传输的。所以在使用bind (ret, sockaddr_in,20)将描述字与地址绑定时,务必先将sockaddr_in转为网络字节序后再存放。
下面函数进行转换:
inet_pton ( ) //将ipv4点分十进制数ip地址转换成32位网络字节序
inet_ntop ( ) //将32位网络字节序ip地址转换成点分十进制数ip地址
ntohs( )//将主机的16位端口转换成网络字节序端口
htons( ) //将网络字节序的16位端口转换成主机端口
ntohl( )//将主机的32位端口转换成网络字节序端口
htonl( ) //将网络字节序的32位端口转换成主机端口
(3) 监听函数listen:
intlisten(int sockfd, int backlog);
sockfd:要监听的客户端的socket描述字
backlog:为相应socket可以排队的最大连接个数
TCP服务器端依次调用socket()、bind()、listen()之后,就会监听指定的socket地址了。
二、客户端
(2)socket函数原型:
用法和服务器的socket函数一样,但这里用于创建客户端套接字。
(1)连接函数connect:
intconnect(int sockfd, const struct sockaddr *addr, socklen_t addrlen) ;
sockfd:客户端的socket描述字
addr: 服务器的socket地址
addrlen : socket地址的长度
客户端通过调用connect函数来建立与TCP服务器的连接
客户端依次调用socket()、connect()之后就向服务器发送了一个连接请求。服务器监听到这个请求之后,就会调用accept()函数取接收请求,连接建立成功。
三、通信过程
(1) 套接字接受函数accept
intaccept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
sockfd:为服务器的socket描述字
addr:为指向struct sockaddr *的指针,用于返回客户端的协议地址
addrlen:协议地址的长度
如果accpet成功,返回值是由内核生成的一个新的描述字,代表与客户端的TCP连接。
注意:accept的第一个参数为服务器的socket描述字,该描述字是服务器调用socket()函数生成的,称为监听socket描述字;而accept函数返回的是已连接的客户端socket描述字。
一个服务器只创建一个监听socket描述字,在该服务器的生命周期内一直存在。
内核为每个由服务器进程接受的客户连接创建了一个已连接socket描述字(客户端socket),当服务器完成了对某个客户的服务,相应的已连接的客户端socket就被关闭。
connect()用于建立连接。accept()用于使服务器等待来自某客户进程的实际连接。
(2)读写函数read()/write() (recvmsg()/sendmsg())
ssize_t read(int fd, void *buf, size_t count);
read函数是负责从fd中读取内容到buf中.当读成功时返回实际所读的字节数,失败时返回-1。ssize_t write(int fd, constvoid *buf, size_tcount);
write函数将buf中的count字节内容写入文件描述符fd.成功时返回写的字节数。
失败时返回-1,
如果返回错误:EINTR 表示在写的时候出现了中断错误
如果返回错误:EPIPE 表示网络连接出现了问题(对方已经关闭了连接)。
(3)关闭相应的socket描述字:close 例如:close(描述字)
在服务器与客户端建立连接之后,会进行一些读写操作,完成了读写操作就要关闭相应的socket描述字,好像要调用fclose关闭打开的文件。 close操作使相应socket描述字的引用计数-1。
四、代码实例
Pv4地址结构的bind函数示例:
struct sockaddr_in mysock;
mysock.sin_family =AF_INET; //TCP地址结构
mysock.sin_port = htons(3333); //端口字节顺序转换
mysock.sin_addr.s_addr = inet_addr(inet_aton("166.111.160.10")); //设置IP地址
服务器和客户端进行通信,那么在服务器和客户端就要分别有一套代码来实现:
服务器端编程的步骤:
1:创建套接字(socket());
2:绑定套接字到一个IP地址和一个端口上(bind());
3:将套接字设置为监听模式等待连接请求(listen());
4:请求到来后,接受连接请求(accept()),返回一个新的对应于此次连接的套接字;
5:用返回的套接字和客户端进行通信,即读/写(send()/recv());
6:返回,等待另一连接请求;
7:关闭套接字,关闭加载的套接字库(close())。
客户端编程的步骤:
1:创建套接字(socket());
2:向服务器发出连接请求(connect());
3:和服务器端进行通信,即读/写(send()/recv());
4:关闭套接字 (close())。
实际代码实例参见:
http://blog.****.net/fly_yr/article/details/50387065
http://blog.****.net/u011819804/article/details/39589489