网络字节序
定义
什么是字节序:CPU对内存数据的存取顺序
大端字节序&小端字节序
大端 :低位保存在高地址 小端:低位保存在低地址
78 12
56 34
34 56
12 78
网络字节序:采用大端字节序传输,防止出现大小端机器解读数据出错的情况
主机字节序:主机字节序指的是机器具体的字节序
主机是大端:认为主机字节序是大端
主机是小端:认为主机字节序是小端
网络设计需要继续转发之前都要将主机字节序转换成网络字节序
网络数据接收之前都需要将网络字节序转换成主机字节序
为什么网络数据需要转换为网络字节序?
1、网络规定采用大端字节序作为网络字节序
2、路由设备或者交换机需要对网络数据进行分用到网络层,以获得“目的ip地址”,而这些设备在继续分用的时候默认是按照网络字节序进行分用的
Udp协议
无连接:当udp的客户端想要给udp服务端发送数据的时候,只需要知道服务端的ip和端口,就可以直接发送
引申含义:在发送数据之前是不知道服务端的状态信息的
不可靠:不报证udp的数据一定到达对端机器
面向数据报:udp数据是整条数据接收和发送的
Tcp协议
有连接:连接双方在发送数据之前,需要进行连接(指需要提前告知,沟通连接的信息)
可靠传输:保证数据是有序且可靠到达对端
面向字节流:上一次和下一次的数据是没有明显的数据边界的
Udp的socket编程
编程流程
cs模型(一般使用):client(客户端)-server(服务端)
bs模型:浏览器-服务器
客户端 服务端
1、创建套接字 1、创建套接字
2、绑定地址信息(不推荐自己绑定) 2、绑定地址信息:ip地址、端口信息
3、先由客户端向服务端发送数据
3、再由服务端向客户端回复数据
4、关闭套接字 4、关闭套接字
接口
创建套接字
int socket(int domain, int type, int protocol);
domain:指定当前的地址域,相当于指定网络层到底使用什么协议
AF_INET:ipv4网络(默认选择)
AF_INET6:ipv6网络
AF_UNIX:本地域套接字
type:创建套接字的类型
SOCK_DGRAM:用户数据报套接字 =》udp协议
SOCK_STREAM:流式套接字 =》tcp协议
protocol:
0:表示使用套接字类型默认的协议
IPPROTO_UDP:17
IPPROTO_TCP:6
返回值:
返回桃姐字描述符,套接字描述符本质上就是一个文件描述符
成功:大于等于0
失败:小于0
创建套接字的原因在于想在内核当中创建一块与协议相对应的缓冲区
需要包含一个头文件
#include <sys/socket.h>
如果protocol一栏中不使用默认的0则需要包含一个头文件
#include <netinet/in.h>
1 #include <stdio.h>
2 #include <unistd.h>
3 #include <sys/socket.h>
4 #include <netinet/in.h>
5
6 int main()
7 {
8 int sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
9 if(sockfd < 0)
10 {
11 perror("socket fail\n");
12 return 0;
13 }
14
15 printf("sockfd : %d\n", sockfd);
16 while(1)
17 {
18 sleep(1);
19 }
20 return 0;
21 }
会发现在闪烁,此时闪烁的原因是它指向了内核中的缓冲区
绑定地址信息
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
sockfd:socket函数返回的套接字描述符
将创建出来的套接字和网卡、端口进行绑定
addr:地址信息结构
struct sockaddr---》通用地址信息结构
struct sockaddr {
sa_family_t sa_family; //地址域信息,占用两个字节
//地址域信息决定了当前使用寿命网络层协议
char sa_data[14]; //本质上任何网络程序都不会直接填充这个14个字节的字符组,
//定义第二个参数总共占用14个字节
}
———————————————
| 2字节地址域信息 |
| 一一 |
| ↑ |
| 14字节地址数据 |
| ↓ |
| 一一 |
———————————————
ipv4:
vim /usr/include/netinet/in.h
struct sockaddr_in
{
__SOCKADDR_COMMON (sin_); //地址域信息:2字节
in_port_t sin_port; /* Port number. */ //端口信息:2字节
struct in_addr sin_addr; /* Internet address. */ //ipv4版本的ip地址:4字节
/* Pad to size of `struct sockaddr'. */ //填充剩下的部分为0:8字节
unsigned char sin_zero[sizeof (struct sockaddr) -
__SOCKADDR_COMMON_SIZE -
sizeof (in_port_t) -
sizeof (struct in_addr)];
};
———————————————
| 2字节地址域信息 |
| 2字节端口信息 |
| 4字节 |
| ip地址 |
| 一一 |
| ↑ |
| 8字节填充为0 |
| ↓ |
| 一一 |
———————————————
地址域信息:网络协议栈通过该信息,来判断传递进来的是哪一个数据结构,判断出来之后,就知道后面的字节该如何解析
本地域套接字:进行本地进程间通信的一种手段
struct sockaddr_un
———————————————
| 2字节地址域信息 |
| 一一 |
| ↑ |
| 108字节路径名 |
| ↓ |
| 一一 |
———————————————
addrlen:地址信息结构的长度,防止越界
发送
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,const struct sockaddr *dest_addr, socklen_t addrlen);
spckfd:套接字描述符
buf:想要发送的数据
len:要发送数据的长度
flags:
0:阻塞发送
dest_addr:目标主机的地址信息结构(ip,port(端口))
addrlen:目标主机地址信息结构的长度
返回值:
成功:返回具体发送的字节数量
失败:返回-1
客户端为什么不推荐绑定地址信息?
本质上是不想让那个客户端程序将端口写死
即不想让客户端在启动的时候都是绑定一个端口(一个端口只能被一个协议当中的一个进程所绑定)
客户端1绑定了端口,本机在启动客户端2的时候会绑定失败,因为端口已经被客户端1拿走了
客户端没有主动的绑定端口,udp客户端在调用sendto的时候,会自动绑定(操作系统分配)一个空闲的端口
接收
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,struct sockaddr *src_addr, socklen_t *addrlen);
sockfd:套接字描述符
buf:将数据接收到buf当中
len:最大可以接收多大的数据
flags:
0:阻塞接收
src_addr:接收的数据来源的主机地址信息结构
addrlen:输入输出型参数
输入:在接收之前准备的对端地址信息结构的长度
输出:实际接收回来的地址信息长度
返回值:
接收到:接收的字节数量
未接收:-1
eg:
char* buf[1024] = {0}; <--数据接收的位置
struct sockaddr_in src_addr; <--定义一个具体结构体例如ipv4
recvfrom(sockfd, buf, sizeof(buf)-1, 0, (struct sockaddr*)&src_addr, )
↑预留\0 ↑传递结构体的地址,需要强转为(struct sockaddr*)
接收完后src_addr中有ip和port,即发送数据的主机的ip地址和端口
关闭
close(int sockfd);
主机字节序转换成为网络字节序
接口1:h(ost)ton(et)l(long) --> htonl 4字节
uint32_t htonl(uint32_t hostlong);
如果主机是大端机调用接口则什么都不干,如果主机是小端机则会将数据转换成小端再返回
接口2:htons(hort) --> htons 2字节
uint16_t htons(uint16_t hostshort);
网络字节序转换成为主机字节序
接口1:ntohl 4字节
uint32_t ntohl(uint32_t netlong);
接口2:ntohs 2字节
uint16_t ntohs(uint16_t netshort);
将ip地址从点分十进制转化为无符号32位整数并转换为网络字节序
in_addr_t inet_addr(const char *cp);
将网络字节序转换为主机字节序并转换为点分十进制的ip地址
char *inet_ntoa(struct in_addr in);
代码实现
绑定地址
首先测试一下绑定地址信息
1 #include <stdio.h>
2 #include <unistd.h>
3 #include <sys/socket.h>
4 #include <netinet/in.h>
5 #include <arpa/inet.h>
6
7 int main()
8 {
9
10 int sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
11 if(sockfd < 0)
12 {
13 perror("socket failed\n");
14 return 0;
15 }
16
17 struct sockaddr_in addr;
18 addr.sin_family = AF_INET;
19 addr.sin_port = htons(12345);
20 addr.sin_addr.s_addr = inet_addr("0.0.0.0");
21
22
23
24 int ret = bind(sockfd, (struct sockaddr*)&addr, sizeof(addr));
25 if(ret < 0)
26 {
27 perror("bind failed\n");
28 return 0;
29 }
30
31 while(1)
32 {
33 sleep(1);
34 }
35 return 0;
36 }
此时没有任何输出,说明while(1)之前的步骤都成功了
可以看到确实是创建成功了,可以使用 netstat -anp | grep [端口] 来查看端口使用情况
udp 表示当前12345端口使用的是udp协议
0.0.0.0:12345 代表当前监听的ip地址与端口
0.0.0.0:* 表示接收从任一ip和任一端口来的数据
如果此时再启动一次程序,会报错,因为端口已经被占用了
udp程序
服务端
1 #include <stdio.h>
2 #include <string.h>
3 #include <unistd.h>
4 #include <sys/socket.h>
5 #include <netinet/in.h>
6 #include <arpa/inet.h>
7
8 int main()
9 {
10
11 int sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
12 if(sockfd < 0)
13 {
14 perror("socket failed\n");
15 return 0;
16 }
17
18 struct sockaddr_in addr;
19 addr.sin_family = AF_INET;
20 addr.sin_port = htons(12345);
21 addr.sin_addr.s_addr = inet_addr("10.0.8.14");
22
23
24
25 int ret = bind(sockfd, (struct sockaddr*)&addr, sizeof(addr));
26 if(ret < 0)
27 {
28 perror("bind failed\n");
29 return 0;
30 }
31
32 while(1)
33 {
34
35
36 char buf[1024] = {0};
37 struct sockaddr_in peer_addr;
38 socklen_t peer_addrlen = sizeof(peer_addr);
39 ssize_t recv_size = recvfrom(sockfd, buf, sizeof(buf) - 1, 0, (struct sockaddr*)&peer_addr, &peer_addrlen);
40 if(recv_size < 0)
41 {
42 continue;
43 }
44
45 printf("i am server, i recv %s, from %s:%d\n", buf, inet_ntoa(peer_addr.sin_addr), ntohs(peer_addr.sin_port));
46
47
48
49 memset(buf, '\0', sizeof(buf));
50 sprintf(buf, "hello, client %s:%d, i am server\n", inet_ntoa(peer_addr.sin_addr), ntohs(peer_addr.sin_port));
51
52
53 sendto(sockfd, buf, strlen(buf), 0, (struct sockaddr*)&peer_addr, sizeof(peer_addr));
54 }
55 return 0;
56 }
客户端
1 #include <stdio.h>
2 #include <string.h>
3 #include <unistd.h>
4 #include <sys/socket.h>
5 #include <netinet/in.h>
6 #include <arpa/inet.h>
7
8 int main()
9 {
10
11 int sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
12 if(sockfd < 0)
13 {
14 perror("socket failed\n");
15 return 0;
16 }
17
18
19
20 while(1)
21 {
22
23 char buf[1024] = "i am client1";
24
25 struct sockaddr_in dest_addr;
26 dest_addr.sin_family = AF_INET;
27 dest_addr.sin_port = htons(12345);
28 dest_addr.sin_addr.s_addr = inet_addr("10.0.8.14");
29 int se = sendto(sockfd, buf, strlen(buf), 0, (struct sockaddr*)&dest_addr, sizeof(dest_addr));
30 if(se < 0)
31 {
32 continue;
33 }
34
35
36 memset(buf, '\0', sizeof(buf));
37 ssize_t recv_size = recvfrom(sockfd, buf, sizeof(buf) - 1, 0, NULL, NULL);
38 if(recv_size < 0)
39 {
40 perror("recvfrom failed\n");
41 continue;
42 }
43
44 printf("server say: %s\n", buf);
45 }
46 return 0;
47 }
运行udp程序
运行一下两个程序
Tcp的socket编程
Tcp的编程流程(三次握手四次挥手)
客户端 服务端
1、创建套接字 1、创建套接字
流式套接字 创建流式套接字(SOCK_STREAM)
2、不推荐绑定地址信息 2、绑定地址信息
ip/port
3、发起连接 3、监听
①告诉操作系统内核,可以接收客户端发起的连接请求
②监听新连接的到来
三次握手建立连接
4、接收新连接
。。。
互相发送数据
。。。
四次挥手断开连接
完成三次握手后即建立连接完成
三次握手的过程是在内核(网络协议栈)中完成的,建立连接的过程不需要程序员参与
第一次:客户端给服务端发送连接请求
第二次:服务端回复客户端的连接请求并发送连接请求
第三次:客户端回复服务端的连接请求,连接建立
断开连接需要四次挥手
Tcp socket编程的接口
创建套接字
int socket(int domain, int type, int protocol);
绑定地址信息
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
监听
int listen(int sockfd, int backlog);
sockfd:套接字描述符
backlog:已完成连接队列的大小
三次握手中:
客户端向服务端发送一个名为SYN的数据包,表示发起连接
服务端收到请求后向客户端发送SYN+ACK两个数据包,其中SYN代表服务端回复也想建立连接,ACK是回复上一个SYN的
客户端向服务端回复一个ACK数据包,用于回复服务端发送的SYN数据包
此时内核中有两个队列,一个叫未完成连接队列,一个叫已完成连接队列
已完成连接队列:
1、已经完成三次握手等待被accept的连接
2、连接状态为“连接建立”的连接
为完成连接队列:
没有完成三次握手的连接
backlog影响了服务端并发接收连接的能力
假设backlog是1,此时未完成队列中的数量为2,即此时只能有一个能进入已完成连接队列,在进入已完成连接队列后该新
建立的连接被拿走,又有一个连接可以进入已完成连接队列,即吞吐量为1
发起连接
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
sockfd:套接字描述符
addr:服务端的地址信息结构(服务端的ip和服务端的端口)
addrlen:服务端地址信息结构长度
返回值:
成功:0
失败:-1
建立新连接
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
作用:从已完成连接队列当中获取已经完成三次握手的连接
sockfd:套接字描述符
addr:客户端的地址信息结构(客户端的ip和客户端的端口)
addrlen:客户端地址信息结构的长度
返回值:
成功:新连接的套接字描述符,也是个文件描述符
失败:-1
①TCP服务端在通信之前创建套接字A,套接字当中有发送缓冲区和接收缓冲区,在绑定地址信息和监听后便可以接收新连接
②此时应该客户端发起连接,发起连接后与TCP服务端创建的套接字A产生了关联,即三次握手的数据是在套接字A中处理的
③调用accept后会在服务端中再创建一个套接字B,也就是新连接的套接字,随后客户端与套接字B进行数据的收发
④套接字A为监听套接字即“listen sock”,套接字B为“new sock”
⑤若此时再有一个客户端想建立连接,发起连接后同样是与套接字A进行三次握手建立连接,在建立连接后客户端会再创建一个
新套接字C与新的客户端进行数据收发
如果没有连接的时候,调用accept也会阻塞
发送数据
ssize_t send(int sockfd, const void *buf, size_t len, int flags);
sockfd:套接字描述符
accept的返回值即新连接的套接字
buf:待要发送的数据
len:发送数据的长度
flags:
0:阻塞发送
MSG_OBB:发送带外数据
返回值:
大于0:返回发送的字节数量
-1:发送失败
接收数据
ssize_t recv(int sockfd, void *buf, size_t len, int flags);
sockfd:套接字描述符
accept的返回值即新连接的套接字
buf:将接收的数据放到哪里去
len:buf的最大接收能力
flags:0 阻塞接收
返回值:
大于0:正常接收了多少字节数据
等于0:对端将连接关闭了
小于0:接收失败
如果连接没有数据,则调用recv会阻塞
关闭套接字
int close(int fd);
Tcp代码实现
判断端口是否在监听
1 #include <stdio.h>
2 #include <unistd.h>
3 #include <sys/socket.h>
4 #include <netinet/in.h>
5 #include <arpa/inet.h>
6
7 int main()
8 {
9
10 int listen_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
11 if(listen_sock < 0)
12 {
13 perror("listen_sock failed\n");
14 return 0;
15 }
16
17
18 struct sockaddr_in addr;
19 addr.sin_family = AF_INET;
20 addr.sin_port = htons(12345);
21 addr.sin_addr.s_addr = inet_addr("10.0.8.14");
22 int ret = bind(listen_sock, (struct sockaddr*)&addr, sizeof(addr));
23 if(ret < 0)
24 {
25 perror("bind failed\n");
26 return 0;
27 }
28
29
30 ret = listen(listen_sock, 1);
31 if(ret < 0)
32 {
33 perror("listen failed\n");
34 return 0;
35 }
36
37 while(1)
38 {
39 sleep(1);
40 }
41 return 0;
42 }
可以看到端口12345现在正在处于LISTEN状态
单线程实现Tcp代码
服务端
1 #include <stdio.h>
2 #include <string.h>
3 #include <unistd.h>
4 #include <sys/socket.h>
5 #include <netinet/in.h>
6 #include <arpa/inet.h>
7
8 int main()
9 {
10
11 int listen_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
12 if(listen_sock < 0)
13 {
14 perror("listen_sock failed\n");
15 return 0;
16 }
17
18
19 struct sockaddr_in addr;
20 addr.sin_family = AF_INET;
21 addr.sin_port = htons(12345);
22 addr.sin_addr.s_addr = inet_addr("10.0.8.14");
23 int ret = bind(listen_sock, (struct sockaddr*)&addr, sizeof(addr));
24 if(ret < 0)
25 {
26 perror("bind failed\n");
27 return 0;
28 }
29
30
31 ret = listen(listen_sock, 1);
32 if(ret < 0)
33 {
34 perror("listen failed\n");
35 return 0;
36 }
37
38 while(1)
39 {
40
41 struct sockaddr_in peer_addr;
42 socklen_t peer_addrlen = sizeof(peer_addr);
43 int new_sock = accept(listen_sock, (struct sockaddr*)&peer_addr, &peer_addrlen);
44 if(new_sock < 0)
45 {
46 perror("accept failed\n");
47 return 0;
48 }
49
50
51 printf("accept %s:%d, create new_sock %d\n", inet_ntoa(peer_addr.sin_addr), ntohs(peer_addr.sin_port), new_sock);
52
53
54
55 char buf[1024] = {0};
56 ssize_t recv_size = recv(new_sock, buf, sizeof(buf) - 1, 0);
57 if(recv_size < 0)
58 {
59 perror("recv failed\n");
60 continue;
61 }
62 else if(recv_size == 0)
63 {
64 perror("peer shutdown\n");
65 close(new_sock);
66
67 close(listen_sock);
68 return 0;
69 }
70
71
72 printf("client %d say: %s\n", new_sock, buf);
73
74
75 memset(buf, '\0', sizeof(buf));
76 sprintf(buf, "i am server, hello client %d\n", new_sock);
77
78 ssize_t send_size = send(new_sock, buf, strlen(buf), 0);
79 if(send_size < 0)
80 {
81 perror("send failed\n");
82 continue;
83 }
84 }
85
86 close(listen_sock);
87 return 0;
88 }
客户端
1 #include <stdio.h>
2 #include <string.h>
3 #include <unistd.h>
4 #include <sys/socket.h>
5 #include <netinet/in.h>
6 #include <arpa/inet.h>
7
8 int main()
9 {
10 int sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
11 if(sockfd < 0)
12 {
13 perror("sockfd failed\n");
14 return 0;
15 }
16
17
18 struct sockaddr_in dest_addr;
19 dest_addr.sin_family = AF_INET;
20 dest_addr.sin_port = htons(12345);
21 dest_addr.sin_addr.s_addr = inet_addr("159.75.12.23");
22 int ret = connect(sockfd, (struct sockaddr*)&dest_addr, sizeof(dest_addr));
23 if(ret < 0)
24 {
25 perror("connect failed\n");
26 return 0;
27 }
28
29 while(1)
30 {
31
32 char buf[1024] = "i am tcp client 1";
33 ssize_t send_size = send(sockfd, buf, strlen(buf), 0);
34 if(send_size < 0)
35 {
36 perror("send failed\n");
37 continue;
38 }
39
40
41 memset(buf, '\0', sizeof(buf));
42 ssize_t recv_size = recv(sockfd, buf, sizeof(buf) - 1, 0);
43 if(recv_size < 0)
44 {
45 perror("recv fail\n");
46 continue;
47 }
48 else if(recv_size == 0)
49 {
50 printf("server shutdown\n");
51 close(sockfd);
52 return 0;
53 }
54
55 printf("server say: %s\n", buf);
56 }
57
58 return 0;
59 }
多进程实现Tcp代码
客户端同单线程
服务端
1 #include <stdio.h>
2 #include <string.h>
3 #include <unistd.h>
4 #include <sys/socket.h>
5 #include <netinet/in.h>
6 #include <arpa/inet.h>
7 #include <signal.h>
8 #include <sys/wait.h>
9
W> 10 void sigcallback(int signo)
11 {
12 wait(NULL);
13 }
14
15 int main()
16 {
17 signal(SIGCHLD, sigcallback);
18
19
20 int listen_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
21 if(listen_sock < 0)
22 {
23 perror("listen_sock failed\n");
24 return 0;
25 }
26
27
28 struct sockaddr_in addr;
29 addr.sin_family = AF_INET;
30 addr.sin_port = htons(12345);
31 addr.sin_addr.s_addr = inet_addr("10.0.8.14");
32 int ret = bind(listen_sock, (struct sockaddr*)&addr, sizeof(addr));
33 if(ret < 0)
34 {
35 perror("bind failed\n");
36 return 0;
37 }
38
39
40 ret = listen(listen_sock, 1);
41 if(ret < 0)
42 {
43 perror("listen failed\n");
44 return 0;
45 }
46
47 while(1)
48 {
49
50 struct sockaddr_in peer_addr;
51 socklen_t peer_addrlen = sizeof(peer_addr);
52 int new_sock = accept(listen_sock, (struct sockaddr*)&peer_addr, &peer_addrlen);
53 if(new_sock < 0)
54 {
55 perror("accept failed\n");
56 return 0;
57 }
58
59
60 printf("accept %s:%d, create new_sock %d\n", inet_ntoa(peer_addr.sin_addr), ntohs(peer_addr.sin_port), new_sock);
61
62 pid_t pid = fork();
63 if(pid < 0)
64 {
65 perror("fork failed\n");
66 close(new_sock);
67 continue;
68 }
69 else if(pid == 0)
70 {
71
72
73 close(listen_sock);
74 while(1)
75 {
76
77 char buf[1024] = {0};
78 ssize_t recv_size = recv(new_sock, buf, sizeof(buf) - 1, 0);
79 if(recv_size < 0)
80 {
81 perror("recv failed\n");
82 continue;
83 }
84 else if(recv_size == 0)
85 {
86 perror("peer shutdown\n");
87 close(new_sock);
88
89 close(listen_sock);
90 return 0;
91 }
92
93
94 printf("client %d say: %s\n", new_sock, buf);
95
96
97 memset(buf, '\0', sizeof(buf));
98 sprintf(buf, "i am server, hello client %d\n", new_sock);
99
100 ssize_t send_size = send(new_sock, buf, strlen(buf), 0);
101 if(send_size < 0)
102 {
103 perror("send failed\n");
104 continue;
105 }
106 }
107 }
108 else
109 {
110
111 close(new_sock);
112 continue;
113 }
114 }
115
116 close(listen_sock);
117 return 0;
118 }
多线程实现Tcp代码
#pragma once
2
3 #include <stdio.h>
4 #include <string.h>
5 #include <unistd.h>
6 #include <pthread.h>
7 #include <sys/socket.h>
8 #include <netinet/in.h>
9 #include <arpa/inet.h>
10 #include <string>
11
12 using namespace std;
13
14 class Tcp_Svr
15 {
16 public:
17 Tcp_Svr()
18 {
19 sockfd_ = -1;
20 }
21
22 ~Tcp_Svr()
23 {
24
25
26 }
27
28 int CreatSocket()
29 {
30
31 sockfd_ = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
32 if(sockfd_ < 0)
33 {
34 perror("sockfd_ failed");
35 return -1;
36 }
37 return sockfd_;
38 }
39
40 int Bind(const string& ip = "0.0.0.0", uint16_t port = 12345)
41 {
42
43 struct sockaddr_in addr;
44 addr.sin_family = AF_INET;
45 addr.sin_port = htons(port);
46 addr.sin_addr.s_addr = inet_addr(ip.c_str());
47 int ret = bind(sockfd_, (struct sockaddr*)&addr, sizeof(addr));
48 if(ret < 0)
49 {
50 perror("bind failed");
51 return -1;
52 }
53 return 0;
54 }
55
56 int Listen(int backlog = 5)
57 {
58
59 int ret = listen(sockfd_, backlog);
60 if(ret < 0)
61 {
62 perror("listen failed");
63 return -1;
64 }
65 return 0;
66 }
67
68 int Accept(struct sockaddr_in* peer_addr)
69 {
70
71 socklen_t peer_addrlen = sizeof(struct sockaddr_in);
72
73 int ret = accept(sockfd_, (struct sockaddr*)peer_addr, &peer_addrlen);
74 if(ret < 0)
75 {
76 peer_addr = NULL;
77 return -1;
78 }
79
80 return ret;
81 }
82
83 void Close()
84 {
85 close(sockfd_);
86 }
87
88 void SetSockFd(int sockfd)
89 {
90 sockfd_ = sockfd;
91 }
92
93 int Send(const string& data)
94 {
95 ssize_t send_size = send(sockfd_, data.c_str(), data.size(), 0);
96 if(send_size < 0)
97 {
98 perror("send failed");
99 return -1;
100 }
101 return send_size;
102 }
103
104 int Recv(string* data)
105 {
106 data->clear();
107 char buf[1024] = {0};
108 ssize_t recv_size = recv(sockfd_, buf, sizeof(buf) - 1, 0);
109 if(recv_size < 0)
110 {
111 perror("recv failed");
112 return -1;
113 }
114 else if(recv_size == 0)
115 {
116 printf("peer shutdown\n");
117 return -100;
118 }
119
120 data->assign(buf, strlen(buf));
121 return recv_size;
122 }
123
124 private:
125 int sockfd_;
126 };
1 #include "tcp_thread.hpp"
2
3 #define CHECK_RET(v) if(v < 0){return -1;}
4
5 void* tcp_thread_start(void* arg)
6 {
7 pthread_detach(pthread_self());
8 Tcp_Svr* new_ts = (Tcp_Svr*)arg;
9
10 while(1)
11 {
12
13 string data = "";
14 int ret = new_ts->Recv(&data);
15 if(ret == -100)
16 {
17 new_ts->Close();
18 delete new_ts;
19 pthread_exit(NULL);
20 }
21 else if(ret <= -1)
22 {
23 continue;
24 }
25 else if(ret > 0)
26 {
27 printf("client say:%s\n", data.c_str());
28 }
29
30
31 data.clear();
32
33 data.assign("i am server");
34 new_ts->Send(data);
35 }
36
37 return NULL;
38 }
39
40 int main()
41 {
42 Tcp_Svr ts;
43 CHECK_RET(ts.CreatSocket());
44 CHECK_RET(ts.Bind());
45 CHECK_RET(ts.Listen());
46
47 while(1)
48 {
49 struct sockaddr_in peer_addr;
50 int new_sockfd = ts.Accept(&peer_addr);
51 if(new_sockfd < 0)
52 {
53 continue;
54 }
55
56
57
58
59
60 Tcp_Svr* new_ts = new Tcp_Svr();
61 if(new_ts == NULL)
62 {
63
64 close(new_sockfd);
65 continue;
66 }
67
68 new_ts->SetSockFd(new_sockfd);
69
70 pthread_t tid;
71 int ret = pthread_create(&tid, NULL, tcp_thread_start, (void*)new_ts);
72 if(ret < 0)
73 {
74 perror("pthread_create failed");
75
76 new_ts->Close();
77 delete new_ts;
78 continue;
79 }
80
81
82 }
83 return 0;
84 }
|