TCP/IP详细解析参考:TCP/IP解析
一、关于TCP下socket编程的思路解析:
通信的过程解析:
二、建立TCP(socket)服务端server解析:
(1)创建套接字(连接协议)对象:
头文件:
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
函数原型解析:
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 <sys/types.h> /* See NOTES */
#include <sys/socket.h>
函数原型及参数解析:
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 <arpa/inet.h>
//本地字节序转网络字节序
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 <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
// 把字符串形式的如 “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 <sys/types.h> /* See NOTES */
#include <sys/socket.h>
int listen(int sockfd, int backlog);
1. sockfd : socket系统调用返回服务端的 socket 描述符
2. backlog : 指可以连接该服务端的最大个数,设置网络访问的请求最大值
(4) 监听到有客户端接入,接受来自客户端的连接:
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
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 :
三、建立TCP(socket)服务端client 解析:
(1)创建套接字(连接协议)对象,上面有这里不解释
(2)连接 或 绑定 服务端 :
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
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 }
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/68482.html