Linux系统学习——基于TCP的socket网络编程

TCP/IP详细解析参考:TCP/IP解析

一、关于TCP下socket编程的思路解析:

Linux系统学习——基于TCP的socket网络编程_第1张图片
Linux系统学习——基于TCP的socket网络编程_第2张图片

通信的过程解析:

Linux系统学习——基于TCP的socket网络编程_第3张图片

二、建立TCP(socket)服务端server解析:

(1)创建套接字(连接协议)对象:

头文件:

#include           /* See NOTES */
#include 

函数原型解析:

int socket(int domain, int type, int protocol);
int socket_fd = socket(协议族,类型,传输协议);

// 返回值 :成功返回一个文件描述符给 socket这个新的套接字,失败返回 -1
  • 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 进行直接访问,功能强大但是使用较为不方便,主要用于协议的开发
    
  • protocol 参数 ,通常赋值为 0

     0 为自动选择 type类型对应的默认协议
     
     也可以使用以下 宏 :
     1.IPPROTO_TCP		TCP 传输协议
     2.IPPROTO_UDP		UDP 传输协议
     3.IPPROTO_SCTP		SCTP 传输协议
     4.IPPROTO_TIPC		TIPC 传输协议
    

使用样例:

int socket_fd;
socket_fd = socket(AF_INET,SOCK_STREAM,0); //创建新的套接字对象 ,返回一个描述符
if(socket_fd == -1)
{
	perror("why : "); // 打印创建socket套接字失败原因
	exit(-1);  // 结束该进程
}

(2)bind设置对象,绑定IP地址和端口:

头文件:

#include           /* See NOTES */
#include 

函数原型及参数解析:

int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
  • 第一个参数sockfd : 创建新套接字 socket 返回的文件描述符(如socket_fd)
  • 第二个参数 addr 作用为: 绑定 IP地址 和 端口号 给 sockfd ,指向含有本机 IP 地址及端口号等信息的 sockaddr 类型的指针,指向要绑定给sockfd 的协议地址结构,这个地址结构根据创建 socket 时的地址协议族的不同而不同

ipv4对应的结构体:

以前使用的

struct sockaddr{
		unisgned short as_family;  //协议族
		char 	sa_data[4]; 	//ip + 端口号
};

现在可替换为:

struct sockaddr_in {
		 sa_family_t    	sin_family; /* 协议族 */
		 in_port_t      	sin_port;   /* 端口号 */ 
		 struct in_addr 	sin_addr;   /* IP地址结构体*/	
		 unisgned		char sin_zero[8];  // 填充没实际意义,只为了和sockaddr结构体在内存中对齐,这样两者可相互转换	
};

//IP地址结构体需要指向 in_addr中按网络字节顺序排列的地址
struct in_addr 
{
	uint32_t       s_addr;     /* address in network byte order */
};

注意:

1.在填充之后进行bind第二个参数的填写的时候需要把sockaddr_in 的结构体对象转换成struct sockaddr类型
2.从argv[]接收来的参数是字符串的形式需要转换成整型
	int atoi(const char *nptr); //字符串转int类型
	long atol(const char *nptr); //字符串转long类型
	long long atoll(const char *nptr);//字符串转longlong类型
3.我们接收来的数组在本地一般是本地字节序(小端序)需要转换成网络字节序才能进行网络的发送与接收
//头文件
#include 
 	   
//本地字节序转网络字节序
   	   uint32_t htonl(uint32_t hostlong); 

       uint16_t htons(uint16_t hostshort);
       
//网络字节序转本地字节序
       uint32_t ntohl(uint32_t netlong);
	   
       uint16_t ntohs(uint16_t netshort);

发送构造数据包 本地字节序---->网络字节序
接收解析数据包 网络字节序---->本地字节序

4.从argv[ ] 上接送过来的IP地址是字符串的形式,需要转换成无符号整型:

头文件:

#include 
#include 
#include 
// 把字符串形式的如 “192.168.1.123” 转换为网络能识别的格式
int inet_aton(const char *straddr, struct in_addr *addrp);
//	straddr 为存放在 argv[]的ip地址
//	addrp 为结构体in_addr的s_addr指向 sockaddr_in中ip地址结构体参数

//	把网络格式的IP地址转换成字符串形式
char *inet_ntoa(struct in_addr in);
//	把结构体in_addr 指向的IP地址结构体内容提取出来

(3)使用对象,监听网络连接:

#include           /* See NOTES */
#include 

int listen(int sockfd, int backlog);
1. sockfd : socket系统调用返回服务端的 socket 描述符
2. backlog : 指可以连接该服务端的最大个数,设置网络访问的请求最大值

(4) 监听到有客户端接入,接受来自客户端的连接:

#include           /* See NOTES */
#include 

int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
1. sockfd : socket系统调用返回服务端的 socket 描述符
2. addr : 创建个指向结构体 struct sockaddr_in 的对象,使用的时候需要强制转换成 struct sockaddr* 型
3. addrlen : 测量这个结构体大小 —— sizeof(struct sockaddr_in ),也就是客户端的长度
struct sockaddr_in c_addr;

int len = sizeof(struct sockaddr_in);
int c_fd = accept(socket_fd, (struct sockaddr *)&c_addr , &len );
//成功后,这些系统调用返回一个非负整数,该整数是接受的套接字的描述符。
//出错时,返回-1,并适当设置errno。

返回值解析:

该函数的返回值是一个新的套接字描述符,返回值表示已连接的套接字描述符,而第一个参数是服务器监听套接字描述符。一个服务器通常仅仅创建一个监听套接字,它在该服务器的生命周期内一直存在。内核为每个由服务器进程接受的客户连接创建一个已连接套接字(表示 TCP 三次握手已完成),当服务器完成对某个给定客户的服务时,相应的已连接套接字就会被关闭

(5) 数据的接受 和 发送 :

接受数据 :

		ssize_t read(int fd, void *buf, size_t count);  //跟读数据到文件操作一样
		
		ssize_t recv(int sockfd, void *buf, size_t len, int flags);

发送数据 :

	   ssize_t send(int sockfd, const void *buf, size_t len, int flags);
	   
	   ssize_t write(int fd, const void *buf, size_t count);	//跟写数据到文件一样操作

演示:

 45         // 4.accept
 46         int len = sizeof(struct sockaddr_in);
 47         while(1)
 48         {
 49                 c_fd = accept(socket_fd,(struct sockaddr *)&c_addr,&len);
 50                 if(c_fd == -1)
 51                 {
 52                         printf("accept error\n");
 53                 }
 54                 printf("get connect : %s\n",inet_ntoa(c_addr.sin_addr));	//将当前连接客户端的IP地址打印出来
 55 
 56                 if(fork() == 0)
 57                 {
 58                         // 5.read
 59                         n_read = read(c_fd,readbuf,128);
 60                         if(n_read == -1)
 61                         {
 62                                 perror("why :");
 63                         }else{
 64                                 printf("get message : %d\n%s\n",n_read,readbuf);
 65                         }
 66                 }
 67                 // 6.write
 68                 write(c_fd,msg,strlen(msg));
 69         }

send和recv :

Linux系统学习——基于TCP的socket网络编程_第4张图片

三、建立TCP(socket)服务端client 解析:

(1)创建套接字(连接协议)对象,上面有这里不解释

(2)连接 或 绑定 服务端 :

#include           /* See NOTES */
#include 

int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
//	如果连接或绑定成功,则返回零。出错时,返回-1,并适当设置errno。

演示:

	struct sockaddr_in c_addr;
	int c_fd = socket(AF_INET,SOCK_STREAM,0);
	
	if( connect(c_fd, (struct sockaddr *)&c_addr , sizeof(struct sockaddr)) == -1)
	{
		printf("connect\n");
		exit(-1);
	}

(3)接受 和 发送数据 和上面一样

四、样例展示,可以多个客户端连接同一个服务端效果:

服务端 server5.c :

  1 #include<stdio.h>
  2 #include <sys/types.h>          /* See NOTES */
  3 #include <sys/socket.h>
  4 #include<stdlib.h>
  5 #include <netinet/in.h>
  6 #include <unistd.h>
  7 #include<string.h>
  8 #include <arpa/inet.h>
  9 
 10 int main(int argc, char **argv)
 11 {
 12         int socket_fd;
 13         int c_fd;
 14         int n_read;
 15         char readbuf[128];
 16 
 17         int mark =0;
 18 
 19         //      char *msg = "I get message";
 20         char msg[128] = {0};
 21 
 22 
 23         struct sockaddr_in s_addr;
 24         struct sockaddr_in c_addr;
 25 
 26         if(argc != 3)
 27         {
 28                 printf("param is laji\n");
 29                 exit(-1);
 30         }
 31 
 32         memset(&s_addr,0,sizeof(struct sockaddr_in)); //清空内存
 33         memset(&c_addr,0,sizeof(struct sockaddr_in));
 34 
 35         // 1.socket
 36         socket_fd = socket(AF_INET,SOCK_STREAM,0);	//创建新的套接字描述符,建立socket对象
 37         if(socket_fd == -1)
 38         {
 39                 perror("why :");
 40                 exit(-1);
 41         }
 42 
 43         printf("connect\n");
 44 
 45         s_addr.sin_family =  AF_INET;	
 46         s_addr.sin_port = htons( atoi(argv[2]) );
 47         inet_aton(argv[1],&s_addr.sin_addr);
 48 
 49         // 2.bind
 50         bind(socket_fd,(struct sockaddr *)&s_addr,sizeof(struct sockaddr_in));	//绑定IP地址和端口
 51 
 52         // 3.listen
 53 
 54         listen(socket_fd,10);	// 监听
 55 
 56         // 4.accept
 57         int len = sizeof(struct sockaddr_in);
 58         while(1)
 59         {
 60                 c_fd = accept(socket_fd,(struct sockaddr *)&c_addr,&len);
 61 
 62                 if(c_fd == -1)
 63                 {
 64                         perror("accept error\n");
 65                 }
 66 
 67                 mark++;
 68                 printf("get connect : %s\n",inet_ntoa(c_addr.sin_addr));
 69 
 70 
 71                 if(fork() == 0)
 72                 {
 73                         if( fork()==0 )
 74                         {
 75                                 while(1)
 76                                 {
 77                                 //      memset(msg,0,sizeof(msg));
 78                                 //      printf("Please input :\n");
 79                                 //      gets(msg);
 80 
 81                                         sprintf(msg,"welcome NO. %d client",mark);
 82                                         write(c_fd,msg,strlen(msg));
 83                                         sleep(3);
 84                                 }
 85                         }
 86 
 87                         while(1)
 88                         {
 89                                 memset(readbuf,0,sizeof(readbuf));	//防止数据残留
 90                                 // 5.read
 91                                 n_read = read(c_fd,readbuf,128);
 92                                 if(n_read == -1)
 93                                 {
 94                                         perror("why :");
 95                                 }else{
 96                                         printf("get message : %d\n%s\n",n_read,readbuf);
 97                                 }
 98                         }
 99                         break;
100                 }
101         }
102         return 0;
103 }

客户端 client4.c

  1 #include<stdio.h>
  2 #include <sys/types.h>          /* See NOTES */
  3 #include <sys/socket.h>
  4 #include<stdlib.h>
  5 #include <netinet/in.h>
  6 #include <unistd.h>
  7 #include<string.h>
  8 #include <arpa/inet.h>
  9 
 10 int main(int argc, char **argv)
 11 {
 12         int c_fd;
 13         int n_read;
 14         char readbuf[128];
 15 
 16         //      char *msg = "message from client";
 17         char msg[128] = {0};
 18 
 19         struct sockaddr_in c_addr;
 20 
 21         memset(&c_addr,0,sizeof(struct sockaddr_in));
 22 
 23         if(argc != 3)
 24         {
 25                 printf("param is laji\n");
 26                 exit(-1);
 27         }
 28 
 29         // 1.socket
 30         c_fd = socket(AF_INET,SOCK_STREAM,0);
 31 
 32         if(c_fd == -1)
 33         {
 34                 printf("socket\n");
 35                 exit(-1);
 36         }
 37 
 38         c_addr.sin_family =  AF_INET;
 39         c_addr.sin_port = htons( atoi(argv[2]) );
 40         inet_aton( argv[1],&c_addr.sin_addr);
 41 
 42         //2.connect
 43         if( connect( c_fd,(struct sockaddr *)&c_addr,sizeof(struct sockaddr) ) == -1 )
 44         {
 45                 printf("connect\n");
 46                 exit(-1);
 47         }
 48 
 49         while(1)
 50         {
 51                 if(fork() ==0)
 52                 {
 53                         while(1)
 54                         {
 55                                 memset(msg,0,sizeof(msg));
 56                                 printf("Please Input :\n");
 57                                 gets(msg);
 58 
 59                                 //3.write
 60                                 write(c_fd,msg,strlen(msg));
 61                         }
 62                 }
 63 
 64                 while(1)
 65                 {
 66                         memset(readbuf,0,sizeof(readbuf));
 67                         // 4.read
 68                         n_read = read(c_fd,readbuf,128);
 69                         if(n_read == -1)
 70                         {
 71                                 perror("read why :");
 72                         }
 73                         else
 74                         {
 75                                 printf("get message : %d\n%s\n",n_read,readbuf);
 76                         }
 77                 }
 78         }
 79         return 0;
 80 }

你可能感兴趣的:(Linux系统学习)