首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 开发语言 > 编程 >

三.TCP套接口编程

2013-11-22 
3.TCP套接口编程对于编程者来说,我们所见到的网络通信实际上是2个socket之间的通信。所以双方第一步会调用s

3.TCP套接口编程

对于编程者来说,我们所见到的网络通信实际上是2个socket之间的通信。所以双方第一步会调用socket创建socket。此时系统内核会创建一个socket描述结构并返回该描述结构的描述字。对于服务器来说,系统自己创造的socket并不适合,因为我们要指定服务器socket的端口号,因此我们会调用bind函数,将我们自己定义的描述结构绑定到描述关键字上。对于客户端来说一般系统自己制定描述结构即可,因此调用bind函数没有什么太多的意义,但可以调用。但貌似指定指定通信的端口号,地址貌似没有意义。

?

1.socket函数。
int socket(int family, int type, int protocol);

?

返回socket描述字。

?

family指协议族:AF_INET IPV4协议,AF_INET6 IPV6协议, AF_LOCAL UNIX域协议, AF_ROUTE路由套接口协议, AF_KEY 密钥套接口协议。

?

type: SOCK_STREAM 字节流套接口, SOCK_DGRAM数据报套接口,SOCK_RAW 原始套接口。

?

protocol:一般设置为0,只有当使用原始套接口时候才有其它设置。

?

family和type组合

???????????????????????????????? AF_INET??????????? AF_INET6??????? AF_LOCAL????????? AF_ROUTE?????????? AF_KEY

SOCK_STREAM????????? TCP?????????????????? TCP???????????????? YES

SOCK_DGRAM??????????? UDP????????????????? UDP???????????????? YES

SOCK_RAW??????????????? IPV4???????????????? IPV6????????????????????????????????????????? YES????????????????????? YES

?

TCP和UDP很容易理解,IPV4和IPV6应该是IP数据包通信方面。YES表示有效,但没有合适缩略词描述,空白表示无效组合。(还有可能其它的如PF_XXX或者AF_UNIX,这里没有列出)。

?

?

2.connect函数:客户端使用,用于建立一个与TCP服务器的连接。

int connect(int sockfd, const struct sockaddr * servaddr, socklen_t addrlen);返回0成功,返回-1出错。

sockfd:前面提到的描述字。

servaddr:服务器的套接口地址结构指针。

addrlen:地址结构指针的大小。

这个函数很简单。

?

这里没有指定从哪个端口连接服务器端口,因为内核会自动选择源地址和一个临时端口。至于绑定端口,看下面的bind函数。

?

connect会触发三路握手,具体的返回错误信息,这里就不列出了。

?

3.bind函数:给套接口分配一个本地的协议地址。

int bind(int sockfd, struct sockaddr * myaddr, socklen_t addrlen); 0成功,-1出错。

在socket函数中就已经创建了套接口socket,但此时的套接口是有系统指定的IP和端口号。有时我们需要自己来制定地址和端口号,这就通过bind函数,将自己的socket绑定到套接口描述字上。上面的函数参数就很容易理解了,addrlen表示的是myaddr的长度。

?

myaddr有3个信息,协议族,地址和端口号。

对于服务器来说:绑定端口号很明显,因为只有知道端口号,我们才能去和服务器通信,当然不绑定也是可以的,这样你就得通过其它办法获得端口号比如说询问网络管理员。绑定IP地址就比较令人费解。服务器绑定IP地址的意义,比如说局域网内,该服务器地址为192.168.1.2,如果你把该地址绑定入socket,那么其它机器就可以通过192.168.1.2来访问。但对于本机来说,它还可以通过127.0.0.1来访问,但此时链接失败,因为你绑定的地址是192.168.1.2而不是127.0.0.1。当然如果你在路由器上做了端口映射,但外网仍然不可以访问你的这台机器服务器进程。

?

对于客户端来说:一般我们不需要去绑定一个自定义的socket,因为系统分配的已经满足我们传输要求。绑定IP地址貌似没什么意义,但绑定端口会有一定作用,因为平常是有系统自动分配的端口去和服务器通信,如果你绑定了端口号,则你就会通过指定的端口号和对方通信。

?

4.listen函数

int listen(int sockfd, int backlog) 返回0成功,-1出错

listen将未连接的套接口转换成监听套接口,第2个参数则是指此套接口最大的排队数目。但backlog定义比较模糊,具体的需要man listen查询,每个系统在实现上各不相同。但想用此参数来限制链接数目是不现实的。

?

5.accept函数

int accept(int sockfd, struct sockaddr * cliaddr, socket_t * addrlen); 返回非负描述字, -1就是出错。

从已完成队列头返回下一个已完成队列,若已完成队列空,则进程睡眠(缺省阻塞方式)。参数cliaddr会返回客户端信息,而addrlen则返回该结构的大小(已写数据的大小))。

?

如果你希望刚刚的程序显示连接者的信息,你所需要加入的代码则是

struct sockaddr_in * client_socket; //IPV4地址套接口描述结构

socket_len len; //长度

char temp[30]; //用来存点十表示法的IP地址,只要IPV4只要大于16即可

connfd = accept(listenfd, (struct sockaddr *)client_socket, &len); //修改的部分

inet_ntop(AF_INET, &client_socket->sin_addr, temp, sizeof(temp)); //将client_socket中记录的IP地址变成点十进制,结果存在temp中,同时返回char *也会指向temp

printf("connection from %s, port %d\n", temp, ntohs(client_socket->sin_port));

?

关于返回值,返回非负的值,则这个值代表了与客户的TCP链接。这里listenfd代表监听套接口描述字,而connfd代表了已连接套接口描述字。比如说服务器和2个客户端通信,服务器IP地址为192.168.1.3,服务器进程监听的端口号是80,2台客户机分别是192.168.1.4和192.168.1.5.内核分配给他们通信的端口号分别为45173和45765。这里IP地址代表了机器,80号端口则代表的是进程(可以这么理解,只要是提交到192.168.1.3:80的请求,就是请求服务器进程服务)。此时链接建立,connfd则代表了链接,这里有2个客户机连接上了服务器,则会有2个connfd。以后传递数据就通过这个链接描述字。

?

6.close函数

close函数并不会触发客户端或者服务器发送结束字节fin,它只是将链接的共享计数减1,当共享计数减为0时候,才会触发客户端或者服务器发送结束字节fin.及时你调用了close并最终导致计数减为0,socket也会将剩余没有发送完的数据发送完毕后才会发送fin字节。

热点排行