avatar

Socket通信流程简介

服务器端——storeserv.c

引入头文件

1
2
3
4
5
6
7
8
9
#include <netdb.h>
#include <sys/socket.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <memory.h>
#include <signal.h>
#include <time.h>

函数声明

1
2
3
4
5
6
// 定义为全局描述符
int sockfd;

void sig_handler(int signo);
void out_addr(struct sockaddr_in *clientaddr);
void do_service(int fd);

主函数

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
int main(int argc, char *argv[])
{
if(argc < 2)
{
printf("Usage : %s <port>\n", argv[0]);
exit(1);
}

if(signal(SIGINT, sig_handler) == SIG_ERR)
{
perror("Signal Sigint Error");
exit(1);
}

/*
步骤1:创建socket(套接字)
注:socket创建于内核中,是一个结构体.
AF_INET:IPV4
SOCK_STREAM:tcp协议
*/
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if(sockfd < 0)
{
perror("Socket Error");
exit(1);
}

// 步骤2:调用bind函数将socket和地址(包括ip、port)进行绑定
struct sockaddr_in serveraddr;
memset(&serveraddr, 0, sizeof(serveraddr));
// 向地址中填入ip、port、internet地址族类型
serveraddr.sin_family = AF_INET; // IPV4
serveraddr.sin_port = htons(atoi(argv[1])); // port
serveraddr.sin_addr.s_addr = INADDR_ANY;
if(bind(sockfd, (struct sockaddr*)&serveraddr, sizeof(serveraddr)) < 0)
{
perror("Bind Error");
exit(1);
}

/*
步骤3:调用listen函数启动监听(指定port监听)
通知系统去接受来自客户端的连接请求(将接收到的客户端连接请求放置到对应的队列中)
第二个参数:队列的长度
*/
if(listen(sockfd, 10) < 0)
{
perror("Listen Error");
exit(1);
}

/*
步骤4:调用accept函数从队列中获得一个客户端的请求连接
并返回新的socket描述符
*/
int fd;
struct sockaddr_in clientaddr;
socklen_t clientaddr_len = sizeof(clientaddr);
while(1)
{
fd = accept(sockfd, (struct sockaddr*)&clientaddr, &clientaddr_len);
if(fd < 0)
{
perror("Accept Error");
continue;
}

// 步骤5:调用IO函数(read/write)和连接的客户端进行双向的通信
out_addr(&clientaddr);
do_service(fd);
}
// 步骤6:关闭socket
close(fd);

return 0;
}

函数原型

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
// 处理Ctrl+C信号
void sig_handler(int signo)
{
if(signo == SIGINT)
{
printf("\nServer Close\n");
close(sockfd);
exit(1);
}
}

// 输出连接上来的客户端相关信息
void out_addr(struct sockaddr_in *clientaddr)
{
// 将端口从网络字节序转换成点分十进制
int port = ntohs(clientaddr->sin_port);
char ip[16];
memset(ip, 0, sizeof(ip));
// 将ip地址从网络字节序转换成点分十进制
inet_ntop(AF_INET, &clientaddr->sin_addr.s_addr, ip, sizeof(ip));
printf("Client: %s(%d) connected\n", ip, port);

}

// 将系统时间给到客户端
void do_service(int fd)
{
while(1)
{
char buffer[30], result[1024];
memset(buffer, 0, sizeof(buffer));
size_t size_buf;
// 获得系统时间
long t = time(0);
char *s = ctime(&t);

// 获得客户端输入的信息
if(size_buf = read(fd, buffer, sizeof(buffer)) < 0)
{
perror("Read Error");
}

// 如果客户端输入q或Q不再继续监听
if(!strcmp(buffer, "q\n") || !strcmp(buffer, "Q\n"))
{
puts("Client Disconnected...");
break;
}

strcat(result, "Message from server:");
strcat(result, buffer);
strcat(result, s);
size_t size = strlen(result) * sizeof(char);

// 将服务器端处理后的信息写回到客户端
if(write(fd, result, size) != size)
{
perror("Write to Client Erroe");
}
// 将result置空
memset(result, 0, sizeof(result));
}
}

客户端——mpclient.c

引入头文件

1
2
3
4
5
6
7
#include<netdb.h>
#include<sys/socket.h>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<memory.h>
#include<unistd.h>

主函数

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
int main(int argc, char *argv[])
{
if(argc < 3)
{
printf("Usage : %s <IP> <port>\n", argv[0]);
exit(1);
}

// 步骤1:创建socket
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
if(sockfd < 0)
{
perror("Socket Error");
exit(1);
}

// 创建服务器端地址,并且填入ip、port和地址族类型(ipv4)
struct sockaddr_in serveraddr;
memset(&serveraddr, 0, sizeof(serveraddr));
serveraddr.sin_family = AF_INET;
serveraddr.sin_port = htons(atoi(argv[2]));
// 将ip地址转换为网络字节序
inet_pton(AF_INET, argv[1], &serveraddr.sin_addr.s_addr);

// 步骤2:客户端调用connect函数连接到服务器端
if(connect(sockfd, (struct sockaddr*)&serveraddr, sizeof(serveraddr)) < 0)
{
perror("Connect Error");
exit(1);
}

// 步骤3:调用IO函数(read/write)和服务器端进行双向通信
char buffer[100];
memset(buffer, 0, sizeof(buffer));
size_t size;

while(1)
{
// 遇到换行符或者文件末尾
fgets(buffer, 100, stdin);

if(!strcmp(buffer, "q\n") || !strcmp(buffer, "Q\n"))
{
write(sockfd, buffer, strlen(buffer));
shutdown(sockfd, SHUT_WR);
return;
}
// 写到客户端
if((size = write(sockfd, buffer, strlen(buffer))) <0)
{
perror("Write to Server Error");
}

// 读取服务器返回的信息,并且写入到缓存当中
if((size = read(sockfd, buffer, sizeof(buffer))) < 0)
{
perror("Read Server information Error");
}

// 写入到屏幕上
if(write(STDOUT_FILENO, buffer, size) != size)
{
perror("Write Screen Error");
}
}

// 步骤4:关闭socket
close(sockfd);

return 0;
}

终端运行

编译

1
2
gcc storeserv.c -o storeserv
gcc mpclient.c -o mpclient

启动

1
2
3
4
// 启动服务器
./storeserv 8888
// 启动客户端
./mpclient 127.0.0.1 8888

运行

1
2
3
4
5
// stdin中输入
gy is handsome!
// 服务器返回
Message from server:gy is handsome!
Wed Jul 1 21:52:56 2020
文章作者: Gy
文章链接: http://sgyat.cn/2020/07/01/Socket编程/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 年轻没有梦
打赏
  • 微信
    微信
  • 支付宝
    支付宝

评论