socket 函数
在C++中,socket()
函数用于创建一个新的网络套接字,它通常包含两个主要参数:域(domain)和类型(protocol)。这两个参数通常作为整数传递:
域名(Domain):
AF_INET
或AF_INET6
: 表示Internet协议(IPv4或IPv6)。这是最常见的,IPv4用AF_INET
表示。AF_UNIX
(仅限于Unix系统): 使用本地文件系统路径名通信。
类型(Protocol):
SOCK_STREAM
或IPPROTO_TCP
: 对于TCP连接,这个参数通常设置为流式套接字,代表可靠的数据传输,如HTTP。SOCK_DGRAM
或IPPROTO_UDP
: 对于UDP连接,这是一个数据报套接字,代表不可靠但更快的通信,如DNS查询。SOCK_RAW
或其他特定值: 如果需要访问底层网络协议(如ICMP、ARP等),可以使用原始套接字。
创建套接字的基本语法类似于:
int socket_type = SOCK_STREAM; // 或 SOCK_DGRAM
int domain = AF_INET; // 或 AF_INET6
int socket_fd = socket(domain, socket_type, 0);
在C++中,当你调用socket()
函数时,第三个参数通常设置为0。这其实是一个可选参数,用于指定套接字选项(socket options)的初始化列表。然而,在大多数情况下,如果省略这个参数,系统会使用默认的行为或配置。
0在这里并不是一个特殊选项,而是表示不提供初始选项。如果你想要应用特定的套接字选项,可以使用setsockopt()
函数传递一个结构体,其中包含了选项名称、值和选项长度。在不清楚具体需求的情况下,默认值0表示不立即设置任何选项,你可以之后再通过setsockopt()
来添加。
有时候,0也可能代表对特定版本的支持或者兼容性处理。但总的来说,对于新手来说,如果不明确需要自定义选项,通常可以直接忽略第三个参数,因为很多情况下socket()
函数不需要额外的初始化设置。
/* Create a new socket of type TYPE in domain DOMAIN, using
protocol PROTOCOL. If PROTOCOL is zero, one is chosen automatically.
Returns a file descriptor for the new socket, or -1 for errors. */
extern int socket (int __domain, int __type, int __protocol) __THROW;
bind函数
bind()
是网络编程中的一个核心函数,它通常在创建监听套接字(如TCP服务器端口)时使用,用于将套接字绑定到一个具体的IP地址和端口号上。其基本语法如下:
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
参数说明:
sockfd
: 表示已经创建好的套接字描述符(socket file descriptor)。addr
: 指向一个结构体地址,通常是struct sockaddr_in
(IPv4)或struct sockaddr_in6
(IPv6),包含了要绑定的IP地址和端口号信息。addrlen
: 结构体addr
的实际大小。
当bind()
函数成功执行后,套接字就被固定到了指定的IP地址和端口,等待连接请求。这对于服务端应用程序来说非常重要,因为它确定了服务器将在何处监听客户端的连接。
需要注意的是,bind()
之前通常需要先调用listen()
函数开启监听,然后通过accept()
接收新连接。
#include<sys/socket.h>
#include<arpa/inet.h>
void test() {
system("clear");
int sock = socket(PF_INET,SOCK_STREAM,0);
if (sock != -1) { //表示创建套接字成功
struct sockaddr_in addr;
addr.sin_family = AF_INET;//填写地址的协议族
addr.sin_addr.s_addr = inet_addr("192.168.0.1");//转化字符串ip地址
addr.sin_port = htons(3344);//端口 u_int16
//int bind (int __fd, __CONST_SOCKADDR_ARG __addr, socklen_t __len)
// # define __SOCKADDR_ARG struct sockaddr *__restrict
int ret = bind(sock,(sockaddr*)&addr,sizeof(addr));
//成功返回 0 失败返回 -1
if (bind != 0) {
// 出错
}
}
}
listen & accept
#include<sys/socket.h>
listen 函数
int listen(int sock,int backlog);
//成功返回 0 失败返回 -1
参数:
backlog:连接请求等待队列(Queue)的长度,若为5,则队列长度为5,表示最多使5个连接请求进入队列。
accept 函数
int accept(int socket,struct sockaddr* addr,socklen_t* addrlen);//会阻塞
//返回客户端套接字 描述符 失败返回 -1
TCP 编程
TCP服务端代码实现
tcp 服务器创建 基本流程
socket() 创建套接字
bind() 分配套接字地址
listen() 等待连接请求状态
accept() 允许连接
read()/write() 数据交换
close() 断开连接
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<iostream>
void error_handling(char* message);
void tcp_server() {
//1.创建两个套接字变量
int server_sock{};
int client_sock{};
//2.两个绑定的地址
typedef struct sockaddr_in sockAddr_in;
sockAddr_in server_addr{};
sockAddr_in client_addr{};
//创建客户端地址的长度
socklen_t client_addr_size{};
//创建一个当连接 接收到时的回文
const char* r_to_c_message = "hello world!\n";
//3.创建服务器套接字
//tcp 协议 面向 (链接/流) 的传输协议
server_sock = socket(PF_INET, SOCK_STREAM, 0);
//若 创建失败
if (server_sock == -1) {
std::cout << "creat socket failed !" << std::endl;
}
//4.绑定地址
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
server_addr.sin_port = htons(2345);
int ret_bind = bind(server_sock, (struct sockaddr*)&server_addr, sizeof(server_addr));
if (ret_bind == -1) {
std::cout << "bind failed!\n" << std::endl;
close(server_sock);
return;
}
//5.listen()
int ret_listen = listen(server_sock, 3);
if (ret_listen == -1) {
std::cout << "listen failed!\n" << std::endl;
close(server_sock);
return;
}
//6.accept()
client_sock = accept(server_sock, (struct sockaddr*)&client_addr, &client_addr_size);
if (client_sock == -1) {
std::cout << "client_sock accept failed!\n" << std::endl;
close(server_sock);
return;
}
//7.send message
ssize_t ret_write = write(client_sock,r_to_c_message,strlen(r_to_c_message));
if (ret_write != strlen(r_to_c_message)) {
std::cout << "ret_write failed!\n" << std::endl;
close(server_sock);
return;
}
//close(client_sock); 可以不执行
close(server_sock); //服务器关闭的时候,客户端会自动关闭
}
int main(int argc, char* argv[]) {
return 0;
}
void error_handling(char* message) {
printf("错误处理\n");
}
connect函数
int connect(int client_sock,struct sockaddr* servaddr,socklen_t addrlen);
//客户端套接字描述符
//mu'bioa目标服务器端地址信息和bian变量地址值
//地址长度
//成功返回0 否则-1
客户端套接字地址信息在哪?
创建套接字后立即调用connet函数,操作系统在内核中 根据计算机ip和端口在调用connect函数时自动分配,无需用标记的bind函数进行分配
TCP客户端代码实现及其联调
流程:
socket() 创建套接字
connect() 请求连接
r/w 交换数据
close() 断开连接
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<sys/wait.h>
#include<iostream>
void error_handling(char* message);
void tcp_server() {
//1.创建两个套接字变量
int server_sock{};
int client_sock{};
//2.两个绑定的地址
typedef struct sockaddr_in sockAddr_in;
sockAddr_in server_addr{};
sockAddr_in client_addr{};
//创建客户端地址的长度
socklen_t client_addr_size{};
//创建一个当连接 接收到时的回文
const char* r_to_c_message = "hello world!\n";
//3.创建服务器套接字
//tcp 协议 面向 (链接/流) 的传输协议
server_sock = socket(PF_INET, SOCK_STREAM, 0);
//若 创建失败
if (server_sock == -1) {
std::cout << "creat socket failed !" << std::endl;
}
//4.绑定地址
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = inet_addr("0.0.0.0");
server_addr.sin_port = htons(2345);
int ret_bind = bind(server_sock, (struct sockaddr*)&server_addr, sizeof(server_addr));
if (ret_bind == -1) {
std::cout << "bind failed!\n" << std::endl;
close(server_sock);
return;
}
//5.listen()
int ret_listen = listen(server_sock, 3);
if (ret_listen == -1) {
std::cout << "listen failed!\n" << std::endl;
close(server_sock);
return;
}
std::cout << "listen is over!" << std::endl;
//6.accept()
client_sock = accept(server_sock, (struct sockaddr*)&client_addr, &client_addr_size);
if (client_sock == -1) {
std::cout << "client_sock accept failed!\n" << std::endl;
close(server_sock);
return;
}
std::cout << "accept is ok" << std::endl;
//7.send message
ssize_t ret_write = write(client_sock,r_to_c_message,strlen(r_to_c_message));
if (ret_write != (ssize_t)strlen(r_to_c_message)) {
std::cout << "ret_write failed!\n" << std::endl;
close(server_sock);
return;
}
//close(client_sock); 可以不执行
close(server_sock); //服务器关闭的时候,客户端会自动关闭
}
void tcp_client() {
int client_socket = socket(PF_INET, SOCK_STREAM, 0);
struct sockaddr_in servaddr;
memset(&servaddr, 0, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");
servaddr.sin_port = htons(2345);
int ret_connect = connect(client_socket, (struct sockaddr*)&servaddr, sizeof(servaddr));
if (ret_connect == 0) {
char buffer[256]{ "" };
read(client_socket, buffer, sizeof(buffer));
std::cout << "client: " << buffer << std::endl;
close(client_socket);
}
std::cout << "client done .. " << std::endl;
}
void runTCP() {
pid_t pid = fork();
if (pid == 0) { //开启子进程成功
sleep(1); //先等待服务器启动成功
tcp_client();
}
else if(pid > 0){//主进程
//调用服务器启动
tcp_server();
std::cout << "server done" << std::endl;
int status = 0;
wait(&status); //避免出现僵尸进程
}
else {
std::cout << "fork failed!"<<std::endl;
}
}
int main(int argc, char* argv[]) {
runTCP();
return 0;
}
void error_handling(char* message) {
printf("错误处理\n");
}
回声服务器
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<sys/wait.h>
#include<iostream>
void error_handling(char* message);
void tcp_server() {
//1.创建两个套接字变量
int server_sock{};
int client_sock{};
//2.两个绑定的地址
typedef struct sockaddr_in sockAddr_in;
sockAddr_in server_addr{};
sockAddr_in client_addr{};
//创建客户端地址的长度
socklen_t client_addr_size{};
//3.创建服务器套接字
//tcp 协议 面向 (链接/流) 的传输协议
server_sock = socket(PF_INET, SOCK_STREAM, 0);
//若 创建失败
if (server_sock == -1) {
std::cout << "creat socket failed !" << std::endl;
}
//4.绑定地址
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = inet_addr("127.0.0.1");//要监听的地址(设置为全网络接口监听)
server_addr.sin_port = htons(2345);
int ret_bind = bind(server_sock, (struct sockaddr*)&server_addr, sizeof(server_addr));
if (ret_bind == -1) {
std::cout << "bind failed!\n" << std::endl;
close(server_sock);
return;
}
//5.listen()
int ret_listen = listen(server_sock, 3);
if (ret_listen == -1) {
std::cout << "listen failed!\n" << std::endl;
close(server_sock);
return;
}
//std::cout << "listen is over!" << std::endl;
//std::cout << "accept is ok" << std::endl;
//7.send message
char buffer[1024];
for (int i = 0; i < 2; i++) {
//6.accept()
client_sock = accept(server_sock, (struct sockaddr*)&client_addr, &client_addr_size);
if (client_sock == -1) {
std::cout << "client_sock accept failed!\n" << std::endl;
close(server_sock);
return;
}
memset(buffer, 0, sizeof(buffer));
//ssize_t len = read(client_sock, buffer, sizeof(buffer));
ssize_t len = 0;
while ((len = read(client_sock, buffer, sizeof(buffer))) > 0) {
len = write(client_sock, buffer, len);
if (len != (ssize_t)strlen(buffer)) {
std::cout << "ret_write failed!\n" << std::endl;
close(server_sock);
return;
}
memset(buffer, 0, sizeof(buffer));
}
if (len <= 0) { //说明空字符串
std::cout << "read failed!" << std::endl;
close(server_sock);
return;
}
close(client_sock);
}
//close(client_sock); 可以不执行
close(server_sock); //服务器关闭的时候,客户端会自动关闭
}
void tcp_client() {
int client_socket = socket(PF_INET, SOCK_STREAM, 0);
struct sockaddr_in servaddr;
memset(&servaddr, 0, sizeof(servaddr));
servaddr.sin_family = AF_INET; //设置Net
servaddr.sin_addr.s_addr = inet_addr("127.0.0.1"); //要发送服务器的ip地址
servaddr.sin_port = htons(2345);
int ret_connect = connect(client_socket, (struct sockaddr*)&servaddr, sizeof(servaddr));
while(ret_connect == 0) {
char buffer[256]{ "" };
fputs("send message\"(Q/q quit)\"):",stdout);
fgets(buffer,sizeof(buffer),stdin);
if ((strcmp(buffer, "Q\n") == 0) || (strcmp(buffer, "q\n") == 0)) {
break;
}
size_t len = strlen(buffer);
size_t send_len{ 0 };
while (send_len < len) {
ssize_t ret = write(client_socket, buffer+send_len, len - send_len); //向服务器分包发送数据
if (ret <= 0) {
fputs("write failed!\n",stdout);
close(client_socket);
std::cout << "client done!"<<std::endl;
break;
}
send_len += (size_t)ret;
}
memset(buffer,0,sizeof(buffer));
size_t read_len = 0;
while (read_len < len) {
ssize_t ret = read(client_socket, buffer+read_len, len-read_len); //接收数据
if (ret <= 0) {
fputs("read failed!\n", stdout);
close(client_socket);
std::cout << "client done!" << std::endl;
break;
}
read_len += (size_t)ret;
}
std::cout << "from server: " << buffer << std::endl;
}
//printf("%s(%d):%s %d\n",__FILE__,__LINE__,__FUNCTION__, ret_connect);
close(client_socket);
std::cout << "client done!" << std::endl;
}
void runTCP() {
pid_t pid = fork();
if (pid == 0) { //开启子进程成功
tcp_server();
}
else if (pid > 0) {//主进程
//调用客户端启动
for(int i = 0;i < 2;++ i)
tcp_client();
int status = 0;
wait(&status); //避免出现僵尸进程
}
else {
std::cout << "fork failed!" << std::endl;
}
}
int main(int argc, char* argv[]) {
runTCP();
return 0;
}
void error_handling(char* message) {
printf("错误处理\n");
}
UDP回声服务器实现
oid UDP_server(int argc,char* argv[]) {
int server_scoket{ -1 };
char message[512]{""};
struct sockaddr_in server_addr,client_addr;
socklen_t clientLen{ 0 };
if (argc != 2) {
printf("usage:% <port>\n",argv[0]);
error_handling("argument is error:");
}
server_scoket = socket(PF_INET,SOCK_DGRAM,0);//UDP
if (server_scoket == -1) {
error_handling("create socket failed");
}
memset(&server_addr,0,sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = inet_addr("0.0.0.0");
server_addr.sin_port = htons((short)atoi(argv[1]));
if (bind(server_scoket, (struct sockaddr*)&server_addr, sizeof(server_addr)) == -1) {
error_handling("bind failed");
}
//printf("服务器绑定成功!\n");
int _nums = 10;
while (_nums--) {
//printf("server开始接收数据\n");
clientLen = sizeof(client_addr);
ssize_t len = recvfrom(server_scoket,message,sizeof(message),0,
(struct sockaddr*)&client_addr, &clientLen);
printf("server开始接收成功:%s\n",message);
if (sendto(server_scoket, message, strlen(message), 0, (struct sockaddr*)&client_addr, clientLen) == -1) {
printf("server发送失败!\n");
}
else {
printf("server发送成功\n");
}
memset(message,0,len);
}
close(server_scoket);
}
int UDP_client(int argc,char* argv[]) {
int client_socket{};
struct sockaddr_in server_addr;
socklen_t server_len{0};
char message[512]{""};
if (argc != 3) {
printf("usage:%s is port\n",argv[0]);
error_handling("argument error!");
}
client_socket = socket(PF_INET,SOCK_DGRAM,0);
if (client_socket == -1) {
error_handling("socket falied");
}
memset(&server_addr,0,sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = inet_addr(argv[1]);
server_addr.sin_port = htons((short)atoi(argv[2]));
while (1) {
printf("input message(q/Q):");
fgets(message,sizeof(message),stdin);
if (strcmp(message, "q\n") == 0 || strcmp(message, "Q\n") == 0
|| strcmp(message, "q") == 0 || strcmp(message, "Q") == 0) {
printf("退出成功!\n");
break;
}
ssize_t len = sendto(client_socket,message,strlen(message),0,(struct sockaddr*)&server_addr,sizeof(server_addr));
if (len == -1) {
std::cout << "sendto failed"<<std::endl;
return -1;
}
memset(message,0,sizeof(message));
recvfrom(client_socket,message,sizeof(message),0,(struct sockaddr*)&server_addr, &server_len);
printf("recv:%s\n",message);
}
close(client_socket);
return 0;
}
void runUDP(char* argv0) {
if (fork() > 0) {
int argc = 3;
char* argv[] = { argv0,(char*)"127.0.0.1",(char*)"2233" };
UDP_client(argc, argv);
int status = 0;
wait(&status);
}
else {
int argc = 2;
char* argv[] = { argv0,(char*)"2233" };
UDP_server(argc, argv);
}
}
int main(int argc, char* argv[]) {
//runTCP();
runUDP(argv[0]);
return 0;
}
void error_handling(const char* message) {
fputs(message,stderr);
fputc('\n',stderr);
exit(1);
}