:!man socket
#include
#include
int socket(int domain, int type, int protocol);
domain | 说明 |
---|---|
AF_INET | IPV4协议 |
AF_INET6 | IPV6协议 |
AF_LOCAL | Unix域协议 |
type | 说明 |
---|---|
SOCK_STREAM | 字节流套接字(TCP/SCTP) |
SOCK_DGRAM | 数据报套接字(UDP) |
SOCK_RAM | 原始套接字 (IPv4/IPv6) |
SOCK_SEQPACKET | 有序分组套接字(SCTP) |
protocol | 说明 |
---|---|
IPPROTO_CP | TCP传输协议 |
IPPROTO_UDP | UDP传输协议 |
IPPROTO_SCTP | SCTP传输协议 |
socket函数在成功时会返回一个小的非负整数值,它与文件描述符类似,就称它为套接字描述符。只需要指定协议族(IPV4、IPV6或Unix)和套接字类型即可
socket_fd=socket(AF_INET,SOCK_STREAM,0);
int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
sockfd
该参数为成功创建socket返回的套接字描述符
addr
该参数为通用的套接字地址结构,套接字地址结构总是以引用形式来传递
IPv4套接字地址结构
IPv4套接字地址结构通常也称为“网际套接字地址结构”,它以sockaddr_in命名,定义在
struct sockaddr_in {
short sin_family; // 地址家族,一般为AF_INET
unsigned short sin_port; // 端口号
struct in_addr sin_addr; // IPv4地址
char sin_zero[8]; // 填充字节,通常为0
};
sockaddr_in结构体定义了套接字的IPv4地址信息。它具有以下字段:
sin_family:指定地址家族,通常为AF_INET表示IPv4地址族。
sin_port:表示端口号,使用无符号短整数(16位)表示,需要以网络字节顺序(大端序)进行表示。
sin_addr:struct in_addr类型的结构体,用于表示IPv4地址。
sin_zero:用于填充字段,通常为0,以确保结构体的大小与sockaddr一致。
struct in_addr {
unsigned long s_addr; // IPv4地址,以网络字节顺序(大端序)表示
};
#define PORT 8596
struct sockaddr_in local_addr;
//set socket address
local_addr.sin_family=AF_INET;
local_addr.sin_port=htons(PORT);
local_addr.sin_addr.s_addr=INADDR_ANY;
bzero(&(local_addr.sin_zero),8);
htons:
头文件
#include
原型
uint32_t htonl(uint32_t hostlong);
uint16_t htons(uint16_t hostshort);
uint32_t ntohl(uint32_t netlong);
uint16_t ntohs(uint16_t netshort);
在这些函数的名字当中,h代表host,n代表network,s代表short,l代表long。
#define PORT 8596
struct sockaddr_in local_addr;
//set socket address
local_addr.sin_family=AF_INET;
local_addr.sin_port=htons(PORT);
local_addr.sin_addr.s_addr=INADDR_ANY;
bzero(&(local_addr.sin_zero),8);
//bind socket
ret=bind(socket_fd, (struct sockaddr *)&local_addr,sizeof(struct sockaddr_in));
if(ret == -1){
perror("bind socket error");
exit(1);
}
int listen(int sockfd, int backlog);
ret=listen(socket_fd, backlog);
if(ret ==-1){
perror("listen error");
exit(1);
}
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
accept 是一个在网络编程中使用的函数,用于接受客户端的连接请求并创建与客户端连接的套接字。
accept 函数通常在服务器端使用,它从监听套接字(listening socket)的连接队列中取出一个连接请求,并创建一个新的套接字来与客户端进行通信。这个新创建的套接字可以用于与客户端进行数据交换。
入参:
返回值:
struct sockaddr_in remote_addr;
int accept_fd=-1;
socklen_t addrlen = sizeof(remote_addr);
accept_fd=accept(socket_fd,( struct sockaddr *)&remote_addr, &addrlen);
ssize_t recv(int sockfd, void *buf, size_t len, int flags);
recv 是一个在网络编程中使用的函数,用于接收数据从已连接的套接字或接收数据报套接字中。
需要注意的是,recv 函数是一个阻塞调用,即在没有数据到达之前,它会一直等待。如果需要非阻塞的操作,可以使用非阻塞 I/O 或多线程/多进程的方式处理接收操作。
入参:
返回值:
#define MESSAGE_SIZE 1024
int accept_fd=-1;
char in_buf[MESSAGE_SIZE]={0,};
memset(in_buf,0,MESSAGE_SIZE);
//read data
ret =recv(accept_fd, (void*)in_buf, MESSAGE_SIZE, 0);
if(ret ==0){
break;
}
printf("receive data:%s\n",in_buf);
ssize_t send(int sockfd, const void *buf, size_t len, int flags);
send 是一个在网络编程中使用的函数,用于发送数据到已连接的套接字或发送数据报套接字中。
入参:
返回值
#define MESSAGE_SIZE 1024
int accept_fd=-1;
char in_buf[MESSAGE_SIZE]={0,};
send(accept_fd, (void *)in_buf, MESSAGE_SIZE, 0);
#include
//socket
#include
#include
//close
#include
//exit
#include
//perror
#include
//memset
#include
//htons
#include
#define PORT 8596
#define MESSAGE_SIZE 1024
int main(){
int ret=-1;
int socket_fd=-1;
int accept_fd=-1;
int backlog=10;
int flag=1;
char in_buf[MESSAGE_SIZE]={0,};
struct sockaddr_in local_addr,remote_addr;
//create socket
socket_fd=socket(AF_INET,SOCK_STREAM,0);
if(socket_fd == -1){
perror("create socket error");
exit(1);
}
//set option of socket
ret = setsockopt(socket_fd, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof(flag));
if ( ret == -1 ){
perror("setsockopt error");
}
//set socket address
local_addr.sin_family=AF_INET;
local_addr.sin_port=htons(PORT);
local_addr.sin_addr.s_addr=INADDR_ANY;
bzero(&(local_addr.sin_zero),8);
//bind socket
ret=bind(socket_fd, (struct sockaddr *)&local_addr,sizeof(struct sockaddr_in));
if(ret == -1){
perror("bind socket error");
exit(1);
}
ret=listen(socket_fd, backlog);
if(ret ==-1){
perror("listen error");
exit(1);
}
//loop to accept client
for(;;){
socklen_t addrlen = sizeof(remote_addr);
accept_fd=accept(socket_fd,( struct sockaddr *)&remote_addr, &addrlen);
for(;;){
memset(in_buf,0,MESSAGE_SIZE);
//read data
ret =recv(accept_fd, (void*)in_buf, MESSAGE_SIZE, 0);
if(ret ==0){
break;
}
printf("receive data:%s\n",in_buf);
send(accept_fd, (void *)in_buf, MESSAGE_SIZE, 0);
}
printf("close client connection......");
close(accept_fd);
}
printf("quit server....");
close(socket_fd);
return 0;
}
gcc baseServer.cpp -o server
gcc 默认不会自动链接 C++ 标准库,因此需要手动添加链接选项 -lstdc++ 来告知编译器链接 C++ 标准库。
gcc baseServer.cpp -o server -lstdc++
或者直接使用g++编译。
gcc 和 g++ 在功能上有一些区别,主要体现在它们的默认行为和对源代码的处理方式上。
1、默认语言:gcc 默认将源代码视为 C 语言源代码进行处理,而 g++ 默认将源代码视为 C++ 语言源代码进行处理。这意味着如果您使用 gcc 编译 C++ 代码,可能会导致一些 C++ 特定的功能和语法无法正常工作。
2、链接库:g++ 在链接时会自动包含 C++ 标准库(例如 libstdc++),而 gcc 则不会。这是因为 C++ 标准库包含了许多与 C++ 相关的功能和数据结构,而 C 标准库(例如 libc)则只包含了与 C 相关的功能。
3、标准支持:g++ 支持更多的 C++ 标准,例如 C++11、C++14、C++17 等。而 gcc 的 C 标准支持(例如 C89、C99)更全面,但对于最新的 C++ 标准可能支持较少。
4、编译选项:gcc 和 g++ 有一些针对特定语言的编译选项。例如,g++ 具有额外的选项来支持 C++ 异常处理、RTTI(运行时类型信息)等特性。
虽然 gcc 和 g++ 在默认行为和一些功能上有所区别,但它们都是 GNU 编译器套件的一部分,共享许多相同的代码和基础设施。实际上,g++ 实际上是一个对 gcc 进行了配置和扩展的别名。
在大多数情况下,如果您要编译 C 代码,可以使用 gcc。如果您要编译 C++ 代码,可以使用 g++,这样可以确保默认行为和所需的语言支持正确。 然而,您也可以使用 gcc 编译 C++ 代码,或者使用 g++ 编译 C 代码,只需根据需要适当调整编译选项和参数。