Linux网络编程(TCP/IP/UDP)

  • TCP/IP 是互联网的基础, TCP代表传输控制协议,IP代表互联网协议。目前有两个版本IP,一个是32位地址的IPv4 和一个是128位的 IPv6 。
  • IP主机和IP地址

       每一个注意由一个32位的IP地址来标识。为了方便起见,通常用32位的IP低质号用记点法标识例如:134.121.64.1  也可以用主机名标识如 dns1.eec.wsu.edu 。实际上应用程序通常使用主机名而不是IP地址。因为给定其中一个,我们都可以通过dns(域名系统)服务器找到另外一个,两者之间可以相互转换。

        IP地址分为两部分 NeiworkID 和 HostID 字段。其中,IP 可以分为A~E类。例如B类IP分为一个16位NeiworkID,前两位是10 。发往UP地址的数据包首先被发送到具有相同networkID的路由器默认IP地址位127.0.0.1.

  • TCP 

       传输控制协议(TCP,Transmission Control Protocol)是一种面向连接的、可靠的、基于字节流的传输层通信协议,TCP旨在适应支持多网络应用的分层协议层次结构。 连接到不同但互连的计算机通信网络的主计算机中的成对进程之间依靠TCP提供可靠的通信服务。TCP假设它可以从较低级别的协议获得简单的,可能不可靠的数据报服务。 原则上,TCP应该能够在从硬线连接到分组交换或电路交换网络的各种通信系统之上操作。

  •  UDP

       Internet 协议集支持一个无连接的传输协议,该协议称为用户数据报协议(UDP,User Datagram Protocol)。UDP 为应用程序提供了一种无需建立连接就可以发送封装的 IP 数据包的方法。

       应用层的数据被传到传输层会添加TCP 或者UDP包头来标识使用的传输协议。合并后的数据被传到IP网络层,添加一个包含IP地址的IP报头来标识发送和接收主机。然后合并后的数据传递到网络链路层,再次将数据分成多个帧,添加发送和接收网络的地址,用于在物理网络之间的传输。

  •  套接字

       套接字是通信的基石,是支持TCP/IP协议的路通信的基本操作单元。可以将套接字看作不同主机间的进程进行双间通信的端点,它构成了单个主机内及整个网络间的编程界面。套接字存在于通信域中,通信域是为了处理一般的线程通过套接字通信而引进的一种抽象概念。套接字通常和同一个域中的套接字交换数据(数据交换也可能穿越域的界限,但这时一定要执行某种解释程序),各种进程使用这个相同的域互相之间用Internet协议簇来进行通信。

       

服务器套接字编程步骤如下:

1.创建socket;
2.绑定socket和端口号;
3.监听端口号;             (UDP省略)
4.接收来自客户端的连接请求;(UDP省略)
5.从socket中读取字符;
6.发送消息回客户机。

客户端套接字编程步骤如下: 

1.创建socket;

2.连接指定计算机的端口; (UDP省略)
3.向socket中写入信息;
4.从服务器接收消息。

在 netdb.h 和 sys/socket.h中有套接字的地址结构定义

struct sockaddr_in {
   sa_family_t sin_family;  //TCP/IP网络的sin_family 始终设置为AF_INET
   in_port_t sin_port;       //包含网络字节顺序排列的端口号
   struct in_addr sin_addr ;   //按网络字节顺序排列的IP地址
}
 
struct in_addr{        
   unit32_t s_addr;     //按网络字节顺序排列的IP地址
}

套接字Socket相关函数

socket函数:本身并没有IP和端口号的具体数值

int socket(int family,int type,int protpcol)
  1. domain 网络程序所在的主机采用的通信协议族
  2. type 网络程序所采用的通信协议
  3. protpcol 如果指定了type,可以直接使用0
  4. 返回值 成功 自然数>=0 错误 -1

bind函数:为套接字描述符合分配一个本地IP地址和一个端口号,将IP地址和端口号与套接字描述符绑定在一起。

int bind(int scokfd,struct sockaddr *myaddr,int addrlen);
  1. sockfd 由socket调用返回的文件描述符
  2. addrlen sockaddr结构的长度
  3. myaddr 一个指向sockaddr的指针,在中有sockaddr的定义
  4. 返回值 成功 0 出错 -1

listen函数:应用于TCP连接的服务程序,它的作用是设置socket套接字允许等待来自客户端的连接请求。

int listen(int sockfd,int backlog);
  1. sockfd 由socket调用返回的文件描述符
  2. backing 设置请求排队的最大长度,当有个多个客户端程序和服务端相连时,使用这个表示可以介绍的排队长度。
  3. 返回值 成功 0 失败 -1

accept函数:调用后,服务器程序会一直处于阻塞状态一直等待来自客户端的连接请求。

int accept(int sockfd,struct sockaddr *cliaddr,socklen_t *addrlen);
  1. sockfd 由socket调用返回的文件描述符
  2. addr/addrlen 用来给客户端程序填写的,服务器端只要传递指针就可以
  3. 返回值 收到请求:套接字描述符(>=0) 失败: -1

connect函数:该函数用于在客户端通过socket套接字建立网络连接

int connect(int sockfd,const struct sockaddr *serv_addr,socklen_t addrlen);
  1. sockfd socket返回的文件描述符
  2. serv_addr 储存了服务器端的连接信息其中sin_add是服务端的地址
  3. addrlen serv_addr的长度
  4. 返回值 成功 0 失败 -1

在Ubuntu下编译并运行

  • 服务端源代码
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    
    #define portnumber 3333
    
    int main(int argc, char* argv[]) {
            int local_listen_socket, server_session_socket;
            struct sockaddr_in server_addr_info_struct;
            struct sockaddr_in client_addr_info_struct;
            int size_of_sockaddr_in;
            int read_got_bytes_nr;
            char buffer[1024];
    
    
            /* socket: 服务器端开始建立sockfd描述符 */
            if ((local_listen_socket = socket(AF_INET, SOCK_STREAM, 0)) == -1) { // AF_INET i.e. IPV4; SOCK_STREAM i.e. TCP
                    fprintf(stderr, "Socket error:%s\n\a", strerror(errno));
                    exit(1);
            }
    
            /* 准备 sockaddr结构及其内部IP、端口信息 */
            bzero(&server_addr_info_struct, sizeof(struct sockaddr_in)); // 初始化,置0
            server_addr_info_struct.sin_family = AF_INET;                 // Internet
            server_addr_info_struct.sin_addr.s_addr = htonl(INADDR_ANY);  // 将本机host上的long数据转化为网络上的long数据,使服务器程序能运行在不同CPU的主机上 
                                                                                                            // INADDR_ANY 表示主机监听任意/所有IP地址。
            //server_addr_info_struct.sin_addr.s_addr=inet_addr("192.168.1.1");  //用于绑定到一个固定IP,inet_addr用于把数字加格式的ip转化为整形ip
            server_addr_info_struct.sin_port = htons(portnumber);         // (将本机器上的short数据转化为网络上的short数据)端口号
    
            /* bind: 绑定sockfd描述符 和 IP、端口 */
            if (bind(local_listen_socket, (struct sockaddr*)(&server_addr_info_struct), sizeof(struct sockaddr)) == -1) {
                    fprintf(stderr, "ERR bind():%s\n\a", strerror(errno));
                    exit(1);
            }
    
            /* 设置允许连接的最大客户端数 */
            if (listen(local_listen_socket, 5) == -1) {
                    fprintf(stderr, "ERR listen():%s\n\a", strerror(errno));
                    exit(1);
            }
    
            while (1) {
                    size_of_sockaddr_in = sizeof(struct sockaddr_in);
                    fprintf(stderr, "Listening & Accepting...\n");
                    if ((server_session_socket = accept(local_listen_socket, (struct sockaddr*)(&client_addr_info_struct), &size_of_sockaddr_in)) == -1) {  // 服务器阻塞, 直到接受到客户连接
                            fprintf(stderr, "ERR accept():%s\n\a", strerror(errno));
                            exit(1);
                    }
    
                    fprintf(stderr, "Got connection from %s\n", inet_ntoa(client_addr_info_struct.sin_addr)); // 网络地址 转换成 字符串
                    if ((read_got_bytes_nr = read(server_session_socket, buffer, 1024)) == -1) {
                            fprintf(stderr, "ERR read():%s\n", strerror(errno));
                            exit(1);
                    }
                    buffer[read_got_bytes_nr] = '\0';
                    printf("Server received %s\n", buffer); /* 这个对话服务已经结束 */
                    close(server_session_socket); /* 下一个 */
            }
    
            /* 结束通讯 */
            close(local_listen_socket);
            exit(0);
    }
    
    

  • 客户端源代码
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#define portnumber 3333

int main(int argc, char* argv[]) {
        int local_socket;
        char buffer[1024];
        struct sockaddr_in server_addr;
        struct hostent* host;

        if (argc != 2) {
                fprintf(stderr, "Usage:%s hostname \a\n", argv[0]);
                exit(1);
        }

        /* 使用hostname查询host 名字 */
        if ((host = gethostbyname(argv[1])) == NULL) {
                fprintf(stderr, "ERR gethostbyname\n");
                exit(1);
        }

        /* 客户程序开始建立 local_socket描述符 */
        if ((local_socket = socket(AF_INET, SOCK_STREAM, 0)) == -1) { // AF_INET:Internet;SOCK_STREAM:TCP
                fprintf(stderr, "ERR socket:%s\a\n", strerror(errno));
                exit(1);
        }

        /* 客户程序填充服务端的资料 */
        bzero(&server_addr, sizeof(server_addr)); // 初始化,置0
        server_addr.sin_family = AF_INET;          // IPV4
        server_addr.sin_port = htons(portnumber);  // (将本机器上的short数据转化为网络上的short数据)端口号
        server_addr.sin_addr = *((struct in_addr*)host->h_addr); // IP地址

        /* 客户程序发起连接请求 */
        if (connect(local_socket, (struct sockaddr*)(&server_addr), sizeof(struct sockaddr)) == -1) {
                fprintf(stderr, "ERR connect:%s\a\n", strerror(errno));
                exit(1);
        }

        /* 连接成功了 */
        printf("Please typein a string:\n");

        /* 读取和发送数据 */
        fgets(buffer, 1024, stdin);
        write(local_socket, buffer, strlen(buffer));

        /* 结束通讯 */
        close(local_socket);
        exit(0);
}

  • 编译
$ gcc server-while-tcp.c -o server-while- tcp.out
$ gcc cilent.c -o client.out
  • 运行
$ ./server-while-tcp.out     //在服务端输入
$ ./client.out 服务端IP地址  //在客户端输入

你可能感兴趣的:(linux,vim)