Linux网络编程(2):基于TCP的套接字编程

来源:互联网 发布:cyberduck windows 编辑:程序博客网 时间:2024/06/05 09:31

参考资料:

http://www.cnblogs.com/skynet/archive/2010/12/12/1903949.html
http://goodcandle.cnblogs.com/archive/2005/12/10/294652.aspx



    


1、socket函数

#include<sys/socket.h>

int socket(int family, int type, int protocol);

指定期望的通信协议类型(使用IPv4的TCP、Ipv6的UDP、Unix域字节流协议等);其中,family参数指明协议簇,type参数指明套接字类型,protocol参数应该设定为某个协议类型常值,或者设为0.它是某一常数值。

family:AF_INET(IPv4协议),AF_INET6(IPv6协议),AF_LOCAL(Unix域协议),AF_ROUTE(路由套接字),AF_KEY(密钥套接字)

type:SOCK_STREAM(字节流套接字),SOCK_DGRAM(数据报套接字),SOCK_SEQPACKET(有序分组套接字),SOCK_RAW(原始套接字)

protocol:IPPROTO_TCP(TCP传输协议),IPPROTO_UDP(UDP传输协议),IPPROTO_SCTP(SCP传输协议)


2、bind函数

#include<sys/socket.h>

int bind(int sockfd, const struct sockaddr *myaddr, socklen_t addrlen);

       把一个本地协议地址赋予给一个套接字(由socket创建的套接字描述符)。对于网际网协议,协议地址是32位IPv4地址或者128位IPv6地址与16位TCP或UDP端口号的组合。

       对于TCP,调用bind函数可以指定一个端口号,或指定一个IP地址,也可以两者都指定,还可以两者都不指定。如果指定端口号为0,那么内核就在bind被调用时选择一个临时端口,而如果指定IP地址为通配地址,那么内核将等到套接字已连接(TCP)或者已在套接字上发出数据报(UDP)是才选择一个本地IP地址。对于IPv4来说,通配地址由常值INADDR_ANY来指定,其值一般为0,它告诉内核去选择IP地址。有时候,为了得到内核所选择的这个临时端口值,必须调用函数getsocdname来返回协议地址。

struct sockaddr_in servaddr;

servaddr.sin_addr.s_addr = htonl(INADDR_ANY);   //头文件<netinet/in.h>


3、connect函数

#include<sys/socket.h>

int connect(int sockfd, const struct sockaddr *servaddr, socklen_t addrlen);

     TCP客户端用connect函数来建立与TCP服务器的连接。

     sockfd是由socket函数返回的套接字描述符,第二个、第三个参数分别是一个指向套接字地址结构的指针和该结构的大小。此时的套接字地址结构必须含有服务器的IP地址和端口号。

     客户在调用函数connect前,不必非得调用bind函数,因为如果需要的话,内核会确定源IP地址,并选择一个临时端口作为源端口。如果是TCP套接字,调用connect函数将激发TCP的“三次握手”,而且在连接建立成功或出错时才返回。 

[cpp] view plaincopy
  1. struct sockaddr_in servaddr;  
  2. if(argc!=2)  
  3.     err_quit(usage: a.out <IPaddress>");  
  4. if((sockfd = socket(AF_INET, SOCK_STREAM, 0))<0)  
  5.     err_sys("socket error");  
  6. bzero(&servaddr, sizeof(servaddr));  
  7. servaddr.sin_family = AF_INET;  
  8. servaddr.sin_port = htons(13);  
  9. if(inet_pton(AF_INET , argv[1] , &servaddr.sin_addr)<=0)  
  10.     err_quit("inet_pton error fof %s", argv[1]);  
  11. if(connect(sockfd , (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0)  
  12.     err_sys(“connect error”);  


4、listen函数

#include<sys/socket.h>

int listen(int sockfd, int backlog);

listen函数仅由TCP服务器调用,通常应该在调用socket和bind这两个函数之后,并在调用accept函数之前调用。它做两件事情:

(1)当socket函数创建一个套接字时,它被假设为一个主动套接字,也就是说,它是一个将调用connect发起连接的客户套接字。listen函数把一个未连接的套接字转换成一个被动套接字,指示内核应接受指向该套接字的连接请求。

(2)规定了内核应该为相应套接字排队的最大连接数。

另外,我们应该知道的一点是:backlog参数可以被认为未完成连接队列和已完成对连接队列总和的最大值。


5、accept函数

#include<sys/socket.h>

int accept(int sockfd , struct sockaddr * cliaddr , socklen_t *addrlen);

accept函数由TCP服务器调用,用于从已完成连接队列队头返回下一个已完成连接。如果已完成连接队列为空,那么进程被投入睡眠(假定套接字为默认的阻塞方式)。参数cliaddr和addrlen用来返回已连接的对端进程(客户)的协议地址。addrlen是值-结果参数:调用前,我们将由*addrlen所引用的整数值置为由cliaddr所指的套接字地址结构的长度,返回时,该整数值即为由内核存放在该套接字地址结构内的确切字节数。

如果accept成功,那么其返回值是由内核自动生成的一个全新描述符,代表与所返回客户的TCP连接。

在accept函数中,第一个参数sockfd为监听套接字描述符(由socket创建,随后用作bind和listend的一个参数的描述符);而accept函数返回值为已连接套接字描述符一个服务器通常仅仅创建一个监听套接字,它在该服务器的生命周期内一直存在。内核为每个服务器进程接受的客户连接创建一个已连接套接字(也就是说对于它的TCP三次握手过程已经完成)。当服务器完成对某个给定客户的服务时,相应的已连接套接字就被关闭。

6、read、write函数

      建立好了TCP连接之后,我们就可以把得到的套接字当做文件描述符来使用。由此,想到了网络程序里面的基本的读写函数read和write函数。

write函数:ssize_t write(int fd,const void *buf,size_t nbytes);
       write函数将buf中的nbytes字节内容写入到文件描述符中,成功返回写的字节数,失败返回-1,并设置errno变量。在网络程序中,当我们向套接字文件描述舒服写数据时有两种可能:
     (1)write的返回值大于0,表示写了部分数据或者是全部的数据,这样用一个while循环不断的写入数据,但是循环过程中的buf参数和nbytes参数是我们自己来更新的,也就是说,网络编程中写函数是不负责将全部数据写完之后再返回的,说不定中途就返回了!write成功返回,只是buf中的数据被复制到了kernel中的TCP发送缓冲区。至于数据什么时候被发往网络,什么时候被对方主机接收,什么时候被对方进程读取,系统调用层面不会给予任何保证和通知。write在什么情况下会阻塞?当kernel的该socket的发送缓冲区已满时。对于每个socket,拥有自己的send buffer和receive buffer。

     (2)返回值小于0,此时出错了,需要根据错误类型进行相应的处理。如果错误是EINTR表示在写的时候出现了中断错误,如果是EPIPE表示网络连接出现了问题。

read函数:ssize_t read(int fd,void *buf,size_t nbyte)
      read函数是负责从fd中读取内容,然后将这些读取的字符放入某一个预存的缓冲区buf,nbyte表示调用一次read操作,应该读多少数量的字符。当读取成功时,read返回实际读取到的字节数,如果返回值是0,表示已经读取到文件的结束了,小于0表示是读取错误。如果错误是EINTR表示在写的时候出现了中断错误,如果是EPIPE表示网络连接出现了问题。

      在《UNIX网络编程》一书中,将read、write分别改写成了readn、writen。


7、recv、send函数

    Recv函数和read函数提供了read和write函数一样的功能,不同的是他们提供了四个参数。

    Int recv(int fd,void *buf,int len,int flags)

    Int send(int fd,void *buf,int len,int flags)

    前面的三个参数和read、write函数是一样的。第四个参数可以是0或者是一下组合:

    MSG_DONTROUTE:不查找表

    是send函数使用的标志,这个标志告诉IP,目的主机在本地网络上,没有必要查找表,这个标志一般用在网络诊断和路由程序里面。

    MSG_OOB:接受或者发生带外数据

    表示可以接收和发送带外数据。

    MSG_PEEK:查看数据,并不从系统缓冲区移走数据

    是recv函数使用的标志,表示只是从系统缓冲区中读取内容,而不清楚系统缓冲区的内容。这样在下次读取的时候,依然是一样的内容,一般在有过个进程读写数据的时候使用这个标志。

    MSG_WAITALL:等待所有数据

    是recv函数的使用标志,表示等到所有的信息到达时才返回,使用这个标志的时候,recv返回一直阻塞,直到指定的条件满足时,或者是发生了错误。


8、close函数

#include<unistd.h>

int close(int sockfd);

关闭套接字,并终止TCP连接。


0 0
原创粉丝点击