网络编程(一).TCP(2)

2021-09-15 20:00:58 浏览数 (1)

tcpcopyclient.c

代码语言:javascript复制
#include <stdio.h> //printf,sprintf,perror 相关函数在此声明
#include <string.h> //memset 相关函数在此声明
#include <unistd.h> //read,close 相关函数在此声明
#include <arpa/inet.h> //socket,sockaddr_in,AF_INET,SOCK_STREAM,htons,inet_addr,connect,send,recv 相关函数和宏在此声明和定义
#include <fcntl.h> //open,O_RDONLY 相关函数和宏在此声明和定义

#define BUF_SIZE 1024
#define PORT 9000 

int main(int argc,char *argv[])
{
  struct sockaddr_in server_sai;
  int sfd=0,res=-1,recvbytes=0,sendbytes=0,readbytes=0,fa=0;
  char buf[BUF_SIZE],buf2[5]={0};
  char *filename=argv[2];  //进行变量的定义和初始化
  
  if(argc !=  3)
  {
    printf("error number of argc:%dn",argc);
    return res;
  }

  if (-1==(fa=open(argv[2],O_RDONLY,0644))) //将最后一个参数作为文件名,打开文件
  {
    printf("cannot open file:%sn",filename);
    return res;
  }

  if(-1 == (sfd=socket(AF_INET,SOCK_STREAM,0))) //创建一个IPV4的TCP socket
  {
    perror("socket");
    return res;
  }

  server_sai.sin_family=AF_INET; //IPV4 协议族
  server_sai.sin_port=htons(PORT);  //9000端口
  server_sai.sin_addr.s_addr=inet_addr(argv[1]);  //使用第一个参数作为IP地址
  memset(&(server_sai.sin_zero),0,sizeof(server_sai.sin_zero)); //将结构体剩余部分填零
  
  if  (-1 == connect(sfd,(struct sockaddr *)&server_sai,sizeof(struct sockaddr))) //使用sfd进行连接
  {
    perror("connect");
    return res;
  }

  memset(buf,0,sizeof(buf)); //将buf清零

  do
  {
    if(-1 == (readbytes=read(fa,buf,sizeof(buf)))) //从指定文件中读取数据写到buf中
    {
      printf("read error on:%sn",filename);
      return res;
    }
    if (-1 == (sendbytes=send(sfd,buf,readbytes,0))) //将buf中的数据写到远端
    {
      perror("send");
      return res;
    }
  }while(readbytes == sizeof(buf) ); //如果读取的数据不再是一整块,就意味着已经读完,随即跳出循环
  
  if (-1 == (recvbytes=recv(sfd,buf2,5,0))) //从远端读取数据到buf2中
  {
    perror("send");
    return res;
  }

  printf("%d -->%sn",recvbytes,buf2); //将接收到的字节数和数据内容打印出来
  
  close(sfd);
  close(fa); //进行清理工作,关闭描述符
  
  res=0;
  return res;
}

编译执行

代码语言:javascript复制
emacs@ubuntu:~/c$ alias gtc
alias gtc='gcc -Wall -g -o'
emacs@ubuntu:~/c$ gtc tcpcopyserver.x tcpcopyserver.c ; gtc tcpcopyclient.x tcpcopyclient.c 
emacs@ubuntu:~/c$ 

此时系统中并没有开放9000端口

代码语言:javascript复制
emacs@ubuntu:~/c$ netstat -ant | grep 9000
emacs@ubuntu:~/c$  

运行服务端

代码语言:javascript复制
emacs@ubuntu:~/c$ ./tcpcopyserver.x 
Listening...

此时系统中多了一个9000端口

代码语言:javascript复制
emacs@ubuntu:~/c$ netstat -ant | grep 9000
tcp        0      0 0.0.0.0:9000            0.0.0.0:*               LISTEN     
emacs@ubuntu:~/c$ 

服务端也并没有 /tmp/x.download 这个文件

代码语言:javascript复制
emacs@ubuntu:~/c$ ll /tmp/x.download 
ls: 无法访问/tmp/x.download: 没有那个文件或目录
emacs@ubuntu:~/c$ 

运行客户端,会立刻返回

代码语言:javascript复制
emacs@ubuntu:~/c$ du -sh 4.png 
8.6M	4.png
emacs@ubuntu:~/c$ 
emacs@ubuntu:~/c$ ./tcpcopyclient.x  127.0.0.1 4.png
2 -->OK
emacs@ubuntu:~/c$ 

服务端会打印信息并且返回,对比两个文件也没有差异

代码语言:javascript复制
emacs@ubuntu:~/c$ ./tcpcopyserver.x 
Listening...
emacs@ubuntu:~/c$ 
emacs@ubuntu:~/c$ diff /tmp/x.download 4.png 
emacs@ubuntu:~/c$

编译执行过程中没有报错,从结果来看,符合预期


sockaddr 结构体

bits/socket.h 中有关于 sockaddr 结构体的定义

代码语言:javascript复制
/* Structure describing a generic socket address.  */
struct sockaddr
  {
    __SOCKADDR_COMMON (sa_);    /* Common data: address family and length.  */
    char sa_data[14];           /* Address data.  */
  };

in_addr 结构体

netinet/in.h 中有关于 in_addr 结构体的定义

代码语言:javascript复制
/* Internet address.  */
typedef uint32_t in_addr_t;
struct in_addr
  {
    in_addr_t s_addr;
  };

sockaddr_in 结构体

netinet/in.h 中有关于 sockaddr_in 结构体的定义

代码语言:javascript复制
/* Structure describing an Internet socket address.  */
struct sockaddr_in
  {
    __SOCKADDR_COMMON (sin_);
    in_port_t sin_port;                 /* Port number.  */
    struct in_addr sin_addr;            /* Internet address.  */

    /* Pad to size of `struct sockaddr'.  */
    unsigned char sin_zero[sizeof (struct sockaddr) -
                           __SOCKADDR_COMMON_SIZE -
                           sizeof (in_port_t) -
                           sizeof (struct in_addr)];
  };

sin_zero[sizeof (struct sockaddr) - __SOCKADDR_COMMON_SIZE - sizeof (in_port_t) - sizeof (struct in_addr)] 中可以看出

这个字段,是为填补与 sockaddr 结构体的长度差

二者的占用的内存大小是一致的,因此可以互相转化,从这个意义上说,他们并无区别


socket

sys/socket.h 中有关于 socket 函数的声明

代码语言:javascript复制
/* Create a new socket of type TYPE in domain DOMAIN, using
   protocol PROTOCOL.  If PROTOCOL is zero, one is chosen automatically.
   Returns a file descriptor for the new socket, or -1 for errors.  */
extern int socket (int __domain, int __type, int __protocol) __THROW;

用于创建一个socket描述符(socket descriptor),它唯一标识一个socket。这个socket描述字跟文件描述字一样,后续的操作都有用到它,把它作为参数,通过它来进行一些读写操作

__domain 即协议域,又称为协议族(family)。常用的协议族有,AF_INET、AF_INET6、AF_LOCAL(或称AF_UNIX,Unix域socket)、AF_ROUTE等等。协议族决定了socket的地址类型,在通信中必须采用对应的地址,如AF_INET决定了要用ipv4地址(32位的)与端口号(16位的)的组合、AF_UNIX决定了要用一个绝对路径名作为地址

__type 指定socket类型。常用的socket类型有,SOCK_STREAM、SOCK_DGRAM、SOCK_RAW、SOCK_PACKET、SOCK_SEQPACKET等等

__protocol 指定协议。常用的协议有,IPPROTO_TCP、IPPTOTO_UDP、IPPROTO_SCTP、IPPROTO_TIPC等,它们分别对应TCP传输协议、UDP传输协议、STCP传输协议、TIPC传输协议

Note: 并不是上面的type和protocol可以随意组合的,如SOCK_STREAM不可以跟IPPROTO_UDP组合。当protocol为0时,会自动选择type类型对应的默认协议


AF_INET 和 SOCK_STREAM 宏定义

bits/socket.h 中有关于 AF_INET 和 SOCK_STREAM 的宏定义

代码语言:javascript复制
emacs@ubuntu:/usr/include$ grep AF_INET bits/socket.h
#define	AF_INET		PF_INET
#define	AF_INET6	PF_INET6
emacs@ubuntu:/usr/include$ grep PF_INET bits/socket.h
#define	PF_INET		2	/* IP protocol family.  */
#define	PF_INET6	10	/* IP version 6.  */
#define	AF_INET		PF_INET
#define	AF_INET6	PF_INET6
emacs@ubuntu:/usr/include$ grep SOCK_STREAM bits/socket.h
  SOCK_STREAM = 1,		/* Sequenced, reliable, connection-based
#define SOCK_STREAM SOCK_STREAM
emacs@ubuntu:/usr/include$

使用此方法可以获取其它想要的宏定义


htons

netinet/in.h 中有关于 htons 的定义

代码语言:javascript复制
/* Functions to convert between host and network byte order.

   Please note that these functions normally take `unsigned long int' or
   `unsigned short int' values as arguments and also return them.  But
   this was a short-sighted decision since on different systems the types
   may have different representations but the values are always the same.  */

extern uint32_t ntohl (uint32_t __netlong) __THROW __attribute__ ((__const__));
extern uint16_t ntohs (uint16_t __netshort)
     __THROW __attribute__ ((__const__));
extern uint32_t htonl (uint32_t __hostlong)
     __THROW __attribute__ ((__const__));
extern uint16_t htons (uint16_t __hostshort)
     __THROW __attribute__ ((__const__));

0 人点赞