1.0版本(有1.0, 就有2.0):
客户端1次请求, 服务器回应一次请求, 结束
server.cpp
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
| #include <stdio.h> #include <string.h> #include <stdlib.h> #include <unistd.h> #include <arpa/inet.h> #include <sys/socket.h> #include <netinet/in.h>
int main(){ int serv_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
struct sockaddr_in serv_addr; memset(&serv_addr, 0, sizeof(serv_addr)); serv_addr.sin_family = AF_INET; serv_addr.sin_addr.s_addr = inet_addr("127.0.0.1"); serv_addr.sin_port = htons(1234); bind(serv_sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr));
listen(serv_sock, 20);
struct sockaddr_in clnt_addr; socklen_t clnt_addr_size = sizeof(clnt_addr); int clnt_sock = accept(serv_sock, (struct sockaddr*)&clnt_addr, &clnt_addr_size);
char str[] = "http://c.biancheng.net/socket/"; write(clnt_sock, str, sizeof(str)); close(clnt_sock); close(serv_sock);
return 0; }
|
client.cpp:
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
| #include <stdio.h> #include <string.h> #include <stdlib.h> #include <unistd.h> #include <arpa/inet.h> #include <sys/socket.h>
int main(){ int sock = socket(AF_INET, SOCK_STREAM, 0);
struct sockaddr_in serv_addr; memset(&serv_addr, 0, sizeof(serv_addr)); serv_addr.sin_family = AF_INET; serv_addr.sin_addr.s_addr = inet_addr("127.0.0.1"); serv_addr.sin_port = htons(1234); connect(sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr)); char buffer[40]; read(sock, buffer, sizeof(buffer)-1); printf("Message form server: %s\n", buffer); close(sock);
return 0; }
|
补充知识
HBO和NBO的概念:
主机字节顺序HBO(Host Byte Order)
网络字节顺序NBO(Network Byte Order)
先转换再用
四个转换函数:
htonl()--"Host to Network Long"
htons()--"Host to Network Short"
ntohl()--"Network to Host Long"
ntohs()--"Network to Host Short"
INADDR_ANY 0.0.0.0泛指本机
作用: 如果有多个网卡, 只需要管理一个套接字, 接受某端口传输的数据就可以了
程序执行过程
client 运行后,通过 connect() 函数向 server 发起请求,
处于监听状态的 server 被激活,
执行 accept() 函数,接受客户端的请求,
然后执行 write() 函数向 client 传回数据。
client 接收到传回的数据后,
connect() 就运行结束了,
然后使用 read() 将数据读取出来。
源码剖析
1) 先说一下 server.cpp 中的代码。
第 11 行通过 socket() 函数创建了一个套接字,参数 AF_INET 表示使用 IPv4 地址,SOCK_STREAM 表示使用面向连接的套接字,IPPROTO_TCP 表示使用 TCP 协议。在 Linux 中,socket 也是一种文件,有文件描述符,可以使用 write() / read() 函数进行 I/O 操作,这一点已在《socket是什么》中进行了讲解。
第 19 行通过 bind() 函数将套接字 serv_sock 与特定的 IP 地址和端口绑定,IP 地址和端口都保存在 sockaddr_in 结构体中。
socket() 函数确定了套接字的各种属性,bind() 函数让套接字与特定的IP地址和端口对应起来,这样客户端才能连接到该套接字。
第 22 行让套接字处于被动监听状态。所谓被动监听,是指套接字一直处于“睡眠”中,直到客户端发起请求才会被“唤醒”。
第 27 行的 accept() 函数用来接收客户端的请求。程序一旦执行到 accept() 就会被阻塞(暂停运行),直到客户端发起请求。
第 31 行的 write() 函数用来向套接字文件中写入数据,也就是向客户端发送数据。
和普通文件一样,socket 在使用完毕后也要用 close() 关闭。
2) 再说一下 client.cpp 中的代码。client.cpp 中的代码和 server.cpp 中有一些区别。
第 19 行代码通过 connect() 向服务器发起请求,服务器的IP地址和端口号保存在 sockaddr_in 结构体中。直到服务器传回数据后,connect() 才运行结束。
第 23 行代码通过 read() 从套接字文件中读取数据。
finally
是时候了, 改造它!
从0开始造大楼
Author:
Qin Peng
License:
Copyright (c) 2020 BY QPWLKQ LICENSE
Slogan:
每一个不曾起舞的日子, 都是对生命的辜负