一、常用函数接口
(1)通用结构体:
struct sockaddr {
sa_family_t sa_family; /* unsigned short , 指定了通信协议家族 */
char sa_data[14];
};
(2)IPV4网络通信地址结构体:
struct sockaddr_in {
pa_family_t sin_family;
port_t sin_port;
struct in_addr {
unsigned int s_addr;
}sin_addr;
};
(3)UNIX本地通信地址结构体:
struct sockaddr_un {
sa_family_t sun_family;
char sun_path[108];
};
(4)socket
函数名:
int socket (int domain, int type, int protocl)
功能: 打开一个套接字,并返回一个相关的文件描述符。
返回值:成功, 一个新的文件描述符; 失败, -1;
参数表:
int domain, 通信域:
AF_INET | AF_INET6 | AF_UNIX | AF_PACKET
int type, 套接字类型:
SOCK_STREAM 流式套接字
SOCK_DGRAM 数据报套接字
SOCK_RAW 原始套接字
int protocl, 协议(在原始套接字中根据需要选择使用, 流式或数据报套接字中为 0).
(5)bind
函数名:
int bind(int sockfd, const struct sockaddr * addr, socklen_t addrlen);
功能: 为一个打开的套接字,绑定一个地址(IP+PORT)。
返回值: 成功, 0; 失败, -1;
参数表:
int sockfd, 通过socket打开并返回的文件描述符。
const struct sockaddr * addr, 通用结构体地址,具体根据通信域选择相关的地址格式。
socklen_t addrlen, 实际地址结构体的大小。
(6)sendto
函数名:
int sendto(int sockfd, void *buff, size_t size, int flag, const struct sockaddr *addr, socklen_t addrlen);
功能: 向一个指定的目标主机发送数据包。
返回值: 成功, 实际发送的字节数; 失败, -1;
参数表:
int sockfd, 通过socket打开并返回的文件描述符。
void *bufff, 即将发送的数据。
size_t size, 数据大小。
int flag, 发送标志位(阻塞、非阻塞等, 如果无特殊要求则设置为0)。
const struct sockaddr * addr, 目标主机的地址结构体(IP+PORT)。
socklen_t addrlen, 目标主机地址结构体大小。
(7)recvfrom
函数名:
int recvfrom(int sockfd, void *buff, size_t size, int flag, struct sockaddr *addr, socklen_t *addrlen);
功能: 接受一个数据包,并带回数据包的来源地址。
返回值: 成功, 实际接收的字节数; 失败, -1; 对方关闭, 0;
参数表:
int sockfd, 通过socket打开并返回的文件描述符。
void *bufff, 接受数据的缓冲区。
size_t size, 缓冲区大小。
int flag, 接收标志位(阻塞、非阻塞等, 如果无特殊要求则设置为0)。
struct sockaddr * addr, 用来保存数据源主机的地址的结构体(IP+PORT)。
socklen_t *addrlen, 期望接收的数据源主机的地址结构体大小(在使用前由用户自己初始化);一旦该函数调用结束,该变量会被修改为实际大小。
(8)listen
函数名:
int listen(int sockfd, int backlog);
功能: 将一个经过bind的套接字置为被动状态,以便将来使用accept接受新的请求。
返回值:成功, 0; 失败, -1;
参数表:
int sockfd, 通过socket创建,并通过bind后的套接字描述符。
int backlog, 握手请求队列的最大值。
(9)accept
函数名:
int accept(int sockfd, struct sockaddr * addr, socklen_t addrlen);
功能: 接受一个握手请求,并返回一个新的文件描述符。
返回值: 成功, 新的文件描述符(用来收发数据的通道); 失败, -1;
参数表:
int sockfd, 经过socket创建,bind后并且listen后的文件描述符。
struct sockaddr *addr, 用来带回握手请求方的地址结构体。
socklen_t addrlen, 期望接收的对方的地址大小,该值会被修改为实际大小,调用者需提前初始化。
(10)select
函数名:
int select(int nfds, fd_set *rdfds, fd_set *wrfds, fd_set *exceptfds, struct timeval * timval);
功能: 在多路IO复用模型中,负责检测多个文件描述符资源。但没有资源可操作时,进程阻塞; 有资源操作时返回可操作的资源个数。
返回值: 成功, 资源个数; 失败, -1; 超时, 0;
参数表:
int nfds, 检测的最大描述符值+1.
fd_set *rdfds, 希望监测的读资源描述符集合。
fd_set *wrfds, 希望监测的写资源描述符集合。
fd_set *wrfds, 希望监测的其他资源描述符集合。
ftruct timeval *timval, 超时监测,设定秒+微秒。
select相关函数:
FD_SET(int fd, fd_set * fds): 将描述加入集合中。
FD_CLR(int fd, fd_set *fds): 将指定描述从集合中移除。
FD_ZERO(fd_set *fds): 清空一个描述符集合。
FD_ISSET(Iint fd, fd_set *fds): 测试一个描述符是否在集合中(测试该描述符资源是否可操作);
(11)其他函数接口:
int send(int sockfd, const void *buff, size_t size, int flag);
int recv(int sockfd, void *buff, size_t size, int flag);
getpeername(int connfd, struct sockaddr *addr, socklen_t *addrlen);
getsockname(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
setsockopt(int level, int optname, const void *optval, socklen_t optlen);
short htons(short );
int htonl(int);
short ntohs(short);
int ntohl(int);
二、TCP编程框架及代码
TCP编程框架:
客户端:
socket();
connect();
send()/write();
recv()/read();
close();
服务器端:
socket();
bind();
listen();
accept();
recv()/read();
send()/write();
close();
编程实例:
服务器端:
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <errno.h>
#include <error.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#define ERROR(_msg_) do{ \
perror(_msg_); \
exit(EXIT_FAILURE); \
}while(0)
#define ERROR2(_msg_) error(EXIT_FAILURE, errno, _msg_)
#define SERVER_PORT 50001//端口号,注意不要选择已被占用端口号
#define SERVER_ADDR "192.168.1.13"//服务器端IP地址
#define BUFF_SIZE 1024
int main(int argc, const char *argv[])
{
int sockfd, connfd;
struct sockaddr_in local_addr,
peer_addr;
socklen_t addrlen;
char buff[BUFF_SIZE];
/*建立Socket连接*/
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (-1 == sockfd)
ERROR("socket");
/*设置socketaddr_in 结构体中相关参数*/
local_addr.sin_family = AF_INET;
local_addr.sin_port = htons(SERVER_PORT);
local_addr.sin_addr.s_addr = inet_addr(SERVER_ADDR);
addrlen = sizeof(local_addr);
/*绑定函数bind()*/
if (-1 == bind(sockfd, (struct sockaddr *)&local_addr, addrlen))
ERROR("bind");
/*调用listen()函数,设置监听模式*/
if (-1 == listen(sockfd ,10))
ERROR("listen");
/*调用accept()函数,等待客户端的连接*/
connfd = accept(sockfd, NULL, NULL);
if (-1 == connfd)
ERROR("accept");
/*调用recv函数接收客户端发送的数据*/
if (-1 == recv(connfd, buff, BUFF_SIZE, 0))
ERROR("recv");
puts(buff);
strcpy(buff, "1234567890");
/*调用send函数向客户端发送数据*/
if (-1 == send(connfd, buff, BUFF_SIZE, 0))
ERROR("send");
close(sockfd);
close(connfd);
return 0;
}
客户端:
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <errno.h>
#include <error.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#define ERROR(_msg_) do{ \
perror(_msg_); \
exit(EXIT_FAILURE); \
}while(0)
#define ERROR2(_msg_) error(EXIT_FAILURE, errno, _msg_)
#define CLIENT_PORT 50001//端口号,注意不要选择已被占用端口号
#define CLIENT_ADDR "192.168.1.13"客户端ip地址
#define BUFF_SIZE 1024
int main(int argc, const char *argv[])
{
int sockfd;
struct sockaddr_in local_addr,
peer_addr;
socklen_t addrlen;
char buff[BUFF_SIZE];
/*建立Socket连接*/
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (-1 == sockfd)
ERROR("socket");
/*设置socketaddr_in 结构体中相关参数*/
peer_addr.sin_family = AF_INET;
peer_addr.sin_port = htons(SERVER_PORT);
peer_addr.sin_addr.s_addr = inet_addr(SERVER_ADDR);
addrlen = sizeof(peer_addr);
/*调用connect()函数向服务器端建立TCP连接*/
if (-1 == connect(sockfd, (struct sockaddr *)&peer_addr, addrlen))
ERROR("connect");
strcpy(buff, "helloworld");
/*发送消息给客户端*/
if (-1 == send(sockfd, buff, BUFF_SIZE, 0))
ERROR("send");
/*接收服务器所发送消息*/
if (-1 == recv(sockfd, buff, BUFF_SIZE, 0))
ERROR("recv");
puts(buff);
//read();
//
close(sockfd);
return 0;
}
通信验证(以下为本机通信):
三、UDP编程框架及代码
UDP编程接口:
客户端:
socket():
sendto():
recvfrom();
close();
服务器端:
socket();
bind():
recvfrom();
sendto();
close();
编程实例:
客户端:
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <stdio.h>
#include <errno.h>
#include <error.h>
#define error_exit(_errmsg_) error(EXIT_FAILURE, errno, _errmsg_)
#define BUFF_SIZE 1024
#define CLT_PORT 5000//端口号
#define CLTADDR "169.254.8.225"//客户端ip
int main()
{
int sockfd;
struct sockaddr_in seraddr;
char buff[BUFF_SIZE];
int ret;
int count = 0;
/*设置socketaddr_in 结构体中相关参数*/
seraddr.sin_family = AF_INET;
seraddr.sin_port = htons(SER_PORT);
seraddr.sin_addr.s_addr = inet_addr(SER_ADDR);
/*建立Socket连接*/
if (-1 == (sockfd = socket(AF_INET, SOCK_DGRAM, 0)))
error_exit("socket");
while (1) {
sprintf(buff, "helloworld%d", count ++);
/*发送消息给服务器*/
if (-1 == sendto(sockfd, buff, strlen(buff)+1, 0,
(struct sockaddr *)&seraddr, sizeof(seraddr)))
error_exit("sendto");
memset(buff, 0, BUFF_SIZE);
/*接收服务器发送的消息*/
if (-1 == (ret = recvfrom(sockfd, buff, BUFF_SIZE, 0, NULL, NULL)))
error_exit("recvfrom");
printf("[%d]%s\n", ret, buff);
}
close(sockfd);
return 0;
}
服务器端
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <stdio.h>
#include <errno.h>
#include <error.h>
#define error_exit(_errmsg_) error(EXIT_FAILURE, errno, _errmsg_)
#define BUFF_SIZE 1024
#define SER_PORT 50000
#define SER_ADDR "169.254.8.225"
int main()
{
int sockfd;
struct sockaddr_in seraddr,
cltaddr;
char buff[BUFF_SIZE];
int ret;
socklen_t addrlen = sizeof(cltaddr);
/*设置socketaddr_in 结构体中相关参数*/
seraddr.sin_family = AF_INET;
seraddr.sin_port = htons(SER_PORT);
seraddr.sin_addr.s_addr = inet_addr(SER_ADDR);
/*建立Socket连接*/
if (-1 == (sockfd = socket(AF_INET, SOCK_DGRAM, 0)))
error_exit("socket");
/*绑定函数bind*/
if (-1 == bind(sockfd, (struct sockaddr *)&seraddr, sizeof(seraddr)))
error_exit("bind");
while (1) {
memset(buff, 0, BUFF_SIZE);
/*调用recvfrom()等待接受客户端的数据*/
if (-1 == (ret = recvfrom(sockfd, buff, BUFF_SIZE, 0,
(struct sockaddr *)&cltaddr, &addrlen)))
error_exit("recvfrom");
printf("%s:%d->%s\n",
inet_ntoa(cltaddr.sin_addr), ntohs(cltaddr.sin_port), buff);
/*向客户端发送数据*/
if (-1 == sendto(sockfd, buff, strlen(buff)+1, 0,
(struct sockaddr *)&cltaddr, sizeof(cltaddr)))
error_exit("sendto");
}
close(sockfd);
return 0;
}
补充声明两个函数:
htons(): 将2字节的本机字节序转换成网络字节序。
inet_addr(): 将点分十进制的IP地址转换成二进制的IP地址,并且按照网络字节序存放。
通信验证(本机):