socket
网络API(应用程序编程接口),实现相同或不同主机上的进程通信
头文件:
#include <sys/types.h>
#include <sys/socket.h>
原型:
int socket(int domain, int type, int protocol);
参数:
int domain:Linux支持的协议族(地址族);
AF_UNIX, AF_LOCAL Local communication unix(7)
AF_INET IPv4 Internet protocols ip(7)
AF_INET6 IPv6 Internet protocols ipv6(7);
int type:套接字类型
SOCK_STREAM:字节流式套接字----> TCP
SOCK_DGRAM: 数据报式套接字----> UDP
SOCK_RAW:原始套接字,传输协议需要在第三个参数指定;
int protocol:传输协议。如果不需要填0;
IPPROTO_TCP 和 IPPROTO_UDP;
返回值:
成功,返回套接字的文件描述符;
失败,返回-1,更新errno;
例程:
//TCP
int sfd = socket(AF_INET, SOCK_STREAM, 0);
if(sfd < 0)
{
perror("socket");
return -1;
}
bind
服务器:(必须)绑定本地的IP和端口号到套接字
客户端:(非必需)绑定本地的IP和端口号到套接字
头文件:
#include <sys/types.h>
#include <sys/socket.h>
原型:
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
参数:
int sockfd:socket创建的套接字;
struct sockaddr *addr:通用地址结构体,一般不采用该结构体,不同的协议族采用不同的地址结构体(详见socket),调用时强制转换到通用地址结构体;
socklen_t addrlen:地址结构体的大小,sizeof(struct sockaddr_in);
返回值:
成功,返回0;
失败,返回-1,更新errno;
例程:
struct sockaddr_in saddr;
saddr.sin_family = AF_INET;
saddr.sin_port = htons(PORT);
saddr.sin_addr.s_addr = inet_addr(IP);
if(bind(sfd,(struct sockaddr*)&saddr,sizeof(saddr)) < 0)
{
ERR_LOG("bind");
return -1;
}
listen
将套接字设置为被动监听状态
头文件:
#include <sys/types.h>
#include <sys/socket.h>
原型:
int listen(int sockfd, int backlog);
参数:
int sockfd:socket创建的套接字;
int backlog:允许同时建立多少链接;
内核会为每一个sockfd创建维护两个队列;
未完成连接的队列
完成连接的队列,等着accept函数拿走。
返回值:
成功,返回0;
失败,返回-1;
例程:
if(listen(sfd,5) < 0)
{
ERR_LOG("listen");
return -1;
}
printf("开始监听...\n");
accept
阻塞函数,接收客户端的连接,并返回套接字;
从已完成连接的队列头中,取出一个sfd连接,生成新的套接字newfd
头文件:
#include <sys/types.h>
#include <sys/socket.h>
原型:
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
参数:
int sockfd:已经被listen设置为被动监听状态的套接字;
struct sockaddr *addr:存储连接成功后客户端的信息:IP和端口号
socklen_t *addrlen:第二个结构体的大小,注意是指针类型;
返回值:
成功,返回一个新的文件描述符,是与客户端建立连接的文件描述符。往这个文件描述符中读写信息,就是与客户端通信;
失败,返回-1,更新errno;
例程:
struct sockaddr_in caddr;
socklen_t caddrlen = sizeof(caddr);
int cfd = accept(sfd,(struct sockaddr*)&caddr,&caddrlen);
if(cfd < 0)
{
ERR_LOG("accept");
return -1;
}
printf("连接成功\n");
connnect
根据服务器的IP和端口号连接到服务器
头文件:
#include <sys/types.h>
#include <sys/socket.h>
原型:
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
参数:
int sockfd:socket创建的套接字;
struct sockaddr *addr:通用地址结构体,填充服务器的地址信息:IP+端口号;
socklen_t addrlen:地址结构体的大小;
返回值:
成功,返回0;
失败,返回-1,更新errno;
例程:
struct sockaddr_in saddr;
saddr.sin_family = AF_INET;
saddr.sin_port = htons(PORT);
saddr.sin_addr.s_addr = inet_addr(IP);
if(connect(sfd,(struct sockaddr*)&saddr,sizeof(saddr)) < 0)
{
ERR_LOG("connect");
return -1;
}
printf("连接成功\n");
recv
从套接字中获取数据
头文件:
#include <sys/types.h>
#include <sys/socket.h>
原型:
size_t recv(int sockfd, void *buf, size_t len, int flags);
参数:
int sockfd:服务器:accept函数返回的新的套接字;客户端:socket返回的套接字;
void *buf:存储读取到的数据;
size_t len:指定要接收的数据大小;
int flags:接收方式,0阻塞方式接收;
返回值:
>0;接收到的字符数;
==0; 对方关闭;
-1; 函数调用出错。
例程:
recv_len = recv(cfd,buf,N,0);
if(recv_len < 0)
{
ERR_LOG("recv");
return -1;
}
else if(0 == recv_len)
{
printf("the client is offline\n");
close(sfd);
close(cfd);
exit(1);
}
send
将数据发送到套接字中
头文件:
#include <sys/types.h>
#include <sys/socket.h>
原型:
ssize_t send(int sockfd, const void *buf, size_t len, int flags);
参数:
int sockfd:服务器:accept函数返回的新的套接字;客户端:socket返回的套接字;
void *buf:存储要发送的数据;
size_t len:发送的数据大小;
int flags:发送方式,0阻塞方式发送;
返回值:
成功,返回发送成功的字节数;
失败,返回-1,更新errno;
例程:
if(send(cfd,buf,strlen(buf),0) < 0)
{
ERR_LOG("send");
break;
}
TCP模型
TCP服务器
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <pthread.h>
#include <stdlib.h>
#define ERR_LOG(msg) do{\
perror(msg);\
printf("%d %s %s\n",__LINE__,__func__,__FILE__);\
}while(0)
#define IP "192.168.1.118"
#define PORT 8888
#define N 128
char buf[N] = "";
void *handler_send(void *arg)
{
pthread_detach(pthread_self());
//6、send发送数据
while(1)
{
bzero(buf,sizeof(buf));
fgets(buf,N,stdin);
buf[strlen(buf)-1] = 0;
if(send(*(int*)arg,buf,strlen(buf),0) < 0)
{
ERR_LOG("send");
break;
}
}
pthread_exit(NULL);
}
int main(int argc, const char *argv[])
{
//1、创建socket套接字
int sfd = socket(AF_INET,SOCK_STREAM,0);
if(sfd < 0)
{
ERR_LOG("socket");
return -1;
}
//2、bind服务器的IP & PORT
struct sockaddr_in saddr;
saddr.sin_family = AF_INET;
saddr.sin_port = htons(PORT);
saddr.sin_addr.s_addr = inet_addr(IP);
if(bind(sfd,(struct sockaddr*)&saddr,sizeof(saddr)) < 0)
{
ERR_LOG("bind");
return -1;
}
//3、listen是否有客户端访问
if(listen(sfd,5) < 0)
{
ERR_LOG("listen");
return -1;
}
printf("开始监听...\n");
//4、accept成功与客户端建立连接,新建文件描述符用于通信
struct sockaddr_in caddr;
socklen_t caddrlen = sizeof(caddr);
int cfd = accept(sfd,(struct sockaddr*)&caddr,&caddrlen);
if(cfd < 0)
{
ERR_LOG("accept");
return -1;
}
printf("连接成功\n");
//创建发送数据线程
pthread_t tid;
if(pthread_create(&tid,NULL,handler_send,(void*)&cfd) != 0)
{
ERR_LOG("pthread_creat");
return -1;
}
int res;
while(1)
{
//5、recv接收数据
bzero(buf,sizeof(buf));
res = recv(cfd,buf,N,0);
if(res < 0)
{
ERR_LOG("recv");
return -1;
}
else if(0 == res)
{
printf("the client is offline\n");
close(sfd);
close(cfd);
exit(1);
}
puts(buf);
}
//7、close文件描述符
close(sfd);
close(cfd);
return 0;
}
TCP客户端
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <pthread.h>
#include <stdlib.h>
#define ERR_LOG(msg) do{\
perror(msg);\
printf("%d %s %s\n",__LINE__,__func__,__FILE__);\
}while(0)
//服务端IP & PORT
#define IP "192.168.1.104"
#define PORT 8888
#define N 128
char buf[N] = "";
void *handler_send(void *arg)
{
pthread_detach(pthread_self());
//5、send发送数据
while(1)
{
bzero(buf,sizeof(buf));
fgets(buf,N,stdin);
buf[strlen(buf)-1] = 0;
if(send(*(int*)arg,buf,strlen(buf),0) < 0)
{
ERR_LOG("send");
break;
}
}
pthread_exit(NULL);
}
int main(int argc, const char *argv[])
{
//1、创建socket套接字
int sfd = socket(AF_INET,SOCK_STREAM,0);
if(sfd < 0)
{
ERR_LOG("socket");
return -1;
}
//2、bind客户端的IP & PORT,非必须
//3、connect连接服务端
struct sockaddr_in caddr;
caddr.sin_family = AF_INET;
caddr.sin_port = htons(PORT);
caddr.sin_addr.s_addr = inet_addr(IP);
if(connect(sfd,(struct sockaddr*)&caddr,sizeof(caddr)) < 0)
{
ERR_LOG("connect");
return -1;
}
printf("连接成功\n");
//创建发送数据线程
pthread_t tid;
if(pthread_create(&tid,NULL,handler_send,(void*)&sfd) != 0)
{
ERR_LOG("pthread_creat");
return -1;
}
int res;
while(1)
{
//4、recv接收数据
bzero(buf,sizeof(buf));
res = recv(sfd,buf,N,0);
if(res < 0)
{
ERR_LOG("recv");
return -1;
}
else if(0 == res)
{
printf("the server is offline\n");
close(sfd);
exit(1);
}
puts(buf);
}
//7、close文件描述符
close(sfd);
return 0;
}
本文链接:https://shengto.top/network_programming/tcp_model.html
转载时须注明出处及本声明