写目录
TCP编程
sockaddr_in套接字
已经提供好了套接字结构体,我们可以直接使用。
struct sockaddr_in{
sa_family_t sin_family; //2字节
in_port_t sin_port; //2字节
struct in_addr sin_addr; //4字节
unsigned char sin_zero[8]; //8字节
};
sockaddr通用套接字
使格式统一,使不同格式的都能传入套接字函数,我们可以强制转换成同意套接字。
struct sockaddr{
sa_family_t sa_family; //2字节
char sa_data[14]; //14字节
};
inet_pton()函数
#include <arpa/inet.h>
int inet_pton(int family, const char*strptr, void *addrptr);
功能:将点分十进制数串转换成32位无符号整数
参数:
family:协议族
strptr :点分十进制数串
addrptr:32位无符号整数的地址
返回值:成功返回1,失败返回其他
inet_ntop()函数
#include <arpa/inet.h>
const char *inet_ntop(int family, constvoid *addrptr, char *strptr, size_t len);
功能:将32位无符号整数转换成点分十进制数串
参数:
family:协议族
addrptr:32位无符号整数的地址
strptr :点分十进制数串
len:strptr缓存区长度
len的宏定义:
#define INET_ADDRSTRLEN 16 //ipv4
#define INET6_ADDRSTRLEN 46 // ipv6
返回值:成功返回字符串首地址,失败返回NULL
创建套接字
int socket(int family, int type,int protocol);
功能:创建套接字
参数:
family:协议族
AF_INET,AF_INET6AF_LOCAL,AF_ROUTE,AF_KEY
type:套接字类型
SOCK_STREAM,SOCK_DGRAM,SOCK_RAW,SOCK_SEQPACKET
protocol:协议类别
0,IPPROTO_TCP,IPPROTO_UDP,IPPROTO_SCTP
返回值:创建的套接字
创建的套接字的特点:
系统不会分配端口,需要绑定端口;创建的是主动套接字,作为服务器需要被动的等待别人连接。
服务端
本地协议与套接字绑定
int bind(int sockfd,const struct sockaddr *myaddr,socklen_t addrlen);
功能:将本地协议与套接字绑定
参数:
sockfd: socket套接字
myaddr: 指向特定于协议的地址结构指针
addrlen:该地址结构的长度
返回值:成功:返回0,失败:其他
监听
int listen(int sockfd, int backlog);
功能;将套接字的主动修改为被动;为套接字创建一个链接队列,用来记录链接到该套接字的所有连接
参数:
sockfd:要监听的套接字地址
backlog:连接队列的长度
返回值:成功返回0;失败返回其他
获取连接
int accept(int sockfd,struct sockaddr *cliaddr,socklen_t *addrlen);
功能:从连接队列中获取一个连接,若连接队列为空,则进入睡眠等待。
参数:
sockfd: socket监听套接字
cliaddr: 用于存放客户端套接字地址结构
addrlen:套接字地址结构体长度
返回值:已连接套接字
客户端
连接服务器
int connect(int sockfd,const structsockaddr *addr,socklen_t len);
功能:与服务器建立连接
参数:
sockfd:socket套接字
addr: 需连接的服务器地址结构
addrlen:地址结构体长度
返回值:成功返回0,失败返回其他
发送数据
ssize_t send(int sockfd, const void* buf,size_t nbytes, int flags);
功能:发送数据(不能发送长度为0的数据)
参数:
sockfd: socket套接字
buf: 待发送数据缓存区的地址
nbytes: 发送缓存区大小(以字节为单位)
flags: 套接字标志(常为0)
返水着:成功发送的字节数
接收数据
ssize_t recv(int sockfd, void *buf,size_t nbytes, int flags);
功能:接受数据
参数:
sockfd: 套接字
buf: 指向接收网络数据的缓冲区
nbytes: 接收缓冲区的大小(以字节为单位)
flags: 套接字标志(常为0)
返回值:成功接收到字节数
关闭连接
close();
关闭一个代表已连接套接字将导致另一端接收到一个0长度的数据包
做服务器时:
关闭socket创建的监听套接字将导致服务器无法继续接受新的连接,但不会影响已经建立的连接
关闭accept返回的已连接套接字将导致它所代表的连接被关闭,但不会影响服务器的监听
做客户端时:
关闭连接就是关闭连接,不意味着其他
客户端:
int main(int argc, char *argv[])
{
unsigned short port = 8000; // 服务器的端口号
char *server_ip = "47.98.51.177"; // 服务器ip地址
char send_buf[512] = "hello every!";
char recv_buf[512] = "";
int sockfd = 0;
int err_log = 0;
struct sockaddr_in server_addr;
if( argc > 1 ) //函数传参,可以更改服务器的ip地址
{
server_ip = argv[1];
}
if( argc > 2 ) //函数传参,可以更改服务器的端口号
{
port = atoi(argv[2]);
}
bzero(&server_addr,sizeof(server_addr)); // 初始化服务器地址
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(port);
inet_pton(AF_INET, server_ip, &server_addr.sin_addr);
sockfd = socket(AF_INET, SOCK_STREAM, 0); // 创建通信端点:套接字
if(sockfd < 0)
{
perror("socket");
exit(-1);
}
err_log = connect(sockfd, (struct sockaddr*)&server_addr, sizeof(server_addr)); // 主动连接服务器
if(err_log != 0)
{
perror("connect");
close(sockfd);
exit(-1);
}
send(sockfd, send_buf, strlen(send_buf), 0); // 向服务器发送信息
recv(sockfd, recv_buf, sizeof(recv_buf), 0); // 接收服务器发回的信息
printf("%s\n", recv_buf);
close(sockfd);
return 0;
}
服务端:
int main(int argc, char *argv[])
{
char recv_buf[2048] = ""; // 接收缓冲区
int sockfd = 0; // 套接字
int connfd = 0;
int err_log = 0;
struct sockaddr_in my_addr; // 服务器地址结构体
unsigned short port = 8000; // 监听端口
if(argc > 1) // 由参数接收端口
{
port = atoi(argv[1]);
}
printf("TCP Server Started at port %d!\n", port);
sockfd = socket(AF_INET, SOCK_STREAM, 0); // 创建TCP套接字
if(sockfd < 0)
{
perror("socket");
exit(-1);
}
bzero(&my_addr, sizeof(my_addr)); // 初始化服务器地址
my_addr.sin_family = AF_INET;
my_addr.sin_port = htons(port);
my_addr.sin_addr.s_addr = htonl(INADDR_ANY);
printf("Binding server to port %d\n", port);
err_log = bind(sockfd, (struct sockaddr*)&my_addr, sizeof(my_addr));
if( err_log != 0)
{
perror("binding");
close(sockfd);
exit(-1);
}
err_log = listen(sockfd, 10);
if(err_log != 0)
{
perror("listen");
close(sockfd);
exit(-1);
}
printf("Waiting client...\n");
while(1)
{
size_t recv_len = 0;
struct sockaddr_in client_addr; // 用于保存客户端地址
char cli_ip[INET_ADDRSTRLEN] = ""; // 用于保存客户端IP地址
socklen_t cliaddr_len = sizeof(client_addr); // 必须初始化!!!
connfd = accept(sockfd, (struct sockaddr*)&client_addr, &cliaddr_len); // 获得一个已经建立的连接
if(connfd < 0)
{
perror("accept");
continue;
}
inet_ntop(AF_INET, &client_addr.sin_addr, cli_ip, INET_ADDRSTRLEN);
printf("client ip = %s\n", cli_ip);
while((recv_len = recv(connfd, recv_buf, sizeof(recv_buf), 0)) > 0)
{
send(connfd, recv_buf, recv_len, 0);
}
close(connfd); //关闭已连接套接字
printf("client closed!\n");
}
close(sockfd); //关闭监听套接字
return 0;
}
UDP编程
发送数据
ssize_t sendto(int sockfd,const void*buf, size_t nbytes,int flags, conststruct sockaddr *to,socklen_t addrlen);
功能:发送UDP数据
参数:
sockfd:套接字
buf:发送数据缓冲区
nbytes:发送数据缓冲区的大小
flags:一般为0
to:指向目的主机地址结构体的指针
addrlen:to所指向内容的长度
注意:可以发送长度为0的数据包
返回值:成功返回发送的字符数,失败返回-1
接收数据
ssize_t recvfrom(int sockfd, void *buf,size_t nbytes,int flags,structsockaddr *from, socklen_t *addrlen);
功能:接受UDP数据
参数:
sockfd:套接字
buf: 接收数据缓冲区
nbytes:接收数据缓冲区的大小
flags: 套接字标志(常为0)
from: 用于存放发送方信息的地址结构体指针
addrlen: from所指内容的长度
注意:若from、addrlen两个参数为NULL,则表示不关心数据来源
返回值:成功返回接受到的字符数,失败返回-1
例程
服务器端:
void main(){
int socketfd,ret;
struct sockaddr_in my_addr;
unsigned short port = 8000;
//创建套接字
socketfd = socket(AF_INET,SOCK_DGRAM,0);
if(socketfd < 0)
{
perror("创建socket失败\n");
exit(-1);
}
bzero(&my_addr,sizeof(my_addr));
my_addr.sin_family = AF_INET;
my_addr.sin_port = htons(port);
my_addr.sin_addr.s_addr = htonl(INADDR_ANY);
printf("要绑定的端口:%d ,16进制显示:%x ,转换后的端口%x \n",port,port,my_addr.sin_port );
//绑定
ret = bind(socketfd,(struct sockaddr*)&my_addr,sizeof(my_addr));
if(ret != 0)
{
perror("绑定失败\n");
exit(-1);
}
printf("等待客户端连接\n");
while(1)
{
char buff[1024] = "";
int recv_len = 0;
struct sockaddr_in client_addr;
char ip[INET_ADDRSTRLEN]="";
socklen_t len = sizeof(client_addr);
recv_len = recvfrom(socketfd,buff,sizeof(buff),0,(struct sockaddr*)&client_addr,&len);
inet_ntop(AF_INET,&client_addr.sin_addr,ip,INET_ADDRSTRLEN);
printf("连接的客户端ip为:%s",ip);
sendto(socketfd,buff,sizeof(buff),0,(struct sockaddr*)&client_addr,len);
}
}
客户端:
void main(){
int socketfd,ret;
struct sockaddr_in my_addr;
unsigned short port = 8000;
char serv_ip[INET_ADDRSTRLEN]="127.0.0.1";
socketfd = socket(AF_INET, SOCK_DGRAM, 0);
if(socketfd < 0)
{
perror("创建socket失败\n");
exit(-1);
}
bzero(&my_addr,sizeof(my_addr));
my_addr.sin_family = AF_INET;
my_addr.sin_port = htons(port);
inet_pton(AF_INET, serv_ip, &my_addr.sin_addr);
printf("准备向服务器发送数据\n");
while(1)
{
char str[100] = "helloworld";
char recv[100] = "";
sendto(socketfd,str,sizeof(str),0,(struct sockaddr*)&my_addr, sizeof(my_addr));
recvfrom(socketfd,recv,sizeof(recv),0,NULL,NULL);
printf("接收到的回传数据:%s \n",recv);
}
}