TCP套接字
1、Server
1、创建套接字,和地址族
int sockFd;
int newSock;//用于接收客户端请求
struct sockaddr_in serverAddr;
struct sockaddr_in clientAddr; /* 接受客户端地址族的结构体对象 */
2、编写服务器地址族
sockFd = socket(AF_INET, SOCK_STREAM, 0);//PF_INET == AF_INET
memset(&serverAddr, 0, sizeof(struct sockaddr_in));
serverAddr.sin_family = AF_INET;
serverAddr.sin_addr.s_addr = htonl(INADDR_ANY); /* s_addr是宏定义, s_addr = S_un.S_addr */
serverAddr.sin_port = htons(8090); //最好采用宏定义方便修改 #define SERVER_PORT 8090
/*
字节序转换函数
htonl: 转换long类型到网络字节序
htons: 转换short类型到网络字节序
ntohl: 转换网络字节序到long类型
ntohs: 转换网络字节序到short类型
inet_addr: 将字符串格式IP转换到网络字节序
inet_ntoa: 将网络字节序格式IP转换到字符串
*/
3、绑定地址族
/* 最好加个判断 */
if(bind(sockFd, (struct sockaddr*)&serverAddr, sizeof(sockaddr_in) == -1)
{
//需要头文件 <errno.h> 或者直接打印,方便查看哪出的问题
fprintf(stderr, "Bind error: %s\a\n", strerror(errno));
exit(1);
}
4、监听
if (listen(sockfd, 8) == -1)
{
fprintf(stderr, "Listen error: %s\n\a", strerror(errno));
exit(1);
}
了解更多backlog可以点我
5、接受
socklen_t size = sizeof(struct sockaddr_in); /* socklen_t与int 大小一致*/
if((newSock = accept(sockFd, (struct sockaddr *)(&clientAddr), &size)) == -1)
{
fprintf(stderr, "Accept error: %s\a\n", strerror(errno));
exit(1);
}
6、读写
char buf[1024] = {0};
read(newSock, buf, sizeof(buf));
write(newSock, buf, sizeof(buf));
int recv(int sockfd,void *buf,int len,int flags)
int send(int sockfd,void *buf,int len,int flags)
前面的三个参数和read,write相同,第四个参数能够是0或是以下的组合
0表示与read,write相同的操作,即查看数据并从系统缓冲区移走数据
MSG_DONTROUTE:不查找路由表
MSG_OOB:接受或发送带外数据
MSG_PEEK:查看数据,并不从系统缓冲区移走数据
MSG_WAITALL :等待任何数据
****************************************************************************************************************
MSG_DONTROUTE:是send函数使用的标志.这个标志告诉IP协议.目的主机在本地网络上面,
没有必要查找路由表.这个标志一般用网络诊断和路由程式里面。
MSG_OOB:表示能够接收和发送带外的数据.
MSG_PEEK:是recv函数的使用标志,表示只是从系统缓冲区中读取内容,而不清除系统缓冲区的
内容。这样下次读的时候,仍然是相同的内容。一般在有多个进程读写数据时能够使用这个标志。
MSG_WAITALL:是recv函数的使用标志,表示等到任何的信息到达时才返回。使用这个标志的时候
recv会一直阻塞,直到指定的条件满足,或是发生了错误。
1)当读到了指定的字节时,函数正常返回,返回值等于len
2)当读到了文档的结尾时,函数正常返回.返回值小于len 3)当操作发生错误时,返回-1,且配置错误为相应错误号(errno)
7、关闭
close(newSock);
int shutdown(int sockfd,int howto)
TCP连接是双向的(是可读写的),当我们使用close时,会把读写通道都关闭,有时侯我们希望只关闭一个向,这个时候我们能够使用shutdown.针对不同的howto,系统回采取不同的关闭方式.
howto=0这个时候系统会关闭读通道.但是能够继续往接字描述符写.
howto=1关闭写通道,和上面相反,着时候就只能够读了.
howto=2关闭读写通道,和close相同在多进程程式里面,假如有几个子进程共享一个套接字时,假如我们使用shutdown, 那么任何子进程都不能够操作了,这个时候我们只能够使用close来关闭子进程的套接字描述符.
2、Client
1、创建套接字
int client_sock;
struct sockaddr_in server_addr;
client_sock = socket(AF_INET, SOCK_STREAM, 0);
2、创建服务器地址族
memset(&server_addr, 0, sizeof server_addr);
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(8090);
server_addr.sin_addr.s_addr = inet_addr(127.0.0.1);
/* 127.0.0.1 本地IP回环*/
3、连接
if (connect(client_sock, (struct sockaddr *)(&server_addr),sizeof server_addr) == -1)
{
fprintf(stderr, "Connect Error: %s\a\n", strerror(errno));
exit(1);
}
4、读写
char buf[1024] = {0};
read(newSock, buf, sizeof(buf));
write(newSock, buf, sizeof(buf));