一些API(上一篇的补充)
accept
SYNOPSIS
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
RETURN VALUE
On success, these system calls return a nonnegative integer that is a
file descriptor for the accepted socket. On error, -1 is returned, and
errno is set appropriately.
accept API是在服务器listen之后使用的,作用是一直阻塞,等待接收客户端的连接。
注意:
原型中的sockfd是listen队列中的socket file descriptor,不是原客户端自己的sockfd。
在man手册里面有更详尽的description讲到,accept是取监听队列中的第一个连接请求的sockfd来用。
第二个参数结构体指针是一个输出型参数,因为accept返回的是一个新的sockfd,我叫它为client fd,得到这个fd的作用是给后续的send和recv使用的。
send
SYNOPSIS
#include <sys/types.h>
#include <sys/socket.h>
ssize_t send(int sockfd, const void *buf, size_t len, int flags);
write:
SYNOPSIS
#include <unistd.h>
ssize_t write(int fd, const void *buf, size_t count);
send和write是非常类似的,在description里面说如果send的flag设置为0的话就跟write的用法一样。
客户端和服务器可以通过send API来相互发送信息。
recv
#include <sys/types.h>
#include <sys/socket.h>
ssize_t recv(int sockfd, void *buf, size_t len, int flags);
recv同样跟read非常像,如果flags设置为0就跟read同等作用了。
客户端和服务器可以通过recv来相互接收信息。
htons
htons: 展开来是host to net short
SYNOPSIS
#include <arpa/inet.h>
uint32_t htonl(uint32_t hostlong);
uint16_t htons(uint16_t hostshort);
uint32_t ntohl(uint32_t netlong);
uint16_t ntohs(uint16_t netshort);
这个库函数的作用:
The htons() function converts the unsigned short integer hostshort from
host byte order to network byte order.
因为端口号这些要根据不同机器大小端的问题,所以要通过这个库函数来转换成网络字节序,使得在传输中与其他设备进行匹配。
对上节的一些骨架进行丰满
服务器的代码
看代码的时候先看按照 socket —bind—listen—accept—的顺序来看。
#include <stdio.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdlib.h>
#define SER_PORT 7777
#define SER_IP "192.168.232.131"
#define LISTEN_BACKLOG 50
int main(int argc, char *argv[])
{
printf("please server ip addrss:\n");
char ser_ip[50] = {0};
scanf("%s",ser_ip);
printf("the input address is %s\n",ser_ip);
int sockfd = -1 ,clifd = -1; //这里就是先定义个client fd备用
int ret = -1;
//bind's needs
struct sockaddr_in sersock, clisock;
memset(&sersock,0,sizeof(struct sockaddr_in));
socklen_t clisock_size;
clisock_size = sizeof(struct sockaddr_in);
//sockaddr_in needs
//char cp[50] = SER_IP; //改了 这里之前是用宏定义的固定ip地址,后面改成自己输入
//send needs
//定义一个结构体,收发信息用的
struct information
{
int a;
char buf[50];
int stat;
}info;
memset(&info,0,sizeof(struct information));
//这里用到上节讲的inet_aton,当然,如果是ipv4的话用inet_addr更加简单。
struct in_addr inp;
inet_aton(ser_ip,&inp);
//socket
sockfd = socket(AF_INET,SOCK_STREAM,0);
if (-1 == sockfd)
{
perror("socket");
return -1;
}
printf("sockfd is %d\n",sockfd);
//bind
sersock.sin_family = AF_INET; //选ipv4模式
sersock.sin_port = htons(SER_PORT); //用到host to net 把端口转成网络字节序
sersock.sin_addr = inp;
ret = bind(sockfd,(const struct sockaddr*)&sersock,sizeof(struct sockaddr_in));
if(-1 == ret)
{
perror("bind");
return -1;
}
printf("bind success bind_ret = %d\n",ret);
//listen
ret = listen(sockfd,LISTEN_BACKLOG);
if(-1 == ret)
{
perror("listen");
return -1;
}
printf("listen success listen_ret = %d\n",ret);
// accept
// accept可以看作是在服务器端做一个与客户端连接时用的匹配信息格式文本
// 就是类似做好一张表格,等待client填写
// 因为client也需要socket,然后connect。
// client的connect相当于server的bind,客户端要于服务器绑定(连接)
clifd = accept(sockfd,(struct sockaddr *)&clisock,&clisock_size);
if(-1 == clifd)
{
perror("accept");
return -1;
}
printf("accept success accept_ret = %d\n",clifd);
//send
while(1)
{
printf("send:\n");
printf("input number:\n");
scanf("%d", &info.a);
printf("input data\n");
scanf("%s", info.buf);
ret = send(clifd,&info,sizeof(struct information),0); //注意这里要clifd,不要只看man手册的sockfd ,因为是发给client,所以要用clifd
if (-1 == ret)
{
perror("send");
return -1;
}
printf("the number of bytes send is %d\n",ret);
recv(clifd,&info,sizeof(struct information),0);
if(0 == info.stat)
{
printf("I know ,bye~\n");
return -1;
}//如果只是一次收发可以放这里,不然的话就会一直阻塞在recv这里等待了
memset(&info,0,sizeof(struct information));
}
return 0;
}
最后的收发存在一点项目逻辑问题的,如果收发的时候不是第一次收到对的信息就会一直阻塞在recv。
扫描二维码关注公众号,回复:
10605244 查看本文章
客户端
客户端的代码和服务器的是非常相似的,也需要socket得到自己的sockfd,但是接着只需要connect即可。同样,收发部分的逻辑有点问题,虽然能运行。。。。
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#define SER_PORT 7777
#define SER_IP "192.168.232.131"
#define LISTEN_BACKLOG 50
int main(int argc, char *argv[])
{
printf("please server ip addrss:\n");
char ser_ip[50] = {0};
scanf("%s",ser_ip);
printf("the input address is %s\n",ser_ip);
int sockfd = -1 ,clifd = -1;;
int ret = -1;
//bind's needs
struct sockaddr_in clisock;
memset(&clisock,0,sizeof(struct sockaddr_in));
socklen_t clisock_size;
clisock_size = sizeof(struct sockaddr_in);
//sockaddr_in needs
//char cp[50] = SER_IP;
struct in_addr inp;
inet_aton(ser_ip,&inp);
//recv needs
struct information
{
int a;
char buf[50];
int stat;
}info;
memset(&info,0,sizeof(struct information));
//socket
sockfd = socket(AF_INET,SOCK_STREAM,0);
if (-1 == sockfd)
{
perror("socket");
return -1;
}
printf("sockfd is %d\n",sockfd);
//connect
clisock.sin_family = AF_INET;
clisock.sin_port = htons(SER_PORT);
clisock.sin_addr = inp;
ret = connect(sockfd,(const struct sockaddr*)&clisock,sizeof(struct sockaddr_in));
if(-1 == ret)
{
perror("connect");
return -1;
}
printf("connect success connect_ret = %d\n",ret);
//recv
while(1)
{
ret = recv(sockfd,&info,sizeof(struct information),0);
if (-1 == ret)
{
perror("recv");
return -1;
}
printf("the number of bytes send is %d\n",ret);
printf("receive:number = [%d]\n",info.a);
printf("receive:data = [%s]\n",info.buf);
if(1 == info.a)
{
printf("done\n");
info.stat = 0;
send(sockfd,&info,sizeof(struct information),0);
return -1;
}
memset(&info,0,sizeof(struct information));
}
return 0;
}
这里的代码很多都借鉴了man bind里面的例子,再次强调一下,真的比自己写的代码好多了。