-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathtcp_server.c
158 lines (130 loc) · 4.84 KB
/
tcp_server.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <netdb.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#define SERVER_PORT 8089
/*
监听后,一直处于accept阻塞状态,
直到有客户端连接,
当客户端如数quit后,断开与客户端的连接
*/
int main(int argc, char **argv)
{
// 调用socket函数返回的文件描述符
int serverSocket;
// 声明两个套接字sockaddr_in结构体变量,分别表示客户端和服务器
struct sockaddr_in server_addr;
struct sockaddr_in client_addr;
int addr_len = sizeof(client_addr);
int client;
char buffer[4096];
int iDataNum;
// socket函数,失败返回-1
// int socket(int domain, int type, int protocol);
// 第一个参数表示使用的地址类型,一般都是ipv4,AF_INET
// 第二个参数表示套接字类型:tcp:面向连接的稳定数据传输SOCK_STREAM
// 第三个参数设置为0
if ((serverSocket = socket(AF_INET, SOCK_STREAM, 0)) < 0)
{
perror("socket");
return 1;
}
bzero(&server_addr, sizeof(server_addr));
// 初始化服务器端的套接字,并用htons和htonl将端口和地址转成网络字节序
server_addr.sin_family = AF_INET;
if(argc > 1)
{
server_addr.sin_port = htons(atoi(argv[1]));
}
else
{
server_addr.sin_port = htons(SERVER_PORT);
}
// ip可是是本服务器的ip,也可以用宏INADDR_ANY代替,代表0.0.0.0,表明所有地址
server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
// 对于bind,accept之类的函数,里面套接字参数都是需要强制转换成(struct sockaddr *)
// bind三个参数:服务器端的套接字的文件描述符,
if (bind(serverSocket, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0)
{
perror("connect");
return 1;
}
// 设置服务器上的socket为监听状态
if (listen(serverSocket, 5) < 0)
{
perror("listen");
return 1;
}
while (1)
{
printf("监听端口: %d\n", SERVER_PORT);
// 调用accept函数后,会进入阻塞状态
// accept返回一个套接字的文件描述符,这样服务器端便有两个套接字的文件描述符,
// serverSocket和client。
// serverSocket仍然继续在监听状态,client则负责接收和发送数据
// clientAddr是一个传出参数,accept返回时,传出客户端的地址和端口号
// addr_len是一个传入-传出参数,传入的是调用者提供的缓冲区的clientAddr的长度,以避免缓冲区溢出。
// 传出的是客户端地址结构体的实际长度。
// 出错返回-1
client = accept(serverSocket, (struct sockaddr *)&client_addr, (socklen_t *)&addr_len);
if (client < 0)
{
perror("accept");
continue;
}
printf("等待消息...\n");
// inet_ntoa ip地址转换函数,将网络字节序IP转换为点分十进制IP
// 表达式:char *inet_ntoa (struct in_addr);
printf("Client IP is %s\n", inet_ntoa(client_addr.sin_addr));
printf("Client Port is %d\n", htons(client_addr.sin_port));
while (1)
{
printf("读取消息:\n");
memset(buffer, 0, sizeof(buffer));
iDataNum = recv(client, buffer, 4096, 0);
if (iDataNum < 0)
{
perror("recv null");
break;
}
if (strcmp(buffer, "QUIT") == 0)
{
printf("Client IP : %s Port : %d has been closed\n", inet_ntoa(client_addr.sin_addr), htons(client_addr.sin_port));
memset(buffer, 0, sizeof(buffer));
snprintf(buffer, sizeof(buffer), "%s", "tcp server will exit ... ...");
if(send(client, buffer, strlen(buffer), 0) < 0)
{
printf("send failed : %s \n", buffer);
}
break;
}
else if((strcmp(buffer, "IP&PORT") == 0))
{
memset(buffer, 0, sizeof(buffer));
snprintf(buffer, sizeof(buffer), "%s %d", inet_ntoa(client_addr.sin_addr), htons(client_addr.sin_port));
if(send(client, buffer, strlen(buffer), 0) < 0)
{
printf("send failed : %s \n", buffer);
}
}
else
{
if(send(client, buffer, strlen(buffer), 0) < 0)
{
printf("send failed : %s \n", buffer);
}
}
memset(buffer, 0, sizeof(buffer));
}
}
close(serverSocket);
return 0;
}