TCP/IP

来源:互联网 发布:带着淘宝混异世txt 编辑:程序博客网 时间:2024/06/14 20:38

#include "unp.h"
//TCP回射服务器程序:main函数
 //IPv4套节字地址结构
struct in_addr
{
 in_addr_t s_addr;//IPv4地址,一般为带符号的32位整数
};
struct sockaddr_in //IPv4套节字地址结构名
{
 uint8_t  sin_len;//无符号的8位整数,是长度字段,简化零长度可变套节字地址结构的处理
 sa_family_t sin_family;//套节字地址结构的地址族,是8位到无符号整数

 //IPv4地址和TCP或UDP端口号在套节字地址结构中总是以网络字节序来存储。
 in_port_t sin_port;//TCP或UDP端口,一般为uint16_t

 /*如果serv定义为某个网际套接字地址结构,那么serv.sin_addr将in_addr结构引用其中的32位
 IPv4地址,而serv.sin_addr.s_addr将按in_addr_t引用同一个32位IPv4地址*/
 struct in_addr sin_addr;

 char  sin_zero[8];//不被使用
};

int main(int argc, char **argv)
{
 int listenfd;//存放套节字描述符,相当于文件描述符,起标识作用
 int connfd;//存放accept返回值(由内核自动生成的一个全新描述符,代表已连接的套接字描述符)
 pid_t childpid;//子进程返回值
 socklen_t clilen;

 struct sockaddr_in cliaddr;//定义IPv4套接字地址结构变量,用于
 struct sockaddr_in servaddr;//定义IPv4套接字地址结构变量,用于初始化和捆绑。
//创建套接字,捆绑服务器的众所周知端口 

 //指定期望的通信协议类型,协议族,套节字类型,组合
 listenfd = Socket (AF_INET, SOCK_STREAM, 0);
 
 //置字节字符串servaddr的前sizeof(servaddr)个字节为零且包括’\0‘(把一个套接字地址结构初始化为0)。
 bzero (&servaddr, sizeof(servaddr));

 //以下三行代码是对地址结构的三个最主要的元素处理,其他元素用不到
 //为协议族指定为IPv4协议
 servaddr.sin_family = AF_INET;

 /*1.INADDR_ANY是通配地址,他告知内核去选择IP地址。
 2.就是指定地址为0.0.0.0的地址,这个地址事实上表示不确定地址,或“所有地址”、“任意地址”。
 3.一般来说,在各个系统中均定义成为0值。htonl和htons函数功能都是本机字节序转网络字节序,
 若本机为大端,与网络字节序同,直接返回;若本机为小端,转换成大端再返回网络字节序的值。
 4.无论是网络字节序还是主机字节序,INADDR_ANY的值(为0)都一样*/
 servaddr.sin_addr.s_addr = htonl (INADDR_ANY); //l代表32位的值,也可以接受64位的
 servaddr.sin_port        = htons (SERV_PORT);  //s代表16位的值

 /*给套接字listenfd捆绑地址servaddr和端口P,第二个参数是指向特定于协议的地址结构的指针,
 第三个参数是该地址结构的长度,*/
 Bind (listenfd, (SA *) *servaddr, sizeof(servaddr));

 /*1.listen函数把一个未连接的套接字转换成一个被动套接字,指示内核应该接受指向该套接字
 的连接请求。也就是导致套接字从CLOSED状态转换到LISTEN状态。
 2.第二个参数规定内核应该为相应套接字排队的最大连接数,在头文件里被定义为1024。*/
 Listen (listenfd, LISTENQ);

 //一直处于监听状态
 for ( ; ; )
 {
//服务器阻塞于accept调用,等待客户连接的完成
  clilen = sizeof(cliaddr);
  /*1.#defined SA struct sockaddr是通用套接字地址结构,后两参数用来返回已连接的对端进
  程(客户)的协议地址。clilen是值-结果参数:调用前,我们将*clilen所引用的整数值置为
  由cliaddr所指的套接字地址结构的长度,返回时,该整数值即为由内核存放在套接字地址结构
  内的确切字节数。
  2.如果accept成功,返回由内核生成的一个全新描述符,代表与所返回客户的TCP连接。
  3.他的第一个参数为监听套接字描述符(由socket创建,随后用作bind和listen的第一个参数的
  描述符),而他的返回值为已连接套接字描述符。一个服务器通常仅仅创建一个监听套接字,它
  在该服务器的生命周期内一直存在。而内核为每个由服务器进程接受的客户连接创建一个已连接
  套接字(也就是说对于它的TCP三路握手过程已经完成)。当服务器完成对某个给定的客户的服
  务时,相应的已连接套接字就被关闭。*/
  connfd = Accept (listenfd, (SA *) &cliaddr, &clilen);

  /*1.fork()是Unix派生新进程的唯一方法。
  2.调用一次返回两次。在调用进程(父进程)中返回一次,返回值是新派生进程(子进程)的进
  程ID号;在子进程返回值是0。因此,返回值本身告知当前进程是子进程还是父进程
  3.一个进程调用fork()函数后,系统先给新的进程分配资源,例如存储数据和代码的空间。然
  后把原来的进程的所有值都复制到新的新进程中,只有少数值与原来的进程的值不同。相当于克
  隆了一个自己。
  4.即在子进程中返回0,在父进程中返回新的子进程的ID号
  5.当一个连接建立时,accept返回,服务器接着调用fork,然后由子进程服务客户(通过已连接
  套接字connfd),父进程则等待另一个连接(通过监听套接字listenfd)。既然新的客户由子进
  程提供服务,父进程就关闭已连接套接字*/
/*fork为每个客户派生一个处理他们的子进程。子进程关闭监听套接字,父进程关闭已连接套接字。子进
程接着调用str_echo处理客户*/
  if ((childpid = Fork ()) == 0)//如果是子进程,则执行下去,要seec系列执行下去。
  {
   Close (listenfd);
   str_echo (connfd);//假设由该函数执行服务客户所需的所有操作。
   Close (connfd);//在子进程中显式地关闭已连接套接字,可以不用,因为下面有exit函数
   exit (0);
  }

  /*当服务器完成对某个给定客户的服务时,相应的已连接套接字就被关闭。下一个客户又来填
  充connfd;而让子进程自己去读写这个已连接套接字*/
  Close (connfd);
 }
}

0 0
原创粉丝点击