一、TCP Socket Server 服务器
基本流程框架
1. 创建服务器套接字Socket
int socket(int domain, int type, int protocol);
AF_INET IPv4 Internet protocols ip(7)
SOCK_STREAM Provides sequenced, reliable, two-way, connection-based
byte streams. An out-of-band data transmission mecha‐
nism may be supported.
RETURN VALUE
On success, a file descriptor for the new socket is returned. On er‐
ror, -1 is returned, and errno is set appropriately.
配置参数(网络模型、协议)
- 英特尔网
IPv4
- TCP协议用到
SOCK_STREAM
- protocol协议书
判断返回值
- 成功后,将返回新套接字的文件描述符。
- 出错时,返回 -1,并适当设置 errno。
实现 创建服务器套接字 功能点
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
int main()
{
int server_socket;
// 创建服务器套接字
server_socket = socket(AF_INET, SOCK_STREAM, 0);
if (server_socket == -1) {
perror("Socket creation failed");
exit(1);
}
// 设置服务器地址结构
// 绑定服务器套接字
// 监听传入的连接请求
// 接受客户端连接请求
// 从客户端接收数据
// 向客户端发送数据
// 关闭套接字
return 0;
}
2. 设置服务器地址结构htons、inet_aton和inet_ntoa
int bind(int sockfd, const struct sockaddr *addr,
socklen_t addrlen);
配置参数
用
struct sockaddr_in
替换struct sockaddr
在/usr/include
搜索struct sockaddr_in
cd /usr/include/
grep
找到对应的struct sockaddr_in
位置
grep "struct sockaddr_in {" * -nir
这条命令是在Linux/Unix操作系统中使用grep
工具来搜索包含字符串 “struct sockaddr_in {” 的文件。具体含义如下:
grep
: 这是用于搜索文本的命令行工具。"struct sockaddr_in {"
: 这是要搜索的字符串,它包含了结构体定义的一部分。*
: 这表示搜索所有文件,不仅限于当前目录,而是包括所有子目录。-n
: 这是grep
的一个选项,用于显示匹配行的行号。-i
: 这是grep
的一个选项,用于执行大小写不敏感的搜索,即不区分大小写。-r
: 这是grep
的一个选项,用于递归地搜索子目录。
因此,这条命令的含义是在当前目录及其所有子目录中,搜索包含字符串 “struct sockaddr_in {” 的文件,并显示匹配行的行号。这通常用于查找包含特定结构体或关键字的源代码文件,以便进行源代码分析或修改。
最后在通过
vim编辑器
找到文件中申明定义的位置
vim linux/in.h +261
vim +对应的位置的头文件 +行号
/* Structure describing an Internet (IP) socket address. */
#if __UAPI_DEF_SOCKADDR_IN
#define __SOCK_SIZE__ 16 /* sizeof(struct sockaddr) */
struct sockaddr_in {
__kernel_sa_family_t sin_family; /* Address family */
__be16 sin_port; /* Port number */
struct in_addr sin_addr; /* Internet address */
/* Pad to size of `struct sockaddr'. */
unsigned char __pad[__SOCK_SIZE__ - sizeof(short int) -
sizeof(unsigned short int) - sizeof(struct in_addr)];
};
#define sin_zero __pad /* for BSD UNIX comp. -FvK */
#endif
这段代码定义了一个结构体 struct sockaddr_in
,用于描述一个 Internet (IP) 套接字地址。这是在网络编程中经常使用的结构,通常用于指定套接字的地址信息。下面是该结构体的成员及其作用:
sin_family
:一个表示地址家族(Address family)的字段。这个字段指定了套接字的地址类型,例如 IPv4 或 IPv6。在这个结构中,sin_family
是一个类型为__kernel_sa_family_t
的字段,用于表示地址家族。sin_port
:一个表示端口号的字段。这个字段指定了套接字的端口号,用于标识应用程序在主机上的通信端口。sin_addr
:一个结构体,表示 Internet 地址。通常,它包含 IP 地址信息,用于指定要连接的远程主机的地址。sin_zero
:用于兼容 BSD UNIX 计算机的字段,通常不需要在现代系统中使用。
此外,结构体中还包含了一个 __pad
字段,用于填充结构体的大小,使其与 struct sockaddr
相匹配。__pad
用于在不同系统上确保结构体的大小一致,以便在网络编程中正确传递套接字地址。
这个结构体通常在网络编程中用于指定要连接的远程服务器的地址,其中 sin_family
指定了地址类型,sin_port
指定了端口号,sin_addr
包含了远程主机的 IP 地址。这允许程序建立与远程主机的连接。
配置第二个参数端口号的字段,要调用字节转换API
因为X86系统CPU是小端字节序,网络是大段字节序
所以要将16位主机字节序数据转换为网络字节序。
#include <arpa/inet.h>
uint16_t htons(uint16_t hostshort);
uint32_t htonl(uint32_t hostlong);
uint16_t ntohs(uint16_t netshort);
uint32_t ntohl(uint32_t netlong);
uint16_t htons(uint16_t host16bitvalue)
:
- 函数名含义:
htons
代表"host to network short",即将主机字节序的16位整数转换为网络字节序。 - 参数:
host16bitvalue
是一个16位整数,表示主机字节序的值。 - 返回值:函数返回一个16位整数,表示网络字节序的值。
配置第三个参数sin_addr
是结构体struct in_addr
里面的变量
#if __UAPI_DEF_IN_ADDR
/* Internet address. */
struct in_addr {
__be32 s_addr;
};
#endif
调用inet_aton和inet_ntoa函数 (地址转换)
inet_aton 函数:把字符串形式的“192.168.1.123”转为网络能识别的格式
int inet_aton(const char *cp, struct in_addr *inp);
inet_aton 函数用于将一个字符串表示的IPv4地址转换为二进制形式的IPv4地址。
inet_ntoa 函数:把网络格式的ip地址转为字符串形式
char *inet_ntoa(struct in_addr in);
inet_ntoa 函数用于将一个二进制形式的IPv4地址转换为字符串表示的IPv4地址。
实现 设置服务器地址结构 功能点
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
//#include <linux/in.h>
#include <arpa/inet.h>
#include <netinet/in.h>
int main()
{
int server_socket;
struct sockaddr_in server_addr;
// 创建服务器套接字
server_socket = socket(AF_INET, SOCK_STREAM, 0);
if (server_socket == -1) {
perror("Socket creation failed");
exit(1);
}
// 设置服务器地址结构
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(12345);
// server_addr.sin_addr.s_addr = INADDR_ANY;
inet_aton("172.168.1.1", &server_addr.sin_addr);
// 绑定服务器套接字
// 监听传入的连接请求
// 接受客户端连接请求
// 从客户端接收数据
// 向客户端发送数据
// 关闭套接字
return 0;
}
3. 绑定服务器套接字Bind
调用bind函数API
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
int bind(int sockfd, const struct sockaddr *addr,
socklen_t addrlen);
bind
函数用于将一个套接字绑定到一个特定的地址和端口号,以便在该地址上监听传入的连接。下面是 bind
函数的详细信息:
-
功能:
bind
函数用于将指定的套接字与特定的网络地址和端口绑定在一起,从而将套接字关联到指定的通信地址。这通常用于服务器端创建监听套接字。 -
参数:
sockfd
:要绑定的套接字的文件描述符。addr
:一个指向struct sockaddr
结构体的指针,其中包含了要绑定的地址信息。这个结构体的具体类型(struct sockaddr_in
或struct sockaddr_in6
)取决于套接字的地址族(address family)。addrlen
:addr
结构体的大小(以字节为单位)。
-
返回值:
- 如果
bind
函数成功,它会返回0。 - 如果发生错误,它会返回-1,并设置全局变量
errno
以指示错误类型。
- 如果
-
注意事项:
- 要成功地使用
bind
,套接字必须事先创建,通常是使用socket
函数创建的。 addr
结构体的内容应该与套接字的地址族和类型匹配。例如,如果使用AF_INET
和SOCK_STREAM
创建的套接字,addr
应该是struct sockaddr_in
类型,并包含IPv4地址和端口号。- 通常,服务器端在调用
bind
后还需要调用listen
函数,以开始监听传入的连接请求。
- 要成功地使用
bind
函数在服务器端网络编程中非常重要,因为它允许服务器指定要监听的地址和端口,以等待客户端的连接请求。通过将套接字绑定到特定地址和端口,服务器可以确保客户端可以找到并与之通信。
实现 绑定服务器套接字 功能点
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
//#include <linux/in.h>
#include <arpa/inet.h>
#include <netinet/in.h>
int main()
{
int server_socket;
struct sockaddr_in server_addr;
// 创建服务器套接字
server_socket = socket(AF_INET, SOCK_STREAM, 0);
if (server_socket == -1) {
perror("Socket creation failed");
exit(1);
}
// 设置服务器地址结构
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(12345);
// server_addr.sin_addr.s_addr = INADDR_ANY;
inet_aton("172.168.1.1", &server_addr.sin_addr);
// 绑定服务器套接字
if (bind(server_socket, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0) {
perror("Binding failed");
exit(1);
}
// 监听传入的连接请求
// 接受客户端连接请求
// 从客户端接收数据
// 向客户端发送数据
// 关闭套接字
return 0;
}
4. 监听传入的连接请求Listen
调用listen函数
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
int listen(int sockfd, int backlog);
listen
函数用于在服务器端创建监听套接字,主要用于准备接受客户端的连接请求。它有以下功能:
-
功能:
listen
函数用于告知操作系统,套接字(通常是服务器套接字)已准备好接受客户端的连接请求。一旦调用listen
,套接字进入监听状态,可以接受连接请求。listen
函数还通过参数backlog
来指定了等待连接的最大队列长度。这个参数表示可以排队等待服务的客户端连接的数量上限,即在未连接队列中可以排队等待的连接请求数。
-
内核维护的两个队列:
- 已连接队列(Completed Connection Queue):已经建立连接的客户端连接会进入这个队列,等待服务器接受。
- 未连接队列(Incomplete Connection Queue):尚未完全建立连接的客户端请求会进入这个队列,等待服务器调用
accept
函数来接受连接。
在未连接队列中,操作系统会保存客户端的连接请求,这些请求还未完成TCP的三次握手过程。这允许服务器先接受连接请求,然后再完成握手。
-
TCP的三次握手:
- 在服务器调用
listen
函数之后,它可以开始接受客户端连接请求。 - 当客户端尝试连接到服务器时,它发送一个SYN(同步)请求,表示建立连接。这个请求进入未连接队列。
- 服务器接受到这个请求后,向客户端发送一个SYN-ACK(同步-确认)响应,表示愿意建立连接。此时,连接请求从未连接队列移动到已连接队列。
- 最后,客户端收到服务器的SYN-ACK响应后,发送一个ACK(确认),确认连接的建立。此时,TCP的三次握手完成,连接正式建立,可以开始数据传输。
- 在服务器调用
listen
函数的作用是告知操作系统,服务器套接字已准备好接受客户端连接请求,并通过指定 backlog
参数来控制未连接队列中等待连接的客户端连接数上限。一旦连接请求到达服务器,它们将会排队等待在未连接队列中,直到服务器接受连接或达到 backlog
上限。
这个机制允许服务器在接受连接之前排队等待连接请求,同时处理其他任务。当连接完成三次握手后,连接进入已连接队列,服务器可以继续与客户端通信。
- 参数:
sockfd
:这是一个已创建的套接字描述符,通常是一个服务器套接字,已经绑定到服务器地址和端口上,用于监听客户端的连接请求。backlog
:这是一个整数值,表示等待连接队列的最大长度,也就是可以排队等待服务的客户端连接的数量上限。这个参数决定了在未连接队列中能够排队的连接数上限。
总之,listen
函数告知操作系统,服务器套接字已准备好接受客户端连接请求,同时通过 backlog
参数来控制未连接队列中等待连接的客户端连接数上限。一旦连接请求到达服务器,它们将会排队等待在未连接队列中,直到服务器接受连接或达到 backlog
上限。
实现 监听传入的连接请求 功能点
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
//#include <linux/in.h>
#include <arpa/inet.h>
#include <netinet/in.h>
int main()
{
int server_socket;
struct sockaddr_in server_addr;
// 创建服务器套接字
server_socket = socket(AF_INET, SOCK_STREAM, 0);
if (server_socket == -1) {
perror("Socket creation failed");
exit(1);
}
// 设置服务器地址结构
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(12345);
// server_addr.sin_addr.s_addr = INADDR_ANY;
inet_aton("172.168.1.1", &server_addr.sin_addr);
// 绑定服务器套接字
if (bind(server_socket, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0) {
perror("Binding failed");
exit(1);
}
// 监听传入的连接请求
if (listen(server_socket, 5) == 0) {
printf("Listening for incoming connections...\n");
} else {
perror("Listening failed");
exit(1);
}
// 接受客户端连接请求
// 从客户端接收数据
// 向客户端发送数据
// 关闭套接字
return 0;
}
5. 接受客户端连接请求Accept
调用accept函数
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
accept
函数是用于接受客户端连接请求的套接字函数。它的功能、参数和返回值如下:
功能:
accept
函数用于从已连接队列中接受客户端连接请求,创建一个新的套接字,用于与客户端进行通信。一旦接受连接,服务器和客户端之间的数据传输就可以开始。
参数:
sockfd
:这是一个监听套接字(通常是服务器套接字),它已经通过socket
、bind
和listen
函数准备好接受客户端连接。addr
:这是一个指向struct sockaddr
类型的指针,用于存储客户端的地址信息。通常,可以将其设置为NULL
,表示不需要获取客户端地址。addrlen
:这是一个整数,表示addr
结构体的长度,通常是sizeof(struct sockaddr)
。
返回值:
- 如果成功,
accept
函数返回一个新的套接字描述符,该描述符用于与客户端通信。这个新套接字是一个专门用于连接到客户端的套接字,它是唯一的。 - 如果发生错误,
accept
返回 -1,并设置errno
变量以指示错误的类型。
一旦 accept
函数成功,服务器可以使用返回的新套接字与客户端建立通信。这个新套接字是一个全双工的通信通道,允许数据在服务器和客户端之间双向传输。
通常,服务器会在循环中多次调用 accept
函数,以便接受多个客户端的连接请求。每次调用 accept
都会返回一个新的套接字,用于处理与一个客户端的通信。这使得服务器能够同时与多个客户端建立连接,并提供并发服务。
连接到本地
ifconfig
inet 192.168.1.145
ping 一下网络看看是否能连通
telnet 网络协议
Telnet 是一种网络协议,用于在远程计算机之间建立交互式文本通信会话。它允许用户通过网络连接到远程主机,就像他们直接坐在目标计算机的终端上一样。用户可以在 Telnet 会话中执行命令、查看远程主机的输出并与远程系统进行交互。
以下是 Telnet 的一些关键特点和用途:
-
远程控制: Telnet 允许用户从本地计算机远程连接到其他计算机,以执行命令、管理文件和资源,甚至配置网络设备。
-
文本协议: Telnet 会话是基于文本的,用户通过纯文本输入和输出进行通信。这使得 Telnet 在控制和监控远程设备、服务器和网络设备方面非常有用。
-
端口号: Telnet 默认使用 TCP 端口 23。用户可以通过指定目标计算机的 IP 地址和端口号来建立 Telnet 连接。
-
非加密: Telnet 是一种不加密的协议,这意味着通信数据在传输过程中是明文的,不安全。因此,在互联网上使用 Telnet 时,应格外小心,特别是在涉及敏感信息或登录凭据时。
-
调试和远程访问: Telnet 常用于远程服务器的调试、系统管理和故障排除。管理员可以通过 Telnet 连接到远程服务器,检查系统状态并执行必要的操作。
-
替代协议: 由于 Telnet 的不安全性,SSH(Secure Shell)已经替代了 Telnet 作为更安全的远程访问协议。SSH 提供了加密通信和更高级别的安全性功能。
虽然 Telnet 仍然存在,但在大多数情况下,特别是在公共网络中,使用 SSH 或其他加密协议是更安全的选择,以保护敏感信息免受未经授权的访问。但在某些特定情况下,Telnet 仍然是一个有用的工具,例如在内部网络中用于设备管理和调试。
telnet 192.168.1.145 12345
实现 接受客户端连接请求 功能点
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
//#include <linux/in.h>
#include <arpa/inet.h>
#include <netinet/in.h>
int main()
{
int server_socket, client_socket;
struct sockaddr_in server_addr, client_addr;
socklen_t client_len = sizeof(client_addr);
// 创建服务器套接字
server_socket = socket(AF_INET, SOCK_STREAM, 0);
if (server_socket == -1) {
perror("Socket creation failed");
exit(1);
}
// 设置服务器地址结构
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(12345);
// server_addr.sin_addr.s_addr = INADDR_ANY;
inet_aton("192.168.1.145", &server_addr.sin_addr);
// 绑定服务器套接字
if (bind(server_socket, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0) {
perror("Binding failed");
exit(1);
}
// 监听传入的连接请求
if (listen(server_socket, 10) == 0) {
printf("Listening for incoming connections...\n");
} else {
perror("Listening failed");
exit(1);
}
// 接受客户端连接请求
client_socket = accept(server_socket, (struct sockaddr*)&client_addr, &client_len);
if (client_socket < 0) {
perror("Acceptance failed");
exit(1);
}
// 从客户端接收数据
// 向客户端发送数据
// 关闭套接字
printf("connect\n");
while (1);
return 0;
}
6. 服务器数据的收发Recv、Send
从客户端接收数据
recv
函数是用于从套接字接收数据的函数。它通常用于网络编程中,从网络连接中读取数据。以下是 recv
函数的详细信息:
函数原型:
ssize_t recv(int sockfd, void *buf, size_t len, int flags);
参数:
sockfd
:这是一个已经建立连接的套接字,用于接收数据。buf
:这是一个指向存储接收数据的缓冲区的指针。len
:这是要接收的数据的最大字节数,通常是缓冲区的大小。flags
:这是一个用于指定接收选项的整数。常见的选项包括0
(无选项)和MSG_WAITALL
(阻塞接收,等待接收完整的数据包)等。
返回值:
- 如果成功,
recv
函数返回接收到的数据的字节数。可以小于len
,表示接收的数据不满足请求的字节数。 - 如果连接已关闭,
recv
返回0
,表示对端已关闭连接。 - 如果发生错误,
recv
返回 -1,并设置errno
变量以指示错误的类型。
功能:
recv
函数用于从套接字接收数据,将数据从网络中读取到缓冲区中。这个数据可以是来自另一台计算机的数据,也可以是同一台计算机上的本地套接字的数据。接收的数据可以包含任何字节,无论是文本数据还是二进制数据。recv
函数通常是阻塞的,即它将等待直到接收到数据或发生错误。但可以通过flags
参数将套接字设置为非阻塞模式,以使recv
函数在没有数据可接收时立即返回。
在网络编程中,recv
函数通常用于从套接字接收来自网络连接的数据。客户端使用 recv
函数从服务器接收响应数据,服务器使用 recv
函数从客户端接收请求数据。这允许双方在网络上进行数据交换,实现各种通信和应用。
从客户端接收数据 功能点
// 从客户端接收数据
recv(client_socket, buffer, sizeof(buffer), 0);
printf("Client says: %s\n", buffer);
向客户端发送数据
send
函数是用于将数据从套接字发送到目标的函数。它通常用于网络编程中,将数据从客户端发送到服务器,或者从服务器发送数据给客户端。以下是 send
函数的详细信息:
函数原型:
ssize_t send(int sockfd, const void *buf, size_t len, int flags);
参数:
sockfd
:这是一个已经建立连接的套接字,用于发送数据。buf
:这是一个指向包含要发送数据的缓冲区的指针。len
:这是要发送的数据的字节数。flags
:这是一个用于指定发送选项的整数。常见的选项包括0
(无选项)和MSG_DONTWAIT
(非阻塞发送)等。
返回值:
- 如果成功,
send
函数返回已发送数据的字节数,可以小于len
,表示未能发送完所有数据。 - 如果发生错误,
send
返回 -1,并设置errno
变量以指示错误的类型。
功能:
send
函数用于将数据从套接字发送到目标。这个目标可以是另一台计算机上的套接字,也可以是同一台计算机上的本地套接字。发送的数据可以包含任何字节,无论是文本数据还是二进制数据。send
函数通常是阻塞的,即它将等待直到所有数据发送完毕或发生错误。但可以通过flags
参数将套接字设置为非阻塞模式,以使send
函数在无法发送数据时立即返回。
在网络编程中,send
函数通常用于将数据发送到套接字,以便在服务器和客户端之间进行通信。客户端使用 send
函数将请求发送给服务器,服务器使用 send
函数将响应发送回客户端。这允许双方在网络上进行数据交换,实现各种通信和应用。
向客户端发送数据 功能点
// 向客户端发送数据
send(client_socket, message, sizeof(message), 0);
printf("Message sent to client.\n");
7. 关闭套接字Close
#include <unistd.h>
// 关闭套接字
close(server_socket);
close(client_socket);
代码实现服务器代码实现服务器
my_socket.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
//#include <linux/in.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <unistd.h>
int main()
{
int server_socket, client_socket;
struct sockaddr_in server_addr, client_addr;
socklen_t client_len = sizeof(client_addr);
// 创建服务器套接字
server_socket = socket(AF_INET, SOCK_STREAM, 0);
if (server_socket == -1) {
perror("Socket creation failed");
exit(1);
}
// 设置服务器地址结构
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(12345);
// server_addr.sin_addr.s_addr = INADDR_ANY;
inet_aton("192.168.1.145", &server_addr.sin_addr);
// 绑定服务器套接字
if (bind(server_socket, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0) {
perror("Binding failed");
exit(1);
}
// 监听传入的连接请求
if (listen(server_socket, 10) == 0) {
printf("Listening for incoming connections...\n");
} else {
perror("Listening failed");
exit(1);
}
// 接受客户端连接请求
client_socket = accept(server_socket, (struct sockaddr*)&client_addr, &client_len);
if (client_socket < 0) {
perror("Acceptance failed");
exit(1);
}
// 从客户端接收数据
recv(client_socket, buffer, sizeof(buffer), 0);
printf("Client says: %s\n", buffer);
// 向客户端发送数据
send(client_socket, message, sizeof(message), 0);
printf("Message sent to client.\n");
// 关闭套接字
close(server_socket);
close(client_socket);
return 0;
}
二、TCP Socket Client 客户端
基本流程框架
1. 创建客户端套接字Socket
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
int main()
{
int client_socket;
// 创建客户端套接字
client_socket = socket(AF_INET, SOCK_STREAM, 0);
if (client_socket == -1) {
perror("Socket creation failed");
exit(1);
}
// 设置服务器地址结构
// 连接到服务器
// 向服务器发送数据
// 从服务器接收数据
// 关闭套接字
return 0;
}
2. 设置服务器地址结构htons、inet_aton和inet_ntoa
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
//#include <linux/in.h>
#include <arpa/inet.h>
#include <netinet/in.h>
int main()
{
int client_socket;
struct sockaddr_in server_addr;
// 创建客户端套接字
client_socket = socket(AF_INET, SOCK_STREAM, 0);
if (client_socket == -1) {
perror("Socket creation failed");
exit(1);
}
// 设置服务器地址结构
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(12345);
// server_addr.sin_addr.s_addr = INADDR_ANY;
inet_aton("192.168.1.145", &server_addr.sin_addr);
// 连接到服务器
// 向服务器发送数据
// 从服务器接收数据
// 关闭套接字
return 0;
}
3. 连接到服务器Connect
connect
函数是用于在客户端建立与服务器的连接的函数。它通常用于网络编程中,用于客户端套接字连接到服务器套接字。以下是 connect
函数的详细信息:
函数原型:
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
int connect(int sockfd, const struct sockaddr *addr,
socklen_t addrlen);
参数:
sockfd
:这是客户端套接字的文件描述符,用于建立连接。addr
:这是一个指向服务器地址信息的结构体指针,通常是struct sockaddr
的指针,其中包含了服务器的 IP 地址和端口号等信息。addrlen
:这是服务器地址信息的长度,通常是sizeof(struct sockaddr)
。
返回值:
- 如果成功,
connect
函数返回0
,表示连接已建立。 - 如果发生错误,
connect
返回 -1,并设置errno
变量以指示错误的类型。
功能:
connect
函数用于在客户端建立与服务器的连接。客户端将其套接字连接到服务器的套接字,以便进行数据交换。一旦连接建立,客户端和服务器之间可以相互发送和接收数据。connect
函数通常是阻塞的,即它将等待直到连接成功或发生错误。连接成功后,客户端可以使用该套接字向服务器发送请求,并接收服务器的响应。
在网络编程中,connect
函数是客户端建立与服务器通信的第一步。客户端使用 connect
函数连接到服务器,然后可以使用 send
函数发送请求,使用 recv
函数接收响应。这允许客户端与服务器之间进行双向通信,实现各种网络应用。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
//#include <linux/in.h>
#include <arpa/inet.h>
#include <netinet/in.h>
int main()
{
int client_socket;
struct sockaddr_in server_addr;
// 创建客户端套接字
client_socket = socket(AF_INET, SOCK_STREAM, 0);
if (client_socket == -1) {
perror("Socket creation failed");
exit(1);
}
// 设置服务器地址结构
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(12345);
// server_addr.sin_addr.s_addr = INADDR_ANY;
inet_aton("192.168.1.145", &server_addr.sin_addr);
// 连接到服务器
if (connect(client_socket, (struct sockaddr*)&server_addr, sizeof(server_addr)) == 0) {
printf("Connected to server.\n");
} else {
perror("Connection failed");
exit(1);
}
// 向服务器发送数据
// 从服务器接收数据
// 关闭套接字
return 0;
}
4. 客户端数据的收发Send、Recv
向服务器发送数据
// 向服务器发送数据
send(client_socket, message, sizeof(message), 0);
printf("Message sent to server.\n");
从服务器接收数据
// 从服务器接收数据
recv(client_socket, buffer, sizeof(buffer), 0);
printf("Server says: %s\n", buffer);
5. 关闭套接字Close
// 关闭套接字
close(client_socket);
代码实现客户端
my_client.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
//#include <linux/in.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <unistd.h>
int main()
{
int client_socket;
struct sockaddr_in server_addr;
char message[] = "Hello, server!";
char buffer[1024];
// 创建客户端套接字
client_socket = socket(AF_INET, SOCK_STREAM, 0);
if (client_socket == -1) {
perror("Socket creation failed");
exit(1);
}
// 设置服务器地址结构
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(12345);
// server_addr.sin_addr.s_addr = INADDR_ANY;
inet_aton("192.168.1.145", &server_addr.sin_addr);
// 连接到服务器
if (connect(client_socket, (struct sockaddr*)&server_addr, sizeof(server_addr)) == 0) {
printf("Connected to server.\n");
} else {
perror("Connection failed");
exit(1);
}
// 向服务器发送数据
send(client_socket, message, sizeof(message), 0);
printf("Message sent to server.\n");
// 从服务器接收数据
recv(client_socket, buffer, sizeof(buffer), 0);
printf("Server says: %s\n", buffer);
// 关闭套接字
close(client_socket);
return 0;
}