1. 接口
1.1 转换操作
转换操作主要分为三类:字节序转换操作、IP地址转换操作和主机名转换操作。
1.1.1 字节序转换操作
No. | 函数 | 含义 | 作用 |
---|
1 | ntohs() | network to host short | 把unsigned short类型从网络序转换到主机序 |
2 | ntohl() | network to host long | 把unsigned long类型从网络序转换到主机序 |
No. | 函数 | 含义 | 作用 |
---|
1 | htons() | host to network short | 把unsigned short类型从主机序转换到网络序 |
2 | htonl() host to network long | 把unsigned long类型从主机序转换到网络序 | |
- 添加头文件
#include <arpa/inet.h>
-
- 主机序的小端转换为网络序的大端
#include <iostream>
#include <arpa/inet.h>
using namespace std;
int main(){
short s = 0x1234;
cout << hex << s << endl;
cout << hex << htons(s) << endl;
cout << sizeof(long) << endl;
int l = 0x123456;
cout << hex << l << endl;
cout << hex << htonl(l) << endl;
}
1.1.2 IP地址转换操作
No. | 函数 | 功能 | 特点 |
---|
1 | int inet_aton(const char string, struct in_addraddr) | 点分十进制数串转网络字节序长整型 | IPv4专用 |
2 | in_addr_t inet_addr(const char* string) | 点分十进制数串转网络字节序长整型 | IPv4专用 |
3 | char* inet_ntoa(struct in_addr addr) | 网络字节序长整型转点分十进制数串 | IPv4专用 |
4 | int inet_pton(int af, const char *src, void *dst) | 点分十进制数串转网络字节序长整型 | IPv4/IPv6通用(推荐) |
5 | const char *inet_ntop(int af, const void *src, char *dst, socklen_t cnt) | 网络字节序长整型转点分十进制数串 | IPv4/IPv6通用(推荐) |
man inet_aton
结构体
No. | 结构体 | 功能 | 特性 |
---|
1 | struct sockaddr | 套接字地址结构 | IPv4/IPv6通用 |
2 | struct sockaddr_in | IPv4套接字地址结构 | IPv4专用 |
3 | struct in_addr | IPv4地址结构 | IPv4专用 |
4 | in_addr_t | IPv4地址类型 | IPv4专用 |
5 | struct sockaddr_in6 | IPv6套接字地址结构 | IPv6专用 |
struct sockaddr {
unsigned short sa_family;
char sa_data[14];
};
struct sockaddr_in{
short sin_family;
unsigned short sin_port;
struct in_addr sin_addr;
unsigned char sin_zero[8];
};
sin_zero[8]用来保证结构体struct sockaddr_in的大小和结构体struct sockaddr的大小相等。
struct in_addr {
in_addr_t s_addr;
};
typedef unsigned int in_addr_t;
struct sockaddr_in6{
uint8_t sin6_len;
sa_family_t sin6_family;
in_port_t sin6_port;
uint32_t sin6_flowinfo;
struct in6_addr sin6_addr;
}
#include <iostream>
#include <arpa/inet.h>
using namespace std;
int main(){
struct in_addr addr;
if(0 == inet_aton("192.168.0.1",&addr)){
cout << "inet_aton error" << endl;
}else{
cout << hex << addr.s_addr << endl;
cout << hex << 192 << endl;
cout << hex << 168 << endl;
}
{
struct in_addr addr;
if(0 == inet_pton(AF_INET,"192.168.0.1",&addr)){
cout << "inet_aton error" << endl;
}else{
cout << hex << addr.s_addr << endl;
}
}
in_addr_t addr2 = inet_addr("192.168.0.1");
if(INADDR_NONE == addr2){
cout << "invalid ip" << endl;
}else{
cout << hex << addr2 << endl;
}
cout << inet_ntoa(addr) << endl;
{
char ip[16];
inet_ntop(AF_INET,&addr,ip,sizeof(ip));
cout << ip << endl;
}
}
1.1.2.1 IPv4专用
点分十进制数串转网络字节序长整型
①推荐方式
int inet_aton(const char *string, struct in_addr*addr)
No. | 参数 | 含义 |
---|
1 | string | 点分十进制IP地址字符串 |
2 | addr | 网络字节序长整型IP地址 |
#include <stdio.h>
#include <stdlib.h>
#include <arpa/inet.h>
int main(int argc,char* argv[]){
if(2 != argc){
printf("usage:%s <ip>\n",argv[0]);
return 1;
}
struct in_addr addr;
int res = inet_aton(argv[1],&addr);
if(0 == res){
perror("inet_aton error");
return 1;
}
printf("res:%d\n%s = 0x%08x",res,argv[1],addr);
return 0;
}
①旧方式
in_addr_t inet_addr(const char* string)
No. | 参数 | 含义 |
---|
1 | string | 点分十进制IP地址字符串 |
No. | 返回值 | 含义 |
---|
1 | INADDR_NONE | 失败 |
2 | 非INADDR_NONE | 网络字节序长整型IP地址 |
#include <stdio.h>
#include <stdlib.h>
#include <arpa/inet.h>
int main(int argc,char* argv[]){
if(2 != argc){
printf("usage:%s <ip>\n",argv[0]);
return 1;
}
in_addr_t addr = inet_addr(argv[1]);
if(INADDR_NONE == addr){
perror("inet_addr error");
return 1;
}
printf("%s = 0x%08x",argv[1],addr);
return 0;
}
网络字节序长整型转点分十进制数串
char* inet_ntoa(struct in_addr addr)
No. | 返回值 | 含义 |
---|
1 | 非NULL | 点分十进制IP地址字符串 |
2 | NULL | 失败 |
#include <stdio.h>
#include <stdlib.h>
#include <arpa/inet.h>
int main(int argc,char* argv[]){
if(2 != argc){
printf("usage:%s <num>\n",argv[0]);
return 1;
}
struct in_addr addr;
addr.s_addr = strtol(argv[1],NULL,16);
char* ip = inet_ntoa(addr);
if(NULL == ip){
perror("inet_ntoa error");
return 1;
}
printf("0x%x = %s",addr.s_addr,ip);
return 0;
}
1.1.2.2 IPv4/IPv6通用(推荐)
点分十进制数串转网络字节序长整型
int inet_pton(int af, const char *src, void *dst)
No. | 参数 | 含义 |
---|
1 | af | 地址族。AF_INET/AF_INET6 |
2 | src | 点分十进制IP地址字符串 |
3 | dst | 网络字节序长整型IP地址 |
No. | 返回值 | 含义 |
---|
1 | <0 | 失败 |
2 | 0 | af和src格式不对 |
#include <stdio.h>
#include <string.h>
#include <arpa/inet.h>
int main(int argc,char* argv[]){
char ip[INET_ADDRSTRLEN];
if(argc == 1){
scanf("%s",ip);
}else if(2 != argc){
printf("usage:%s <ip>\n",argv[0]);
return 1;
}
struct in_addr addr;
memset(&addr,0,sizeof(addr));
if(1 != inet_pton(AF_INET,(argc == 1?ip:argv[1]),&addr)){
perror("inet_pton err");
return 1;
}
printf("%x\n",addr.s_addr);
}
网络字节序长整型转点分十进制数串
const char *inet_ntop(int af, const void *src, char *dst, socklen_t cnt)
No. | 参数 | 含义 |
---|
1 | af | 地址族。AF_INET/AF_INET6 |
2 | src | 网络字节序长整型IP地址 |
3 | dst | 点分十进制IP地址字符串 |
4 | cnt | 缓存区dst的大小 |
No. | 返回值 | 含义 |
---|
1 | NULL | 失败 |
2 | 非NULL | dst指针 |
#include <stdio.h>
#include <string.h>
#include <arpa/inet.h>
int main(int argc,char* argv[]){
if(2 != argc){
printf("usage:%s <dec num>\n",argv[0]);
return 1;
}
struct in_addr addr;
memset(&addr,0,sizeof(addr));
addr.s_addr = strtol(argv[1],NULL,16);
char ip[INET_ADDRSTRLEN];
if(NULL == inet_ntop(AF_INET,&addr,ip,sizeof(ip))){
perror("inet_ntop err");
return 1;
}
printf("%s\n",ip);
}
1.1.3 主机名转换操作
No. | 函数 | 功能 |
---|
1 | struct hostent *gethostbyname(const char *hostname) | 主机名转地址 |
2 | struct hostent *gethostbyaddr(const char * addr, int len, int type) | 地址转主机名 |
3 | struct hostent *gethostbyaddr(const char * addr, int len, int type) | 地址转主机名 |
1.1.3.1 主机名字和地址信息struct hostent
No. | 参数 | 含义 |
---|
1 | h_name | 主机名字 |
2 | h_aliases | 以空指针结尾的主机别名队列 |
3 | h_addrtype | 地址类型。AF_INET/AF_INET6 |
4 | h_length | 地址长度。在AF_INET类型地址中为4 |
5 | h_addr | 第一个IP地址 |
6 | h_addr_list | 以空指针结尾的IP地址的列表 |
1.1.3.2 主机名转地址
struct hostent *gethostbyname(const char *hostname)
No. | 返回值 | 含义 |
---|
1 | NULL | 出错 |
2 | 非NULL | hostent结构指针 |
#include <stdio.h>
#include <netdb.h>
#include <arpa/inet.h>
int main(int argc,char** argv){
struct hostent* host = gethostbyname(argv[1]);
if(NULL == host){
herror("gethostbyname err");
return 1;
}
printf("hostname:%s\n",host->h_name);
printf("aliases:");
while(*host->h_aliases != NULL){
printf("%s ",*host->h_aliases);
host->h_aliases++;
}
printf("\n");
printf("addrtype:%s\n",host->h_addrtype == AF_INET?"AF_INET":"AF_INET6");
printf("length:%d\n",host->h_length);
printf("addrlist:");
while(*host->h_addr_list != NULL){
printf("%s ",inet_ntoa(*(struct in_addr*)*host->h_addr_list));
host->h_addr_list++;
}
}
1.1.3.3 地址转主机名
struct hostent *gethostbyaddr(const char * addr, int len, int type)
No. | 参数 | 含义 |
---|
1 | addr | 网络字节顺序地址 |
2 | len | 地址的长度。在AF_INET类型地址中为4 |
3 | type | 地址类型。AF_INET/AF_INET6 |
No. | 返回值 | 含义 |
---|
1 | NULL | 出错 |
2 | 非NULL | hostent结构指针 |
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <netdb.h>
#include <arpa/inet.h>
int main(int argc,char* argv[]){
int c,af=AF_INET;
while((c = getopt(argc,argv,"6")) != -1){
switch(c){
case '6':
af = AF_INET6;
break;
}
}
if(optind != argc-1){
printf("usage:%s [-6] <ip>\n",argv[0]);
return 1;
}
struct in_addr addr;
struct in6_addr addr6;
void* dst = af == AF_INET ? (void*)&addr : (void*)&addr6;
if(0 >= inet_pton(af,argv[1],dst)){
perror("inet_pton error");
return 1;
}
socklen_t len = AF_INET ? sizeof(struct in_addr) : sizeof(struct in6_addr) ;
struct hostent * phost = gethostbyaddr(dst,len,af);
if(NULL == phost){
perror("gethostbyname error");
return 1;
}
char ip[20];
switch( phost->h_addrtype ){
case AF_INET:
case AF_INET6:
if(NULL == inet_ntop(phost->h_addrtype,(void*)phost->h_addr,ip,20)){
perror("inet_ntop error");
return 1;
}
printf("host:%s\n0x%x = %s",argv[1],phost->h_addr,ip);
break;
}
}
2. socket操作
2.1 接口
2.1.1 创建
int socket(int domain, int type, int protocol)
No. | 参数 | 含义 |
---|
1 | domain | 协议域 |
2 | type | 类型 |
3 | protocol | 协议 |
No. | 返回值 | 含义 |
---|
1 | -1 | 失败 |
2 | >0 | socket描述符 |
2.1.2 关闭
int close(int sockfd)
int shutdown(int sockfd,int howto)
No. | 参数 | 含义 |
---|
1 | sockfd | socket套接字 |
2 | howto | 关闭方式 |
No. | 方式 | 值 | 含义 |
---|
1 | SHUT_RD | 0 | 关闭连接的读 |
2 | SHUT_WR | 1 | 关闭连接的写 |
3 | SHUT_RDWR | 2 | 连接的读和写都关闭 |
2.1.3 属性
设置
int setsockopt(int sockfd, int level, int optname,const void *optval, socklen_t optlen)
No. | 参数 | 含义 |
---|
1 | sockfd | 套接字描述符 |
2 | level | 选项层次 |
3 | optname | 选项 |
4 | optval | 选项值指针 |
5 | optlen | optval缓冲区长度 |
No. | 参数 | 含义 |
---|
1 | SOL_SOCKET | 通用套接字选项 |
2 | IPPROTO_TCP | TCP选项 |
3 | IPPROTO_IP | IP选项 |
4 | IPPROTO_IPV6 | IPv6选项 |
选项分为SOL_SOCKET级别和IPPROTO_IP级别两个级别
No. | 参数 | 含义 |
---|
1 | SO_REUSEADDR | 让端口释放后立即就可以被再次使用。一个端口释放后会等待两分钟之后才能再被使用。 |
2 | SO_RCVBUF | 接收确定缓冲区大小 |
3 | SO_SNDBUF | 发送缓冲区大小 |
4 | SO_SNDTIMEO | 发送时限 |
5 | SO_RCVTIMEO | 接收时限 |
6 | SO_BROADCAST | 广播 |
7 | SO_DONTLINGER | 关闭端口不进入TIME_WAIT状态 |
8 | SO_LINGER | 关闭端口进入TIME_WAIT状态的属性 |
No. | 参数 | 含义 |
---|
1 | IP_ADD_MEMBERSHIP | 加入指定的组播组。 |
2 | IP_DROP_MEMBERSHIP | 离开指定的组播组。 |
3 | IP_MULTICAST_IF | 指定发送组播数据的IP地址。 |
4 | IP_MULTICAST_LOOP | 发送组播数据的主机是否作为接收组播数据的组播成员。 |
int getsockopt(int sockfd, int level, int optname,void *optval, socklen_t *optlen)
No. | 参数 | 含义 |
---|
1 | sockfd | 套接字描述符 |
2 | level | 选项层次 |
3 | optname | 选项 |
4 | optval | 选项值指针 |
5 | optlen | optval缓冲区长度 |
2.1.4 绑定
int bind(int socket, const struct sockaddr* address, socklen_t address_len)
No. | 参数 | 含义 |
---|
1 | socket | 套接字描述符 |
2 | address | 地址和端口号 |
3 | address_len | address缓冲区的长度 |
No. | 返回值 | 含义 |
---|
1 | 0 | 成功 |
2 | SOCKET_ERROR | 失败 |
2.1.5 监听
int listen(int sockfd, int backlog)
No. | 参数 | 含义 |
---|
1 | sockfd | 监听的socket描述符 |
2 | backlog | 排队的最大连接个数 |
2.1.6 连接
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
No. | 参数 | 含义 |
---|
1 | sockfd | 客户端的socket描述字 |
2 | addr | 服务器的socket地址 |
3 | addrlen | 服务器的socket地址的长度 |
2.1.7 接受
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen)
No. | 参数 | 含义 |
---|
1 | sockfd | 服务器的socket描述符,监听socket描述符 |
2 | addr | 客户端的socket地址 |
3 | addrlen | 客户端的socket地址的长度 |
如果不需要获取客户端的套接字地址,后两个参数可设置为NULL。
int connfd = accept(listenfd,NULL,NULL);
如果需要获取,则按照如下方式设置。
struct sockaddr_in remote_addr;
bzero(&remote_addr,sizeof(remote_addr));
socklen_t remote_addr_len = sizeof(remote_addr);
int connfd = accept(listenfd,(struct sockaddr*)&remote_addr,&remote_addr_len);
2.1.8 发送
ssize_t write(int fd, const void *buf, size_t len);
No. | 参数 | 含义 |
---|
1 | fd | 文件描述符 |
2 | buf | 写入数据 |
3 | len | 写入数据的长度 |
ssize_t send(int sockfd, const void *buf, size_t len, int flags);
No. | 参数 | 含义 |
---|
1 | sockfd | sockfd文件描述符 |
2 | buf | 写入数据 |
3 | len | 写入数据的长度 |
4 | flags | 通常为0 |
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,const struct sockaddr *dest_addr, socklen_t addrlen);
No. | 参数 | 含义 |
---|
1 | sockfd | sockfd文件描述符 |
2 | buf | 写入数据 |
3 | len | 写入数据的长度 |
4 | flags | 通常为0 |
5 | dest_addr | 目标socket地址 |
6 | addrlen | 目标socket地址长度 |
2.1.9 接收
ssize_t read(int fd, void *buf, size_t len);
No. | 参数 | 含义 |
---|
1 | fd | 文件描述符 |
2 | buf | 读取数据 |
3 | len | 读取数据的长度 |
No. | 返回值 | 含义 |
---|
1 | 0 | 读到文件的结束 |
2 | >0 | 实际所读的字节数 |
3 | <0 | 出错 |
ssize_t recv(int sockfd, void *buf, size_t len, int flags);
No. | 参数 | 含义 |
---|
1 | fd | 文件描述符 |
2 | buf | 读取数据 |
3 | len | 读取数据的长度 |
4 | flags | 通常为0 |
No. | 返回值 | 含义 |
---|
1 | 0 | 读到文件的结束 |
2 | >0 | 实际所读的字节数 |
3 | <0 | 出错 |
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,struct sockaddr *src_addr, socklen_t *addrlen)
No. | 参数 | 含义 |
---|
1 | fd | 文件描述符 |
2 | buf | 读取数据 |
3 | len | 读取数据的长度 |
4 | flags | 通常为0 |
5 | dest_addr | 目标socket地址 |
6 | addrlen | 目标socket地址长度 |
No.| 返回值| 含义
1 |0 |读到文件的结束
2 |>0| 实际所读的字节数
3 |<0 |出错
3. 实践
3.1 特殊地址设置
No. | 特殊地址 | ipv4 | ipv6 |
---|
1 | 通配地址 | in_addr.sin_addr.s_addr = htonl(INADDR_ANY) | in6_addr.sin6_addr=in6addr_any |
2 | 回环地址 | in_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK) | in6_addr.sin6_addr=in6addr_loopback |
3 | 广播地址 | in_addr.sin_addr.s_addr = htonl(INADDR_BROADCAST) | 无广播 |
3.2 原则
- 客户端/发送端
必须指定连接/发送的IP(广播地址、回环地址或者某个具体地址)。
必须指定连接/发送的port。 - 服务器/接受端
IP指定为通配地址、回环地址或者某个具体地址。
必须指定绑定监听/接受的port。
netstat 查看网络连接状态、socket端口打开状态
No. | 选项 | 作用 |
---|
1 | -antp | 查看tcp的状态 |
2 | -anup | 查看udp的状态 |
3.3 TCP基本流程
No. | C/S | 函数 |
---|
1 | Server | socket()、bind()、listen()、accept()、recv()/read()、send()/write() |
2 | Client | socket()、connect()、send()/write()、recv()/read() |
案例1:一个简单的TCP的客户端和服务器
- 创建连接套接字
int connfd = socket(AF_INET,SOCK_STREAM,0);
if(-1 == connfd){
cout << "socket error" << endl;
return 1;
}
- 连接服务器
struct sockaddr_in remote_addr;
remote_addr.sin_family = AF_INET;
remote_addr.sin_addr.s_addr = inet_addr(argv[1]);
remote_addr.sin_port = htons(atoi(argv[2]));
if(-1==connect(connfd,(struct sockaddr*)&remote_addr,sizeof(remote_addr))){
cout << "connect error" << endl;
return 1;
}else{
cout << "connect success" << endl;
}
- 发送信息
string message;
getline(cin,message);
write(connfd,message.c_str(),message.size()+1);
- 接收数据
char buffer[1024] = {0};
read(connfd,buffer,sizeof(buffer));
cout << buffer << endl;
- 关闭套接字
close(connfd);
#include <iostream>
#include <arpa/inet.h>
#include <unistd.h>
using namespace std;
int main(int argc,char* argv[]){
if(3!=argc){
cout << "Usage:" << argv[0] << " IP port" << endl;
return 1;
}
int connfd = socket(AF_INET,SOCK_STREAM,0);
if(-1 == connfd){
cout << "socket error" << endl;
return 1;
}
struct sockaddr_in remote_addr;
remote_addr.sin_family = AF_INET;
remote_addr.sin_addr.s_addr = inet_addr(argv[1]);
remote_addr.sin_port = htons(atoi(argv[2]));
if(-1==connect(connfd,(struct sockaddr*)&remote_addr,sizeof(remote_addr))){
cout << "connect error" << endl;
return 1;
}else{
cout << "connect success" << endl;
}
string message;
getline(cin,message);
write(connfd,message.c_str(),message.size()+1);
char buffer[1024] = {0};
read(connfd,buffer,sizeof(buffer));
cout << buffer << endl;
close(connfd);
return 0;
}
#include <iostream>
#include <arpa/inet.h>
#include <unistd.h>
#include <strings.h>
using namespace std;
int main(int argc,char* argv[]){
if(3!=argc){
cout << "Usage:" << argv[0] << " IP port" << endl;
return 1;
}
int listenfd = socket(AF_INET,SOCK_STREAM,0);
if(-1 == listenfd){
cout << "listen socket error" << endl;
return 1;
}
struct sockaddr_in local_addr;
local_addr.sin_family = AF_INET;
local_addr.sin_addr.s_addr = inet_addr(argv[1]);
local_addr.sin_port = htons(atoi(argv[2]));
if(-1 == bind(listenfd,(struct sockaddr*)&local_addr,sizeof(local_addr))){
cout << "bind error" << endl;
return 1;
}else{
cout << "bind success" << endl;
}
if(-1==listen(listenfd,10)){
cout << "listen error" << endl;
return 1;
}else{
cout << "listen success" << endl;
}
struct sockaddr_in remote_addr;
bzero(&remote_addr,sizeof(remote_addr));
socklen_t remote_addr_len = sizeof(remote_addr);
accept(listenfd,(struct sockaddr*)&remote_addr,&remote_addr_len);
if(-1 == connfd){
cout << "accept error" << endl;
return 1;
}else{
cout << "accept success" << endl;
cout << inet_ntoa(remote_addr.sin_addr) << ":" << ntohs(remote_addr.sin_port) << endl;
}
char buffer[1024] = {0};
read(connfd,buffer,sizeof(buffer));
cout << buffer << endl;
string message;
getline(cin,message);
write(connfd,message.c_str(),message.size()+1);
close(connfd);
close(listenfd);
}
- 说明
使用下面的代码,是为了避免出现Bind error: Address already in use
if(-1 == bind(listenfd,(struct sockaddr*)&local_addr,sizeof(local_addr)))
启用SO_REUSEADDR选项后,bind()函数将允许地址的立即重用。
-
Socket与三次握手
-
Socket与四次挥手
-
测试是否成功
在服务端(需要先打开服务端)
./server 127.0.0.1 8088
在客户端
./client 127.0.0.1 8088
- 效果展示
- 说明:关于IP连接问题
案例2:简单的TCP的客户端和服务器(接收数据和发送数据采用多线程方式)
#include <iostream>
#include <arpa/inet.h>
#include <unistd.h>
#include <strings.h>
using namespace std;
void show_connect(int fd) {
struct sockaddr_in local_addr;
socklen_t local_addr_len = sizeof(local_addr);
bzero(&local_addr,local_addr_len);
getsockname(fd,(struct sockaddr*)&local_addr,&local_addr_len);
cout << "local " << inet_ntoa(local_addr.sin_addr) << ":" << ntohs(local_addr.sin_port) << endl;
struct sockaddr_in remote_addr;
socklen_t remote_addr_len = sizeof(remote_addr);
bzero(&remote_addr,remote_addr_len);
getpeername(fd,(struct sockaddr*)&remote_addr,&remote_addr_len);
cout << "remote " << inet_ntoa(remote_addr.sin_addr) << ":" << ntohs(remote_addr.sin_port) << endl;
}
void* read_msg(void* fd) {
int connfd = *(int*)fd;
while(true) {
char buffer[1024] = {0};
int len = read(connfd,buffer,sizeof(buffer));
if(0==len) {
cout << "server exit" << endl;
break;
} else {
cout <<"server:"<< buffer << endl;
}
}
}
void* write_msg(void* fd) {
int connfd = *(int*)fd;
while(true) {
string message;
getline(cin,message);
write(connfd,message.c_str(),message.size()+1);
}
}
int main(int argc,char* argv[]) {
if(3!=argc) {
cout << "Usage:" << argv[0] << " IP port" << endl;
return 1;
}
int connfd = socket(AF_INET,SOCK_STREAM,0);
if(-1 == connfd) {
cout << "socket error" << endl;
return 1;
}
struct sockaddr_in remote_addr;
remote_addr.sin_family = AF_INET;
remote_addr.sin_addr.s_addr = inet_addr(argv[1]);
remote_addr.sin_port = htons(atoi(argv[2]));
if(-1==connect(connfd,(struct sockaddr*)&remote_addr,sizeof(remote_addr))) {
cout << "connect error" << endl;
return 1;
} else {
cout << "connect success" << endl;
show_connect(connfd);
}
pthread_t tids[2];
pthread_create(tids,NULL,write_msg,&connfd);
pthread_create(tids+1,NULL,read_msg,&connfd);
for(auto tid:tids){
pthread_join(tid,NULL);
}
close(connfd);
return 0;
}
#include <iostream>
#include <arpa/inet.h>
#include <unistd.h>
#include <strings.h>
using namespace std;
void show_connect(int fd) {
struct sockaddr_in local_addr;
socklen_t local_addr_len = sizeof(local_addr);
bzero(&local_addr,local_addr_len);
getsockname(fd,(struct sockaddr*)&local_addr,&local_addr_len);
cout << "local " << inet_ntoa(local_addr.sin_addr) << ":" << ntohs(local_addr.sin_port) << endl;
struct sockaddr_in remote_addr;
socklen_t remote_addr_len = sizeof(remote_addr);
bzero(&remote_addr,remote_addr_len);
getpeername(fd,(struct sockaddr*)&remote_addr,&remote_addr_len);
cout << "remote " << inet_ntoa(remote_addr.sin_addr) << ":" << ntohs(remote_addr.sin_port) << endl;
}
void* read_msg(void* fd) {
int connfd = *(int*)fd;
while(true) {
char buffer[1024] = {0};
int len = recv(connfd,buffer,sizeof(buffer),0);
if(0==len) {
cout << "client exit" << endl;
break;
} else {
cout << "client:" << buffer << endl;
}
}
}
void* write_msg(void* fd) {
int connfd = *(int*)fd;
while(true) {
string message;
getline(cin,message);
send(connfd,message.c_str(),message.size()+1,0);
}
}
int main(int argc,char* argv[]) {
if(3!=argc) {
cout << "Usage:" << argv[0] << " IP port" << endl;
return 1;
}
int listenfd = socket(AF_INET,SOCK_STREAM,0);
if(-1 == listenfd) {
cout << "listen socket error" << endl;
return 1;
}
struct sockaddr_in local_addr;
local_addr.sin_family = AF_INET;
local_addr.sin_addr.s_addr = inet_addr(argv[1]);
local_addr.sin_port = htons(atoi(argv[2]));
if(-1 == bind(listenfd,(struct sockaddr*)&local_addr,sizeof(local_addr))) {
cout << "bind error" << endl;
return 1;
} else {
cout << "bind success" << endl;
}
if(-1==listen(listenfd,10)) {
cout << "listen error" << endl;
return 1;
} else {
cout << "listen success" << endl;
}
struct sockaddr_in remote_addr;
bzero(&remote_addr,sizeof(remote_addr));
socklen_t remote_addr_len = sizeof(remote_addr);
int connfd = accept(listenfd,(struct sockaddr*)&remote_addr,&remote_addr_len);
if(-1 == connfd) {
cout << "accept error" << endl;
return 1;
} else {
cout << "accept success" << endl;
cout << inet_ntoa(remote_addr.sin_addr) <<":" << ntohs(remote_addr.sin_port) << endl;
show_connect(connfd);
}
pthread_t tids[2];
pthread_create(tids,NULL,write_msg,&connfd);
pthread_create(tids+1,NULL,read_msg,&connfd);
for(auto tid:tids){
pthread_join(tid,NULL);
}
close(connfd);
close(listenfd);
}
案例3:简单的TCP的客户端和服务器(一个服务器支持多个客户端)
#include <iostream>
#include <vector>
#include <thread>
#include <arpa/inet.h>
#include <unistd.h>
#include <strings.h>
using namespace std;
void show_connect(int fd) {
struct sockaddr_in local_addr;
socklen_t local_addr_len = sizeof(local_addr);
bzero(&local_addr,local_addr_len);
getsockname(fd,(struct sockaddr*)&local_addr,&local_addr_len);
cout << "local " << inet_ntoa(local_addr.sin_addr) << ":" << ntohs(local_addr.sin_port) << endl;
struct sockaddr_in remote_addr;
socklen_t remote_addr_len = sizeof(remote_addr);
bzero(&remote_addr,remote_addr_len);
getpeername(fd,(struct sockaddr*)&remote_addr,&remote_addr_len);
cout << "remote " << inet_ntoa(remote_addr.sin_addr) << ":" << ntohs(remote_addr.sin_port) << endl;
}
int main(int argc,char* argv[]) {
if(3!=argc) {
cout << "Usage:" << argv[0] << " IP port" << endl;
return 1;
}
int listenfd = socket(AF_INET,SOCK_STREAM,0);
if(-1 == listenfd) {
cout << "listen socket error" << endl;
return 1;
}
int flag = 1;
setsockopt(listenfd,SOL_SOCKET,SO_REUSEADDR,&flag,sizeof(flag));
struct sockaddr_in local_addr;
local_addr.sin_family = AF_INET;
local_addr.sin_addr.s_addr = inet_addr(argv[1]);
local_addr.sin_port = htons(atoi(argv[2]));
if(-1 == bind(listenfd,(struct sockaddr*)&local_addr,sizeof(local_addr))) {
cout << "bind error" << endl;
return 1;
} else {
cout << "bind success" << endl;
}
if(-1==listen(listenfd,10)) {
cout << "listen error" << endl;
return 1;
} else {
cout << "listen success" << endl;
}
vector<int> fds;
thread([&fds]{
while(true){
string message;
getline(cin,message);
message = "广告:" + message;
for(auto fd:fds){
write(fd,message.c_str(),message.size()+1);
}
}
}).detach();
while(true) {
struct sockaddr_in remote_addr;
bzero(&remote_addr,sizeof(remote_addr));
socklen_t remote_addr_len = sizeof(remote_addr);
int connfd = accept(listenfd,(struct sockaddr*)&remote_addr,&remote_addr_len);
if(-1 == connfd) {
cout << "accept error" << endl;
return 1;
} else {
cout << "accept success" << endl;
cout << inet_ntoa(remote_addr.sin_addr) <<":" << ntohs(remote_addr.sin_port) << endl;
show_connect(connfd);
fds.push_back(connfd);
}
thread([connfd,&fds]{
while(true){
char buffer[1024] = {0};
int n = read(connfd,buffer,1024);
if(n == 0){
break;
}else{
for(auto fd:fds){
if(fd==connfd) continue;
write(fd,buffer,1024);
}
}
}
}).detach();
}
close(listenfd);
}
-注意
// 设置端口重复利用
int flag = 1;
setsockopt(listenfd,SOL_SOCKET,SO_REUSEADDR,&flag,sizeof(flag));
#include <iostream>
#include <arpa/inet.h>
#include <unistd.h>
#include <strings.h>
using namespace std;
string name;
void show_connect(int fd) {
struct sockaddr_in local_addr;
socklen_t local_addr_len = sizeof(local_addr);
bzero(&local_addr,local_addr_len);
getsockname(fd,(struct sockaddr*)&local_addr,&local_addr_len);
cout << "local " << inet_ntoa(local_addr.sin_addr) << ":" << ntohs(local_addr.sin_port) << endl;
struct sockaddr_in remote_addr;
socklen_t remote_addr_len = sizeof(remote_addr);
bzero(&remote_addr,remote_addr_len);
getpeername(fd,(struct sockaddr*)&remote_addr,&remote_addr_len);
cout << "remote " << inet_ntoa(remote_addr.sin_addr) << ":" << ntohs(remote_addr.sin_port) << endl;
}
void* read_msg(void* fd) {
int connfd = *(int*)fd;
while(true) {
char buffer[1024] = {0};
int len = read(connfd,buffer,sizeof(buffer));
if(0==len) {
cout << "server exit" << endl;
break;
} else {
cout << buffer << endl;
}
}
}
void* write_msg(void* fd) {
int connfd = *(int*)fd;
while(true) {
string message;
getline(cin,message);
message = name + ":" + message;
write(connfd,message.c_str(),message.size()+1);
}
}
int main(int argc,char* argv[]) {
if(4!=argc) {
cout << "Usage:" << argv[0] << " IP port name" << endl;
return 1;
}
name = argv[3];
int connfd = socket(AF_INET,SOCK_STREAM,0);
if(-1 == connfd) {
cout << "socket error" << endl;
return 1;
}
struct sockaddr_in remote_addr;
remote_addr.sin_family = AF_INET;
remote_addr.sin_addr.s_addr = inet_addr(argv[1]);
remote_addr.sin_port = htons(atoi(argv[2]));
if(-1==connect(connfd,(struct sockaddr*)&remote_addr,sizeof(remote_addr))) {
cout << "connect error" << endl;
return 1;
} else {
cout << "connect success" << endl;
show_connect(connfd);
}
pthread_t tids[2];
pthread_create(tids,NULL,write_msg,&connfd);
pthread_create(tids+1,NULL,read_msg,&connfd);
for(auto tid:tids){
pthread_join(tid,NULL);
}
close(connfd);
return 0;
}
3.4 UDP
3.4.1 单播
基本流程
- 打开socket
int connfd = socket(AF_INET,SOCK_DGRAM,0);
if(-1 == connfd) {
cout << "create socket error" << endl;
return 1;
}
- 设置发送地址和端口
struct sockaddr_in local_addr;
local_addr.sin_family = AF_INET;
local_addr.sin_addr.s_addr = inet_addr(argv[1]);
local_addr.sin_port = htons(atoi(argv[2]));
- 发送数据
发送数据和接收数据采用不同的线程
thread([=] {
for(;;) {
string message;
getline(cin,message);
sendto(connfd,message.c_str(),message.size()+1,0,(struct sockaddr*)&local_addr,sizeof(local_addr));
}
}).detach();
for(;;) {
char buffer[1024];
socklen_t len = sizeof(local_addr);
recvfrom(connfd,buffer,1024,0,(struct sockaddr*)&local_addr,&len);
cout << buffer << endl;
}
- 关闭socket
close(connfd);
#include <iostream>
#include <thread>
#include <arpa/inet.h>
#include <unistd.h>
using namespace std;
int main(int argc,char* argv[]) {
if(3!=argc) {
cout << "Usage:" << argv[0] << " IP Port" << endl;
return 1;
}
int connfd = socket(AF_INET,SOCK_DGRAM,0);
if(-1 == connfd) {
cout << "create socket error" << endl;
return 1;
}
struct sockaddr_in local_addr;
local_addr.sin_family = AF_INET;
local_addr.sin_addr.s_addr = inet_addr(argv[1]);
local_addr.sin_port = htons(atoi(argv[2]));
thread([=] {
for(;;) {
string message;
getline(cin,message);
sendto(connfd,message.c_str(),message.size()+1,0,(struct sockaddr*)&local_addr,sizeof(local_addr));
}
}).detach();
for(;;) {
char buffer[1024];
socklen_t len = sizeof(local_addr);
recvfrom(connfd,buffer,1024,0,(struct sockaddr*)&local_addr,&len);
cout << buffer << endl;
}
close(connfd);
}
- 打开socket
int connfd = socket(AF_INET,SOCK_DGRAM,0);
if(-1 == connfd) {
cout << "create socket error" << endl;
return 1;
}
- 设置接收地址和端口
struct sockaddr_in local_addr;
local_addr.sin_family = AF_INET;
local_addr.sin_addr.s_addr = inet_addr(argv[1]);
local_addr.sin_port = htons(atoi(argv[2]));
- 端口绑定
if(-1 == bind(connfd,(struct sockaddr*)&local_addr,sizeof(local_addr))) {
cout << "bind error" << endl;
return 1;
}
- 接受和发送数据
struct sockaddr_in remote_addr;
bzero(&remote_addr,sizeof(remote_addr));
socklen_t len;
thread([&] {
for(;;) {
char buffer[1024];
recvfrom(connfd,buffer,1024,0,(struct sockaddr*)&remote_addr,&len);
cout << buffer << endl;
}
}).detach();
for(;;) {
string message;
getline(cin,message);
sendto(connfd,message.c_str(),message.size()+1,0,(struct sockaddr*)&remote_addr,len);
}
- 关闭socket
close(connfd);
#include <iostream>
#include <thread>
#include <arpa/inet.h>
#include <unistd.h>
#include <strings.h>
using namespace std;
int main(int argc,char* argv[]) {
if(3!=argc) {
cout << "Usage:" << argv[0] << " IP Port" << endl;
return 1;
}
int connfd = socket(AF_INET,SOCK_DGRAM,0);
if(-1 == connfd) {
cout << "create socket error" << endl;
return 1;
}
struct sockaddr_in local_addr;
local_addr.sin_family = AF_INET;
local_addr.sin_addr.s_addr = inet_addr(argv[1]);
local_addr.sin_port = htons(atoi(argv[2]));
if(-1 == bind(connfd,(struct sockaddr*)&local_addr,sizeof(local_addr))) {
cout << "bind error" << endl;
return 1;
}
struct sockaddr_in remote_addr;
bzero(&remote_addr,sizeof(remote_addr));
socklen_t len;
thread([&] {
for(;;) {
char buffer[1024];
recvfrom(connfd,buffer,1024,0,(struct sockaddr*)&remote_addr,&len);
cout << buffer << endl;
}
}).detach();
for(;;) {
string message;
getline(cin,message);
sendto(connfd,message.c_str(),message.size()+1,0,(struct sockaddr*)&remote_addr,len);
}
close(connfd);
}
按照下表设置服务器。
单播接受端
No. | Recv | IP | port |
---|
1 | unicast_recv | 0.0.0.0 | 8001 |
2 | unicast_recv | 127.0.0.1 | 8001 |
3 | unicast_recv | XXX.XXX.XXX.XXX | 8001 |
单播播发送端
unicast_send 0.0.0.0 8001 HelloWorld
unicast_send 127.0.0.1 HelloWorld
unicast_send XXX.XXX.XXX.XXX 8001 HelloWorld
3.4.2 组播/多播
基本流程
与单播发送者一致
- 打开套接字
int connfd = socket(AF_INET,SOCK_DGRAM,0);
if(-1 == connfd) {
cout << "create socket error" << endl;
return 1;
}
- 构建服务器地址结构
struct sockaddr_in local_addr;
local_addr.sin_family = AF_INET;
local_addr.sin_addr.s_addr = inet_addr(argv[1]);
local_addr.sin_port = htons(atoi(argv[2]));
- 绑定地址
if(-1 == setsockopt(connfd,IPPROTO_IP,IP_ADD_MEMBERSHIP,&group,sizeof(group))){
cout << "multicast setting error" << endl;
return 1;
}
4. 构建组播属性结构
struct ip_mreq group;
group.imr_interface.s_addr = inet_addr(argv[1]);
group.imr_multiaddr.s_addr = inet_addr(argv[3]);
- 设置组播权限和属性
if(-1 == setsockopt(connfd,IPPROTO_IP,IP_ADD_MEMBERSHIP,&group,sizeof(group))){
cout << "multicast setting error" << endl;
return 1;
}
- 设置客户端组播地址
struct sockaddr_in remote_addr;
bzero(&remote_addr,sizeof(remote_addr));
- 发送和接收数据
socklen_t len;
thread([&] {
for(;;) {
char buffer[1024];
recvfrom(connfd,buffer,1024,0,(struct sockaddr*)&remote_addr,&len);
cout << buffer << endl;
}
}).detach();
for(;;) {
string message;
getline(cin,message);
sendto(connfd,message.c_str(),message.size()+1,0,(struct sockaddr*)&remote_addr,len);
}
- 关闭套接字
close(connfd);
#include <iostream>
#include <thread>
#include <arpa/inet.h>
#include <unistd.h>
#include <strings.h>
using namespace std;
int main(int argc,char* argv[]) {
if(4!=argc) {
cout << "Usage:" << argv[0] << " IP Port multiIP" << endl;
return 1;
}
int connfd = socket(AF_INET,SOCK_DGRAM,0);
if(-1 == connfd) {
cout << "create socket error" << endl;
return 1;
}
struct sockaddr_in local_addr;
local_addr.sin_family = AF_INET;
local_addr.sin_addr.s_addr = inet_addr(argv[1]);
local_addr.sin_port = htons(atoi(argv[2]));
if(-1 == bind(connfd,(struct sockaddr*)&local_addr,sizeof(local_addr))) {
cout << "bind error" << endl;
return 1;
}
struct ip_mreq group;
group.imr_interface.s_addr = inet_addr(argv[1]);
group.imr_multiaddr.s_addr = inet_addr(argv[3]);
if(-1 == setsockopt(connfd,IPPROTO_IP,IP_ADD_MEMBERSHIP,&group,sizeof(group))){
cout << "multicast setting error" << endl;
return 1;
}
struct sockaddr_in remote_addr;
bzero(&remote_addr,sizeof(remote_addr));
socklen_t len;
thread([&] {
for(;;) {
char buffer[1024];
recvfrom(connfd,buffer,1024,0,(struct sockaddr*)&remote_addr,&len);
cout << buffer << endl;
}
}).detach();
for(;;) {
string message;
getline(cin,message);
sendto(connfd,message.c_str(),message.size()+1,0,(struct sockaddr*)&remote_addr,len);
}
close(connfd);
}
按照下表设置服务器。
多播接受端
No. | Recv | IP | Multi | port |
---|
1 | multicast_recv | 0.0.0.0 | 224.0.1.1 | 8002 |
2 | multicast_recv | 127.0.0.1 | 224.0.1.1 | 8002 |
3 | multicast_recv | XXX.XXX.XXX.XXX | 224.0.1.1 | 8002 |
单播播发送端
unicast_send 224.0.1.1 8002 HelloWorld