网络模型
网络编程
网络编程
目的 实现多机通信(跨机跨系统通信)
地址 IP地址/端口号
协议(数据交互格式) 如HTTP/HTTPS/TCP/UDP
/*
就像货运一样
地址 具体收货地址+收货人+联系方式(信件/短信/语音等)
协议 空运/水运/铁路运输/货车运输/还是人力运输(包含货物装箱打包方式)
像告知另一对象消息(数据)
我是请求者 对方是接收者 我要知道对方的 住址+姓名+联系方式(地址)
信息的传输方式 信件/短信/语音消息/实时语音/视频通话(协议)
*/
TCP与UDP的对比
1. TCP面向连接(如打电话要先拨号建立连接);UDP是无连接的,即发送数据之前 不需 要建立连接
2. TCP提供可靠的服务。也就是说,通过TCP连接传送的数据,无差错,不丢失,不重复,且按序到达;UDP尽最大努力交付,即不保证可靠交付
3. TCP面向字节流,实际上是TCP把数据看成一连串无结构的字节流;UDP是面向报文的
UDP没有拥塞控制,因此网络出现拥塞不会使源主机的发送速率降低(对实时应用很有用,如IP电话,实时视频会议等)
4. 每一条TCP连接只能是点到点的;UDP支持一对一,一对多,多对一和多对多的交互通信
5. TCP首部开销20字节;UDP的首部开销小,只有8个字节
6. TCP的逻辑通信信道是全双工的可靠信道,UDP则是不可靠信道
字节序
小端字节序 -起始内存存储末尾数据(低序字节)
大端字节序 -起始内存存储起始数据(高序字节)
X86是小端字节序
网络是大端字节序
#include <netinet/in.h>
#include <arpa/inet.h>
uint16_t htons(uint16_t host16bitvalue); //返回网络字节序的值uint32_t htonl(uint32_t host32bitvalue); //返回网络字节序的值uint16_t ntohs(uint16_t net16bitvalue); //返回主机字节序的值uint32_t ntohl(uint32_t net32bitvalue); //返回主机字节序的值
h代表host,n代表net,s代表short(两个字节),l代表long(4个字节),通过上面的4个函数可以实现主机字节序和网络字节序之间的转换。有时可以用INADDR_ANY,INADDR_ANY指定地址让操作系统自己获取
ip地址与网络格式的相互转换
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
// IP地址字符串转网络格式IP信息
int inet_aton(const char *cp, struct in_addr *inp); //常用
cp 字符串IP
inp 保存网络格式IP的指针(结构体属性)
用法// inet_aton("127.0.0.1",&sock_addr.sin_addr);
/*
参数说明
cp:一个指向以点分十进制(dotted-decimal)格式表示的 IPv4 地址的 C 字符串的指针。例如:"192.168.1.1"。
inp:一个指向 struct in_addr 结构体的指针,该结构体用于存储转换后的地址。
*/
返回值
如果成功,函数返回非零值(通常为 1)。如果转换失败,函数返回零。
in_addr_t inet_addr(const char *cp);
in_addr_t inet_network(const char *cp);
// 网络格式IP信息转字符串IP
char *inet_ntoa(struct in_addr in); //常用
in 保存网络格式IP
参数说明
in:一个 struct in_addr 结构体,包含了要转换的 IPv4 地址。
返回值
函数返回一个指向静态缓冲区的指针,该缓冲区包含了以点分十进制格式表示的 IPv4 地址。由于这个缓冲区是静态的,因此连续调用 inet_ntoa 会覆盖之前的结果。
如果传入无效的 struct in_addr 结构体,函数的行为是未定义的。
struct in_addr inet_makeaddr(in_addr_t net, in_addr_t host);
in_addr_t inet_lnaof(struct in_addr in);
in_addr_t inet_netof(struct in_addr in);
sockt套接字
搭建服务器
socket函数原型
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
// 创建套接字
int socket(int domain, int type, int protocol);
domain 指明所使用的协议族,通常为 AF_INET,表示互联网协议族(TCP/IP 协议族)
AF_INET IPV4 因特网域
AF_INET6 IPv6 因特网域
AF_UNIX Unix域 本地进程间通信(IPC)
AF_LOCAL 本地进程间通信(IPC)
AF_ROUTE 路由套接字
AF_KEY 密钥套接字
AF_UNSPEC 末指定
AF_PACKET 原始数据包套接字,允许直接访问底层网络设备
注意:AF 代表 "Address Family"。
type 参数指定 socket 的类型:
SOCK_STREAM 流式套接字提供可靠的、面向连接的通信流;它使用TCP协议,从而保证了数据传输的正确性和顺序性
SOCK_DGRAM 数据报套接字定义了一种无连接的服,数据通过相互独立的报文进行传输,是无序的,并且不保证是可靠、无差错的。它使用数据报协议UDP.
SOCK_RAW 允许程序使用低层协议,原始套接字允许对底层协议如 IP或 ICMP 进行直接访问,功能强大但使用较为不便,主要用于一些协议的开发。
SOCK_SEQPACKET:双向的、可靠的、基于连接的连续数据包服务,通常用于像 SCTP 这样的协议。
注意:SOCK 代表 "Socket"。
protocol 通常赋值”0(会更具type自动选择对应的传输协议)
0 选择 type 类型对应的默认协议
IPPROTO_TCP TCP传输协议
IPPROTO_UDP UDP传输协议
IPPROTO_SCTP SCTP传输协议
IPPROTO_TIPC TIPC传输协议
返回值
如果成功,socket 函数将返回一个非负整数,这是新创建的套接字的描述符。
如果出现错误,它将返回 -1,并设置全局变量 errno 以指示错误。
bind函数原型
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
// 绑定端口及其IP地址
int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
sockfd socket 函数返回的套接字描述符。它标识了需要进行绑定的套接字
addr 指向 sockaddr 结构体(或其兼容类型,如 sockaddr_in 对于 IPv4 或 sockaddr_in6 对于 IPv6)的指针,该结构体包含了套接字需要绑定的地址和 端口信息
/*
常见的地址结构体是 sockaddr_in,它通常用于 IPv4 地址
struct sockaddr_in {
short sin_family; // 地址族,通常为 AF_INET
unsigned short sin_port; // 端口号(网络字节序)
struct in_addr sin_addr; // IPv4 地址
char sin_zero[8]; // 未使用,填充至结构体的固定大小
};
sin_family 字段设置为 AF_INET 表示 IPv4。
sin_port 字段设置为要绑定的端口号(需要转换为网络字节序,通常使用 htons 函数)。
sin_addr 字段是一个 in_addr 结构体,通常使用 inet_pton 或 inet_addr 函数来设置 IPv4 地址
*/
addrlen 地址结构的大小)这是一个整数,指定了 addr 参数所指向的地址结构的 大小(以字节为单位)。对于 sockaddr_in 结构体,这个值通常是 sizeof(struct sockaddr_in)。
返回值
如果成功,bind 函数返回 0。
如果出现错误,返回 -1,并设置全局变量 errno 以指示错误。
listen函数原型 服务器监听
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
int listen(int sockfd, int backlog);
sockfd socket 函数返回的套接字描述符。它标识了需要进行绑定的套接字
backlog 这个参数定义了内核应该为相应套接字排队的最大连接个数。这个队列包含了已经完成三次握手但服务器还未调用 accept 处理的连接。如果队列已满,客户端连接将被拒绝。(需要注意的是,backlog 的实际含义可能会因操作系统的不同而有所差异。在 Linux 上,backlog 是两个队列之和:已完成连接队列(已完成三次握手但服务器还未调用 accept)和未完成连接队列(还在等待服务器完成三次握手的连接)。而在一些其他系统上,backlog 可能只表示已完成连接队列的大小。)
返回值
如果成功,listen 函数返回 0。
如果失败,返回 -1 并设置全局变量 errno 以指示错误。
accept函数原型 等待客户端请求并响应连接请求客户端??
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
sockfd socket 函数返回的套接字描述符。它标识了需要进行绑定的套接字
addr 指向 struct sockaddr 的指针,用于存放客户端的地址信息。如果调用 者对此信息不感兴趣,可以将此参数设置为 NULL。
addrlen 指向 socklen_t 类型的指针,用来存放客户端地址信息的实际长度。在调 用 accept 之前,应该将此参数指向一个 socklen_t 变量,并初始化为 sizeof(struct sockaddr_in)(或其他适当的地址结构大小,比如 sizeof(struct sockaddr_in6) 对于 IPv6)。在函数返回时,这个变量将被设置 为客户端地址信息的实际长度。
返回值(返回的请求客户端连接句柄,通信中需要用的这个句柄)
如果成功,accept 函数会返回一个新的套接字描述符,用于与客户端通信。这个新套接字和原始套接字 sockfd 是相互独立的。
如果失败,accept 函数会返回 -1,并设置全局变量 errno 以指示错误。
#define _GNU_SOURCE /* See feature_test_macros(7) */
#include <sys/socket.h>
int accept4(int sockfd, struct sockaddr *addr,socklen_t *addrlen, int flags);
测试连接
图解步骤
连接成功后,退出需要操作 **Ctrl+] 然后在telnet 命令行输入 quit **
测试连接代码
#include <stdio.h>
#include <stdlib.h> // exit
#include <sys/types.h>
#include <sys/socket.h>
//#include <linux/in.h> // struct sockaddr_in Problems with header files(头文件的问题)
#include <netinet/in.h> // htons inet_aton
#include <arpa/inet.h> // htons inet_aton
int main(int argc,char** argv)
{
int sock_fd; // socket handle(套接字句柄)
// 1.create a TCP protocol socket(创建一个TCP协议的套接字)
sock_fd = socket(AF_INET,SOCK_STREAM,0);
if(sock_fd == -1){ // check whether the creation is successful(判断是否创建成功)
perror("socket");
exit(-1);
}
struct sockaddr_in sock_addr; // define the IP address and its port information structure(定义IP地址及其端口信息结构体)
sock_addr.sin_family = AF_INET; // Address family(地址族)
sock_addr.sin_port = htons(6767); // Port, and X86 small-endian to (big-endian) network byte order(端口,并且X86小端字节序转(大端)网络字节序)
// sock_addr.sin_addr.s_addr = inet_aton("127.0.0.1"); // Struct nested struct,ip address string translation network format(结构体嵌套结构体,ip地址字符串转换网络格式)
inet_aton("192.168.1.252",&sock_addr.sin_addr); // Struct nested struct,ip address string translation network format(结构体嵌套结构体,ip地址字符串转换网络格式)
// 2.bind the IP address and port number(绑定IP地址及其端口)
bind(sock_fd,(struct sockaddr*)&sock_addr,sizeof(struct sockaddr_in));
// 3.network monitoring(网络监听)
listen(sock_fd,10); // Monitor ten(监听十个)
// 4.Wait for the client request and respond to the connection request client(等待客户端请求并响应连接请求客户端)
int cloent_fd = accept(sock_fd,NULL,NULL); // Return the request client connection handle that is needed for communication(返回的请求客户端连接句柄,通信中需要用的这个句柄)
// 5.
// 6.
printf("telnet successful\n");
while(1); // Prevent server shutdown (exit)(防止服务器关闭(退出))
printf("successful.\n");
return 0;
}
单次向服务器通信
#include <stdio.h>
#include <stdlib.h> // exit
#include <sys/types.h>
#include <sys/socket.h>
//#include <linux/in.h> // struct sockaddr_in Problems with header files(头文件的问题)
#include <netinet/in.h> // htons inet_aton
#include <arpa/inet.h> // htons inet_aton
#include <string.h> // memset
int main(int argc,char** argv)
{
int sock_fd; // socket handle(套接字句柄)
// 1.create a TCP protocol socket(创建一个TCP协议的套接字)
sock_fd = socket(AF_INET,SOCK_STREAM,0);
if(sock_fd == -1){ // check whether the creation is successful(判断是否创建成功)
perror("socket");
exit(-1);
}
struct sockaddr_in sock_addr; // define the IP address and its port information structure(定义IP地址及其端口信息结构体)
memset(&sock_addr,0,sizeof(struct sockaddr_in)); // Data clearing(数据清空)
sock_addr.sin_family = AF_INET; // Address family(地址族)
sock_addr.sin_port = htons(6767); // Port, and X86 small-endian to (big-endian) network byte order(端口,并且X86小端字节序转(大端)网络字节序)
// sock_addr.sin_addr.s_addr = inet_aton("127.0.0.1"); // Struct nested struct,ip address string translation network format(结构体嵌套结构体,ip地址字符串转换网络格式)
inet_aton("192.168.1.252",&sock_addr.sin_addr); // Struct nested struct,ip address string translation network format(结构体嵌套结构体,ip地址字符串转换网络格式)
// 2.bind the IP address and port number(绑定IP地址及其端口)
bind(sock_fd,(struct sockaddr*)&sock_addr,sizeof(struct sockaddr_in));
// 3.network monitoring(网络监听)
listen(sock_fd,10); // Monitor ten(监听十个)
struct sockaddr_in cloent_addr; // Client IP address and port structure(客户端IP信息及其端口结构体)
memset(&cloent_addr,0,sizeof(struct sockaddr_in)); // Data clearing(数据清空)
int len = sizeof(struct sockaddr_in); // Length is pointer(长度是指针)
// 4.Wait for the client request and respond to the connection request client(等待客户端请求并响应连接请求客户端)
int cloent_fd = accept(sock_fd,(struct sockaddr*)&cloent_addr,&len); // Return the request client connection handle that is needed for communication(返回的请求客户端连接句柄,通信中需要用的这个句柄)
if(cloent_fd == -1){
perror("accept");
exit(-1);
}else{ // Success here is best achieved by generating a child process(这里成功最好生成一个子进程)
printf("client successful IP:%s\n",inet_ntoa(cloent_addr.sin_addr)); // Print the client IP address(打印客户端IP)
}
char readbuf[1024];
int n_read;
// 5.Receive client data(接收客户端数据)
n_read = read(cloent_fd,readbuf,1024);
if(n_read == -1){
perror("read");
}else{
printf("len:%d--%s\n",strlen(readbuf),readbuf);
}
char* writebuf = "Server data has been received.";
// 6.Returns a message to the client(给客户端返回消息)
write(cloent_fd,writebuf,strlen(writebuf)+1);
// while(1); // Prevent server shutdown (exit)(防止服务器关闭(退出))
return 0;
}
客户端
connect函数原型
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
// 连接服务器
int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
sockfd socket 函数返回的套接字描述符。它标识了需要进行绑定的套接字
addr 指向 sockaddr 结构体的指针,该结构体包含要连接到的服务器的地址信息。在实际使用中,这个结构体通常会被 sockaddr_in(IPv4)或 sockaddr_in6(IPv6)等结构体所替代,这些结构体提供了更具体的地址信息。
注意:由于 sockaddr 是一个通用的地址结构,你可能需要将其强制转换为更具体的类型(如 sockaddr_in),以便填充和访问其字段.
addrlen 类型:socklen_t 描述:这是 addr 参数所指向的 sockaddr 结构体的大小(以字节为单位)。这允许 connect 函数知道该结构体有多大,从而正确地读取地址信息。
如果连接成功,函数返回 0。
如果连接失败,函数返回 -1,并设置全局错误变量 errno 以指示错误。你可以使用诸如 perror 或 strerror 的函数来获取有关错误的详细信息
作用:connect 函数尝试与由 addr 参数指定的地址建立一个连接。如果连接成功,函数返回 0;如果连接失败,它返回 -1 并设置全局错误变量 errno 以指示错误
客户机与服务端通信
#include <stdio.h>
#include <stdlib.h> // exit
#include <sys/types.h>
#include <sys/socket.h>
//#include <linux/in.h> // struct sockaddr_in Problems with header files(头文件的问题)
#include <netinet/in.h> // htons inet_aton
#include <arpa/inet.h> // htons inet_aton
#include <string.h> // memset
int main(int argc,char** argv)
{
int sock_fd; // socket handle(套接字句柄)
// 1.create a TCP protocol socket(创建一个TCP协议的套接字)
sock_fd = socket(AF_INET,SOCK_STREAM,0);
if(sock_fd == -1){ // check whether the creation is successful(判断是否创建成功)
perror("socket");
exit(-1);
}
struct sockaddr_in sock_addr; // define the IP address and its port information structure(定义IP地址及其端口信息结构体)
memset(&sock_addr,0,sizeof(struct sockaddr_in)); // Data clearing(数据清空)
sock_addr.sin_family = AF_INET; // Address family(地址族)
sock_addr.sin_port = htons(6767); // Port, and X86 small-endian to (big-endian) network byte order(端口,并且X86小端字节序转(大端)网络字节序)
// sock_addr.sin_addr.s_addr = inet_aton("127.0.0.1"); // Struct nested struct,ip address string translation network format(结构体嵌套结构体,ip地址字符串转换网络格式)
inet_aton("192.168.1.252",&sock_addr.sin_addr); // Struct nested struct,ip address string translation network format(结构体嵌套结构体,ip地址字符串转换网络格式)
// 2.Establish a connection/connection(建立连接/连接服务器)
if(connect(sock_fd,(struct sockaddr*)&sock_addr,sizeof(struct sockaddr)) == -1){
perror("connect");
exit(-1);
}
// 3.Send data to the server(向服务器发送数据)
char* writebuf = "Did the server receive the data?"; // Ready to send data(准备发送的数据)
write(sock_fd,writebuf,strlen(writebuf)+1);
// 4.Receive the data returned by the server(接收服务器返回的数据)
char readbuf[1024]; // Prepare the data cache(准备数据缓存区)
int n_read;
n_read = read(sock_fd,readbuf,1024);
if(n_read == -1){
perror("read");
}else{
printf("len:%d--%s\n",strlen(readbuf),readbuf);
}
// while(1); // Prevent server shutdown (exit)(防止服务器关闭(退出))
return 0;
}
优化交互(还有BUG)
服务端
#include <stdio.h>
#include <stdlib.h> // exit
#include <sys/types.h>
#include <sys/socket.h>
//#include <linux/in.h> // struct sockaddr_in Problems with header files(头文件的问题)
#include <netinet/in.h> // htons inet_aton
#include <arpa/inet.h> // htons inet_aton
#include <string.h> // memset
#include <unistd.h>
int main(int argc,char** argv) // Set up the server in parameter transfer mode(使用传参方式搭建服务器)
{
if(argc != 3){ // Judge the number of parameters(判断参数个数)
perror("main");
exit(-1);
}
int sock_fd; // socket handle(套接字句柄)
// 1.create a TCP protocol socket(创建一个TCP协议的套接字)
sock_fd = socket(AF_INET,SOCK_STREAM,0);
if(sock_fd == -1){ // check whether the creation is successful(判断是否创建成功)
perror("socket");
exit(-1);
}
struct sockaddr_in sock_addr; // define the IP address and its port information structure(定义IP地址及其端口信息结构体)
memset(&sock_addr,0,sizeof(struct sockaddr_in)); // Data clearing(数据清空)
sock_addr.sin_family = AF_INET; // Address family(地址族)
// atio(argv[1]) Parameter strong rotation(参数强转)
sock_addr.sin_port = htons(atoi(argv[2])); // Port, and X86 small-endian to (big-endian) network byte order(端口,并且X86小端字节序转(大端)网络字节序)
// sock_addr.sin_addr.s_addr = inet_aton("127.0.0.1"); // Struct nested struct,ip address string translation network format(结构体嵌套结构体,ip地址字符串转换网络格式)
// The IP address is also passed(IP地址也传参)
inet_aton(argv[1],&sock_addr.sin_addr); // Struct nested struct,ip address string translation network format(结构体嵌套结构体,ip地址字符串转换网络格式)
// 2.bind the IP address and port number(绑定IP地址及其端口)
bind(sock_fd,(struct sockaddr*)&sock_addr,sizeof(struct sockaddr_in));
// 3.network monitoring(网络监听)
listen(sock_fd,10); // Monitor ten(监听十个)
struct sockaddr_in cloent_addr; // Client IP address and port structure(客户端IP信息及其端口结构体)
memset(&cloent_addr,0,sizeof(struct sockaddr_in)); // Data clearing(数据清空)
int len = sizeof(struct sockaddr_in); // Length is pointer(长度是指针)
int cloent_fd;
char readbuf[1024];
int n_read;
char writebuf[1024] = {0};
while(1){ // Always monitor the server for new client access(一直监听服务器是否有新的客户端接入)
// 4.Wait for the client request and respond to the connection request client(等待客户端请求并响应连接请求客户端)
cloent_fd = accept(sock_fd,(struct sockaddr*)&cloent_addr,&len); // Return the request client connection handle that is needed for communication(返回的请求客户端连接句柄,通信中需要用的这个句柄)
if(cloent_fd == -1){
perror("accept");
exit(-1);
}
// Success here is best achieved by generating a child process(这里成功最好生成一个子进程)
printf("client successful IP:%s\n",inet_ntoa(cloent_addr.sin_addr)); // Print the client IP address(打印客户端IP)
if(fork() == 0){
if(fork() == 0){
// 6.Returns a message to the client(给客户端返回消息)
while(1){
memset(writebuf,0,sizeof(writebuf));
printf("input:");
fgets(writebuf,1024-1,stdin);
write(cloent_fd,writebuf,strlen(writebuf)+1);
}
}
while(1){
// 5.Receive client data(接收客户端数据)
memset(readbuf,0,sizeof(readbuf));
n_read = read(cloent_fd,readbuf,1024);
if(n_read == -1){
perror("read");
}else{
printf("len:%ld--%s\n",strlen(readbuf),readbuf);
}
}
//break;
}
}
// while(1); // Prevent server shutdown (exit)(防止服务器关闭(退出))
return 0;
}
客户端
#include <stdio.h>
#include <stdlib.h> // exit
#include <sys/types.h>
#include <sys/socket.h>
//#include <linux/in.h> // struct sockaddr_in Problems with header files(头文件的问题)
#include <netinet/in.h> // htons inet_aton
#include <arpa/inet.h> // htons inet_aton
#include <string.h> // memset
#include <unistd.h>
int main(int argc,char** argv) // Set up the server in parameter transfer mode(使用传参方式搭建服务器)
{
if(argc != 3){ // Judge the number of parameters(判断参数个数)
perror("main");
exit(-1);
}
int sock_fd; // socket handle(套接字句柄)
// 1.create a TCP protocol socket(创建一个TCP协议的套接字)
sock_fd = socket(AF_INET,SOCK_STREAM,0);
if(sock_fd == -1){ // check whether the creation is successful(判断是否创建成功)
perror("socket");
exit(-1);
}
struct sockaddr_in sock_addr; // define the IP address and its port information structure(定义IP地址及其端口信息结构体)
memset(&sock_addr,0,sizeof(struct sockaddr_in)); // Data clearing(数据清空)
sock_addr.sin_family = AF_INET; // Address family(地址族)
sock_addr.sin_port = htons(atoi(argv[2])); // Port, and X86 small-endian to (big-endian) network byte order(端口,并且X86小端字节序转(大端)网络字节序)
// sock_addr.sin_addr.s_addr = inet_aton("127.0.0.1"); // Struct nested struct,ip address string translation network format(结构体嵌套结构体,ip地址字符串转换网络格式)
inet_aton(argv[1],&sock_addr.sin_addr); // Struct nested struct,ip address string translation network format(结构体嵌套结构体,ip地址字符串转换网络格式)
// 2.Establish a connection/connection(建立连接/连接服务器)
if(connect(sock_fd,(struct sockaddr*)&sock_addr,sizeof(struct sockaddr)) == -1){
perror("connect");
exit(-1);
}
char writebuf[1024] = {0}; // Ready to send data(准备发送的数据)
char readbuf[1024]; // Prepare the data cache(准备数据缓存区)
int n_read;
while(1){
// 3.Send data to the server(向服务器发送数据)
if(fork() == 0){
while(1){
memset(writebuf,0,sizeof(writebuf));
printf("input:");
// gets(writebuf); // This usage is not safe(这个用法不安全)
fgets(writebuf,1024-1,stdin);
write(sock_fd,writebuf,strlen(writebuf)+1);
}
}
while(1){
// 4.Receive the data returned by the server(接收服务器返回的数据)
memset(readbuf,0,sizeof(readbuf));
n_read = read(sock_fd,readbuf,1024);
if(n_read == -1){
perror("read");
}else{
printf("len:%ld--%s\n",strlen(readbuf),readbuf);
}
}
}
// while(1); // Prevent server shutdown (exit)(防止服务器关闭(退出))
return 0;
}
优化服务端
#include <stdio.h>
#include <stdlib.h> // exit
#include <sys/types.h>
#include <sys/socket.h>
//#include <linux/in.h> // struct sockaddr_in Problems with header files(头文件的问题)
#include <netinet/in.h> // htons inet_aton
#include <arpa/inet.h> // htons inet_aton
#include <string.h> // memset
#include <unistd.h>
int main(int argc,char** argv) // Set up the server in parameter transfer mode(使用传参方式搭建服务器)
{
if(argc != 3){ // Judge the number of parameters(判断参数个数)
perror("main");
exit(-1);
}
int sock_fd; // socket handle(套接字句柄)
// 1.create a TCP protocol socket(创建一个TCP协议的套接字)
sock_fd = socket(AF_INET,SOCK_STREAM,0);
if(sock_fd == -1){ // check whether the creation is successful(判断是否创建成功)
perror("socket");
exit(-1);
}
struct sockaddr_in sock_addr; // define the IP address and its port information structure(定义IP地址及其端口信息结构体)
memset(&sock_addr,0,sizeof(struct sockaddr_in)); // Data clearing(数据清空)
sock_addr.sin_family = AF_INET; // Address family(地址族)
// atio(argv[1]) Parameter strong rotation(参数强转)
sock_addr.sin_port = htons(atoi(argv[2])); // Port, and X86 small-endian to (big-endian) network byte order(端口,并且X86小端字节序转(大端)网络字节序)
// sock_addr.sin_addr.s_addr = inet_aton("127.0.0.1"); // Struct nested struct,ip address string translation network format(结构体嵌套结构体,ip地址字符串转换网络格式)
// The IP address is also passed(IP地址也传参)
inet_aton(argv[1],&sock_addr.sin_addr); // Struct nested struct,ip address string translation network format(结构体嵌套结构体,ip地址字符串转换网络格式)
// 2.bind the IP address and port number(绑定IP地址及其端口)
bind(sock_fd,(struct sockaddr*)&sock_addr,sizeof(struct sockaddr_in));
// 3.network monitoring(网络监听)
listen(sock_fd,10); // Monitor ten(监听十个)
struct sockaddr_in cloent_addr; // Client IP address and port structure(客户端IP信息及其端口结构体)
memset(&cloent_addr,0,sizeof(struct sockaddr_in)); // Data clearing(数据清空)
int len = sizeof(struct sockaddr_in); // Length is pointer(长度是指针)
int cloent_fd;
char readbuf[1024];
int n_read;
char writebuf[1024] = {0};
int mark = 0;
while(1){ // Always monitor the server for new client access(一直监听服务器是否有新的客户端接入)
// 4.Wait for the client request and respond to the connection request client(等待客户端请求并响应连接请求客户端)
cloent_fd = accept(sock_fd,(struct sockaddr*)&cloent_addr,&len); // Return the request client connection handle that is needed for communication(返回的请求客户端连接句柄,通信中需要用的这个句柄)
if(cloent_fd == -1){
perror("accept");
exit(-1);
}else{
mark++;
}
// Success here is best achieved by generating a child process(这里成功最好生成一个子进程)
printf("client successful IP:%s\n",inet_ntoa(cloent_addr.sin_addr)); // Print the client IP address(打印客户端IP)
if(fork() == 0){
if(fork() == 0){
// 6.Returns a message to the client(给客户端返回消息)
while(1){
//memset(writebuf,0,sizeof(writebuf));
//printf("input:");
//fgets(writebuf,1024-1,stdin);
sprintf(writebuf,"client NO.%d",mark);
write(cloent_fd,writebuf,strlen(writebuf)+1);
sleep(2);
}
}
while(1){
// 5.Receive client data(接收客户端数据)
memset(readbuf,0,sizeof(readbuf));
n_read = read(cloent_fd,readbuf,1024);
if(n_read == -1){
perror("read");
}else{
printf("len:%ld--%s\n",strlen(readbuf),readbuf);
}
}
//break;
}
}
// while(1); // Prevent server shutdown (exit)(防止服务器关闭(退出))
return 0;
}
拓展
键盘输入API
#include <stdio.h>
int fgetc(FILE *stream);
char *fgets(char *s, int size, FILE *stream);
s 指向储存输入内容地址的指针。
size 要读取的最大字符数量(包括字符串末尾的空字符)。
// stream 参数直接写stdin
stream 指向FILE对象的指针,指明要读入的文件。如果从键盘中读入输入的数据,则以 stdin作为参数,该标准输入标识符定义在<stdio.h>中。
int getc(FILE *stream);
int getchar(void);
int ungetc(int c, FILE *stream);