Linux系统编程——网络编程的学习

Linux系统编程学习相关博文

  1. Linux系统编程——文件编程的学习
  2. Linux系统编程——进程的学习
  3. Linux系统编程——进程间通信的学习
  4. Linux系统编程——线程的学习

Linux系统编程——网络编程的学习

  • 一、概述
    • 1. TCP/UDP
    • 2. 端口号
    • 3. 字节序
    • 4. Sockt服务器和客户端的开发步骤
      • 1. 服务器
      • 2. 客户端
  • 二、网络编程API
  • 三、API介绍
    • 1. socket函数
    • 2. bind函数
    • 3. listen函数
    • 4. accept函数
    • 5. connect函数
    • 6. inet_aton函数
    • 7. inet_ntoa函数
    • 8. htons函数
  • 四、API的使用例子

一、概述

1. TCP/UDP

  1. TCP面向连接( 如打电话要先拨号建立连接);UDP是无连接的,即发送数据之前不需要建立连接
  2. TCP提供可靠的服务。也就是说,通过TCP连接传送的数据,无差错,不丢失,不重复,且按序到达;UDP尽最大努力交付,即不保证可靠交付
  3. TCP面向字节流,实际上是TCP把数据看成一连串无结构的字节流;UDP是面向报文的,UDP没有拥塞控制,因此网络出现拥塞不会使源主机的发送速率降低 (对实时应用很有用,如IP电话,实时视频会议等)
  4. 每一条TCP连接只能是点到点的;UDP支持一对一,一对多,多对一和多对多的交互通信
  5. TCP首部开销20字节;UDP的首部开销小,只有8个字节
  6. TCP的逻辑通信信道是全双工的可靠信道,UDP则是不可靠信道

2. 端口号

一台拥有IP地址的主机可以提供许多服务,比如Web服务、FTP服务、SMTP服务等。这些服务完全可以通过1个IP地址来实现。那么,主机是怎样区分不同的网络服务呢?显然不能只靠IP地址,因为IP 地址与网络服务的关系是一对多的关系。实际上是通过“IP地址+端口号”来区分不同的服务的。端口提供了一种访问通道,服务器一般都是通过知名端口号来识别的。例如,对于每个TCP/IP实现来说,FTP服务器的TCP端口号都是21,每个Telnet服务器的TCP端口号都是23,每个TFTP (简单文件传送协议) 服务器的UDP端口号都是69。

3. 字节序

  1. 小端字节序 (Little endian):将低序字节存储在起始地址
  2. 大端字节序 (Big endian):将高序字节存储在起始地址
  3. 网络字节序 = 大端字节序

4. Sockt服务器和客户端的开发步骤

1. 服务器

  1. 创建套接字
  2. 为套接字添加信息 (IP地址和端口号)
  3. 监听网络连接
  4. 监听到有客户端接入,接受一个连接
  5. 数据交互
  6. 关闭套接字,断开连接

2. 客户端

  1. 创建套接字
  2. 连接服务器 (输入服务器IP地址和端口号)
  3. 数据交互
  4. 关闭套接字,断开连接
    Linux系统编程——网络编程的学习_第1张图片

二、网络编程API

在Linux系统中,操作系统提供了一系列的API,详细看下图

创建套接字	socket()
绑定		bind()
监听		listen()
连接(服务器)		accept()
连接(客户端)		connect()

地址转换		inet_aton() / inet_ntoa
字节序转换	htons() / htonl() / ntohs() / ntohl()

三、API介绍

1. socket函数

#include           /* See NOTES */
#include 

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

 1. 函数功能:创建套接字
 2. 形参说明:
domain:指明所使用的协议族,通常为AF_INET,表示互联网协议族(TCP/IP协议族)
	- AF_INET	IPV4因特网域
	- AF_INET6	IPv6因特网域 
	- AF_UNIX 	Unix域 
	- AF_ROUTE 	路由套接字
	- AF_KEY 	密钥套接字 
	- AF_UNSPEC 未指定
type:指定 socket 的类型
	- SOCK STREAM
	 	流式套接字提供可靠的、面向连接的通信流,它使用 TCP 协议,从而保证了数据传输的正确性和顺序性
	- SOCK DGRAM
	 	数据报套接字定义了一种无连接的服,数据通过相互独立的报文进行传输,是无序的,并且不保证是可靠、无差错的。它使用数据报协议 UDP
	- SOCK RAW
		允许程序使用底层协议,原始套接字允许对底层协议如 IP 或 ICMP 进行直接访问,功能强大但使用较为不便,主要用于一些协议的开发
protocal:通常赋值为0
	- 0 选择 type 类型对应的默认协议
	- IPPROTO_TCP TCP 传输协议
	- IPPROTO_UDP UDP 传输协议
	- IPPROTO_SCTP SCTP 传输协议
	- IPPROTO_TIPC TIPC 传输协议
3. 返回值:成功,返回套接字的文件描述符;失败,返回-1

2. bind函数

#include           /* See NOTES */
#include 

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

1. 函数功能:用于绑定IP地址和端口号到sockfd
2. 形参说明:
sockfd:调用socket函数返回的套接字描述符
addr:是一个指向包含有本机 IP 地址及端口号等信息的 sockaddr 类型的指针,指向要绑定给 sockfd 的协议地址结构,这个地址结构根据地址创建 socket 时的地址协议族的不同而不同(该结构体原型在下方)
addrlen:addr的大小

struct sockaddr结构体原型:
	struct sockaddr {
		sa_family_t sa_family;
		char        sa_data[14];
	};
可同等替换为:
	struct sockaddr_in {
		__kernel_sa_family_t  sin_family; /* 协议族 */
		__be16  sin_port;   /* 端口号 */
		struct in_addr  sin_addr;   /* IP地址结构体 */
		
		/* 填充到结构体sockaddr的大小,没有实际意义,只是为跟sockaddr结构体在内存中对齐,这样两者才能相互转换 */
		unsigned char  __pad[__SOCK_SIZE__ - sizeof(short int) -
		sizeof(unsigned short int) - sizeof(struct in_addr)];
	};
3. 返回值:成功,返回0;失败,返回-1

3. listen函数

#include           /* See NOTES */
#include 

int listen(int sockfd, int backlog);

1. 函数功能:监听设置,设置能处理的最大连接数
	- 设置能处理的最大连接数,listen() 并末开始接受连线,只是设置 sockect 的 listen 模式,listen 函数只用于服务端,服务路进程不知道要与谁连接,因此,它不会主动地要求与某个进程连接,只是一直监听是否有其他客户进程与之连接,然后响应该连接请求,并对它做出处理,一个服务进程可以同时处理多个客户进程的连接。主要就两个功能:将一个未连接的套接字转换为一个被动套接字(监听),规定内核为相应套接字排队的最大连接数。
	- 内核为任何一个给定监听套接字维护两个队列:
		- 未完成连接队列,每个这样的 SYN 报文段对应其中一项:已由某个客户发出并到达服务醒,而服务器正在等待完成相应的 TCP 二次握手过程。这些套接字处于 SYN REVD 状态;
		- 已完成连接队列,每个已完成 TCP 三次握手过程的客户端对应其中一项。这些套接字处于 ESTABLISHED 状态
2. 形参说明:
sockfd:调用socket函数返回的套接字描述符
backlog:请求队列允许的最大请求数
3. 返回值:成功,返回0;失败,返回-1

4. accept函数

#include           /* See NOTES */
#include 

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

1. 函数功能:用于从已完成连接队列队头返回下一个已完成的连接。如果已完成连接队列为空,那么进程被投入睡眠
2. 形参说明:
sockfd:调用socket函数返回的套接字描述符
addr:用来返回已连接的对端(客户端)的协议地址
addrlen:客户端地址长度
3. 返回值:该函数的返回值是一个新的套接字描述符,返回值是表示已连接的套接字描述符,而第一个参数是服务器监听套接字描述符。一个服务器通常仅仅创建一个监听套接字,它在该服务器的生命周期内一直存在,内核为每个由服务器进程接受的客户连接创建一个已连接套接字(表示 TCP 三次挥手已完成),当服务器完成对某个给定客户的服务时,相应的已连接套接字就会被关闭。成功,返回0;失败,返回-1

5. connect函数

#include           /* See NOTES */
#include 

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

1. 函数功能:绑定之后的client端(客户端),与服务器建立连接
2. 形参说明:
sockfd:目的服务器的套接字描述符
addr:服务器端的IP地址和端口号的结构体指针
addrlen:addr大小
3. 返回值:成功,返回0;失败,返回-1

6. inet_aton函数

#include 
#include 
#include 

int inet_aton(const char *cp, struct in_addr *inp);
       char *inet_ntoa(struct in_addr in);

1. 函数功能:将字符串形式的 IP 地址转为网络能识别的格式
2. 形参说明:
cp:IP地址
inp:存放IP地址的结构体指针(如下所示)

struct sockaddr结构体原型:
	struct sockaddr {
		sa_family_t sa_family;
		char        sa_data[14];
	};
可同等替换为:
	struct sockaddr_in {
		__kernel_sa_family_t  sin_family; /* 协议族 */
		__be16  sin_port;   /* 端口号 */
		struct in_addr  sin_addr;   /* IP地址结构体 */
		
		/* 填充到结构体sockaddr的大小,没有实际意义,只是为跟sockaddr结构体在内存中对齐,这样两者才能相互转换 */
		unsigned char  __pad[__SOCK_SIZE__ - sizeof(short int) -
		sizeof(unsigned short int) - sizeof(struct in_addr)];
	};

7. inet_ntoa函数

#include 
#include 
#include 

char *inet_ntoa(struct in_addr in);

1. 函数功能:将网络格式的IP地址转为字符串格式
2. 形参说明:
in:存放IP地址的结构体(结构体原型见上方)

8. htons函数

#include 

uint16_t htons(uint16_t hostshort);

1. 函数功能:返回网络字节序的值(关于什么是字节序请看概述部分),用来转换端口号
2. 形参说明:
hostshort:转换的端口号。h代表host,n代表net,s代表short(两个字节),l代表long(4个字节),通过上面的4个函数可以实现主机字节序和网络字节序之间的转换。有时可以用INADDR_ANY,INADDR_ANY指定地址让操作系统自己获取

四、API的使用例子

实现功能:服务器可以接收各个客户端的信息,每隔五秒服务器向连接的客户端发送信息(类似心跳包)
1. 服务端

  1 #include <stdio.h>
  2 #include <stdlib.h>
  3 #include <string.h>
  4 
  5 #include <sys/types.h>          /* See NOTES */
  6 #include <sys/socket.h>
  7 #include <arpa/inet.h>
  8 #include <netinet/in.h>
  9 
 10 //1. int socket(int domain, int type, int protocol);
 11 //2. int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
 12 //3. uint16_t htons(uint16_t hostshort);
 13 //4. int inet_aton(const char *cp, struct in_addr *inp);
 14 //5. int listen(int sockfd, int backlog);
 15 //6. int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
 16 
 17 int main(int argc, char **argv)
 18 {
 19     int mark = 0;
 20     int c_fd = 0;
 21     int s_fd = 0;
 22     int s_bind = 0;
 23     int c_lent = 0;
 24     int n_read = 0;
 25     int s_listen = 0;
 26 
 27     char msg[128] = {'\0'};
 28     char readBuf[128] = {'\0'};
 29     char sendBuf[128] = {'\0'};
 30     //char *sendBuf = "Thank you for your message";
 31 
 32     struct sockaddr_in s_addr;
 33     struct sockaddr_in c_addr;
 34 
 35     memset(&s_addr, 0, sizeof(struct sockaddr_in));
 36     memset(&c_addr, 0, sizeof(struct sockaddr_in));
 37 
 38     if(argc != 3){
 39         printf("Please input three params\n");
 40         exit(-1);
 41     }
 42 
 43     //1. socket
 44     s_fd = socket(AF_INET, SOCK_STREAM, 0);
 45     if(s_fd == -1){
 46         perror("Create socekt failed:");
 47         exit(-1);
 48     }
 49 
 50     //2. bind
 51     s_addr.sin_family = AF_INET;
 52     s_addr.sin_port = htons(atoi(argv[2]));
 53     inet_aton(argv[1], &s_addr.sin_addr);
 54 
 55     s_bind = bind(s_fd, (struct sockaddr *)&s_addr, sizeof(struct sockaddr_in));
 56     if(s_bind == -1){
 57         perror("Bind failed:");
 58         exit(-1);
 59     }
 60 
 61     //3. listen
 62     s_listen = listen(s_fd, 10);
 63     if(s_listen == -1){
 64         perror("Listen failed:");
 65         exit(-1);
 66     }
 67 
 68     printf("listen...\n");
 69 
 70     //4. accept
 71     while(1){
 72         c_lent = sizeof(struct sockaddr_in);
 73         c_fd = accept(s_fd, (struct sockaddr *)&c_addr, &c_lent);
 74         if(c_fd == -1){
 75             perror("Accept failed:");
 76             exit(-1);
 77         }
 78 
 79         printf("Get connect: %s\n", inet_ntoa(c_addr.sin_addr));
 80 
 81         mark++;
 82 
 83         if(fork() == 0){
 84             while(1){
 85                 memset(sendBuf, '\0', sizeof(sendBuf));
 86 
 87                 sprintf(sendBuf, "Welcome No.%d client", mark);
 88                 write(c_fd, sendBuf, sizeof(sendBuf));
 89                 sleep(5);
 90 
 91                 if(fork() == 0){
 92                     while(1){
 93                         memset(msg, '\0', sizeof(msg));
 94                         memset(readBuf, '\0', sizeof(readBuf));
 95 
 96                         n_read = read(c_fd, readBuf, sizeof(readBuf));
 97                         if(n_read == -1){
 98                             printf("Read failed:");
 99                         }else{
100                             sprintf(msg, "Get No.%d message: %s", mark, readBuf);
101                             printf("%s\n", msg);
102                         }
103                     }
104                 }
105             }
106         }
107     }
108 
109     return 0;
110 }

2. 客户端

  1 #include <stdio.h>
  2 #include <stdlib.h>
  3 #include <string.h>
  4 
  5 #include <sys/types.h>          /* See NOTES */
  6 #include <sys/socket.h>
  7 #include <arpa/inet.h>
  8 #include <netinet/in.h>
  9 
 10 //1. int socket(int domain, int type, int protocol);
 11 //2. int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
 12 //3. int inet_aton(const char *cp, struct in_addr *inp);
 13 //4. char *inet_ntoa(struct in_addr in);
 14 
 15 int main(int argc, char **argv)
 16 {
 17     int c_fd = 0;
 18     int n_read = 0;
 19 
 20     char readBuf[128] = {'\0'};
 21     char sendBuf[128] = {'\0'};
 22     //char *sendBuf = "Message from client";
 23 
 24     struct sockaddr_in c_addr;
 25 
 26     memset(&c_addr, 0, sizeof(struct sockaddr_in));
 27 
 28     if(argc != 3){
 29         printf("Please input three params\n");
 30         exit(-1);
 31     }
 32 
 33     //1. socket
 34     c_fd = socket(AF_INET, SOCK_STREAM, 0);
 35     if(c_fd == -1){
 36         perror("Create socekt failed:");
 37         exit(-1);
 38     }
 39 
 40     //2. connect
 41     c_addr.sin_family = AF_INET;
 42     c_addr.sin_port = htons(atoi(argv[2]));
 43     inet_aton(argv[1], &c_addr.sin_addr);
 44 
 45     if(connect(c_fd, (struct sockaddr *)&c_addr, sizeof(struct sockaddr_in)) == -1){
 46         perror("Connect failed:");
 47         exit(-1);
 48     }
 49 
 50     while(1){
 51         memset(sendBuf, '\0', sizeof(sendBuf));
 52 
 53         printf("input :");
 54         gets(sendBuf);
 55         write(c_fd, sendBuf, strlen(sendBuf));
 56 
 57         if(fork() == 0){
 58             while(1){
 59                 memset(readBuf, '\0', sizeof(readBuf));
 60 
 61                 n_read = read(c_fd, readBuf, sizeof(readBuf));
 62                 if(n_read == -1){
 63                     printf("Read failed:");
 64                 }else{
 65                     printf("Get server message: %s\n", readBuf);
 66                 }
 67             }
 68         }
 69     }
 70 
 71     return 0;
 72 }

你可能感兴趣的:(linux,网络,学习)