Socket编程实践(2) Socket API 与 简单例程

Socket编程实践(2)  Socket API 与 简单例程

在本篇文章中,先介绍一下Socket编程的一些API,然后利用这些API实现一个客户端-服务器模型的一个简单通信例程。该例子中,服务器接收到客户端的信息后,将信息重新发送给客户端。

socket()函数socket()函数用于创建一个套接字。这就好像购买了一个电话。不过该电话还没有分配号码。

代码语言:javascript复制 #include

#include

int socket(int domain, int type, int protocol)参数说明:

domain:指定通信的协议族,这些协议族定义在头文件< sys/socket.h >中。使用IPV4协议族时,该参数设置为AF_INET。type :指定socket的类型。在上一篇文章中介绍过,套接字常用的有三种类型:流式套接字SOCK_STREAM,数据报套接字SOCK_DGRAM,原始套接字SOCK_RAW。protocol : 该参数指定了一种协议类型用于所选择的套接字。如果仅有一种协议支持某种套接字类型,那么该参数可以定义为0,此时使用默认协议;如果一种套接字类型可能有多种协议类型,那么必须显式指定协议类型。关于具体细节,可以man socket进行查阅。socket()的返回值:成功时返回非负整数;失败时返回-1;

bind() 函数bind()函数绑定一个本地地址到套接字上,这相当于为电话绑定了号码。当一个套接字通过socket()被创建,它并没有绑定到具体的地址上,bind()来完成这个步骤。 bind()函数的函数原型如下:

代码语言:javascript复制 #include

#include

int bind(int sockfd, const struct sockaddr *addr,

socklen_t addrlen);参数说明:

sockfd:socket()函数创建后成功返回的套接字addr : 需要绑定的地址addrlen:套接字的大小这里需要使用到sockaddr_in结构来表示一个地址,该结构如下:

代码语言:javascript复制struct sockaddr_in

{

sa_family_t sin_family;

in_port_t sin_port;

struct in_addr sin_addr;

};

struct in_addr

{

uint32_t s_addr;

}sockaddr_in需要强制转换为struct sockaddr*类型,传递给bind()函数的第二个参数。下面是一段例程:

代码语言:javascript复制int main()

{

int listenfd = socket(AF_INET,SOCK_STREAM,0);

if(listenfd == -1)

err_exit("socket error");

struct sockaddr_in addr;

//填充结构

addr.sin_family = AF_INET;

addr.sin_port= htons(8001); //主机字节序转换为网络字节序

addr.sin_addr= htonl(INADDR_ANY);//绑定主机的任一个IP地址

/*下面两句具有相同的功能:都是绑定到本机ip地址*/

//inet_aton("127.0.0.1",&addr.sin_addr);

//addr.sin_addr.s_addr = inet_addr("127.0.0.1");

if(bind(listenfd,(const struct sockaddr*)&addr,sizeof(addr))==-1)

err_exit("bind error");

}listen()函数当使用socket()创建了一个套接字时,该套接字默认是主动套接字。使用listen()函数会使套接字称为一个被动套接字,也就是说,该套接字将被用来接受连接的数据,这些数据通过accept()函数接收。

listen()函数的函数原型如下:

代码语言:javascript复制 #include

#include

int listen(int sockfd, int backlog);参数说明:

sockfd : 套接字。backlog: 指定连接队列的长度。对于给定的监听套接字,内核需要维护两个队列:

已完成连接队列:该队列中的连接处于ESTABLISHED状态,也即是已经完成了三次握手过程。未完成连接队列:该队列中的连接处于SYN_RCVD状态,还未建立连接。两个队列的长度之和不能够超过backlogi。如果一个连接请求到达时未完成队列已满,客户端可能接收到一个错误指示ECONNREFUSED。服务器使用accept()函数从已完成连接队列的队头返回一个连接。下面是TCP为监听套接口维护的两个队列:

accept()函数accept()函数用于从已完成队列的队头返回一个连接。它的函数原型为:

代码语言:javascript复制#include

#include

int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);参数说明:

sockfd : 服务器套接字addr :用于接收对等方(客户端)的套接字地址。该参数填充为NULL时,不接收任何信息。addrlen:返回对等方的套接字地址长度。如果不关心可以设置为NULL,否则一定要初始化。函数返回值:成功返回一个非负整数,代表一个套接字;失败返回-1;

connect()函数该函数用于建立一个连接到指定的套接字。函数的原型为:

代码语言:javascript复制#include

#include

int connect(int sockfd, const struct sockaddr *addr,

socklen_t addrlen);参数说明:

sockfd : 未连接的套接字addr:未连接的套接字地址addrlen:addr的长度一个简单的socket 通信例程客户端代码:

代码语言:javascript复制#include

#include

#include

#include

#include

#include

#include

#include

#define ERR_EXIT(m)\

do \

{\

perror(m);\

exit(EXIT_FAILURE);\

}while(0)

int main()

{

/*创建一个套接字*/

int sock = socket(AF_INET,SOCK_STREAM,0);

if(sock == -1)

ERR_EXIT("socket");

/*定义一个地址结构*/

struct sockaddr_in servaddr;

memset(&servaddr,0,sizeof(servaddr));

servaddr.sin_family = AF_INET;

servaddr.sin_port = htons(5888);

servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");

/*进行连接*/

if(connect(sock,(struct sockaddr*)&servaddr,sizeof(servaddr))<0)

{

ERR_EXIT("connect");

}

else

{

printf("连接成功\n");

}

char sendbuf[1024]={0};

char recvbuf[1024]={0};

/*从标准输入中读入*/

while(fgets(sendbuf,sizeof(sendbuf),stdin)!=NULL)

{

write(sock ,sendbuf,strlen(sendbuf));

if(read (sock,recvbuf,sizeof(recvbuf))>0)

{

printf("从服务器接收信息:\n");

fputs(recvbuf,stdout);

}

memset(&sendbuf,0,sizeof(sendbuf));

memset(&recvbuf,0,sizeof(recvbuf));

}

close(sock);

return 0;

} 服务器端代码:

代码语言:javascript复制#include

#include

#include

#include

#include

#include

#include

#include

#define ERR_EXIT(m)\

do \

{\

perror(m);\

exit(EXIT_FAILURE);\

}while(0)

int main()

{

/* 创建一个套接字*/

int listenfd= socket(AF_INET ,SOCK_STREAM,0);

if(listenfd==-1)

ERR_EXIT("socket");

/*定义一个地址结构并填充*/

struct sockaddr_in addr;

addr.sin_family = AF_INET; //协议族为ipv4

addr.sin_port = htons(5888); //绑定端口号

addr.sin_addr.s_addr = htonl(INADDR_ANY);//主机字节序转为网络字节序

/*将套接字绑定到地址上*/

if(bind(listenfd,(const struct sockaddr *)&addr ,sizeof(addr))==-1)

{

ERR_EXIT("bind");

}

/*监听套接字,成为被动套接字*/

if(listen(listenfd,SOMAXCONN)<0)

{

ERR_EXIT("Listen");

}

struct sockaddr_in peeraddr;

socklen_t peerlen = sizeof(peeraddr);

/*定义一个套接字,通常称为已连接套接字*/

int conn ;

conn = accept(listenfd,(struct sockaddr*)&peeraddr,&peerlen);

if(conn <0)

ERR_EXIT("accept error");

else

printf("连接到服务器的客户端的IP地址是:%s,端口号是:%d\n",inet_ntoa(peeraddr.sin_addr),htons(peeraddr.sin_port));

/*循环获取数据、发送数据*/

char recvbuf[1024];

while(1)

{

memset(recvbuf,0,sizeof(recvbuf));

int ret = read(conn,recvbuf ,sizeof(recvbuf));

fputs(recvbuf,stdout);

write(conn,recvbuf,sizeof(recvbuf));

}

/*关闭套接字*/

close(listenfd);

close(conn);

return 0;

}

相关推荐

现在红尘试炼的宝宝多少级可以领?
365bet官方下载

现在红尘试炼的宝宝多少级可以领?

⌛ 09-02 👁️ 7825
理财通一万元收益多少?微信理财通收益怎么算?
皇冠365bet体育投

理财通一万元收益多少?微信理财通收益怎么算?

⌛ 01-14 👁️ 9770
顶级浪漫动漫推荐:让你沉醉的爱情故事
365打水账号怎么防止封号

顶级浪漫动漫推荐:让你沉醉的爱情故事

⌛ 07-18 👁️ 561
洗完衣服发现衣服上有一个蛆怎么回事
365bet官方下载

洗完衣服发现衣服上有一个蛆怎么回事

⌛ 10-29 👁️ 7611
h文的意思
皇冠365bet体育投

h文的意思

⌛ 10-23 👁️ 9793
华为拦截骚扰电话全攻略:从设置到第三方软件及应用
365打水账号怎么防止封号

华为拦截骚扰电话全攻略:从设置到第三方软件及应用

⌛ 11-10 👁️ 2628
韩国整形外科诊所价格
365打水账号怎么防止封号

韩国整形外科诊所价格

⌛ 10-24 👁️ 3822
梦幻西游69法系多少灵力合格
皇冠365bet体育投

梦幻西游69法系多少灵力合格

⌛ 01-26 👁️ 9900
神武2宝宝图鉴 有神兽和不错变异宝宝
365打水账号怎么防止封号

神武2宝宝图鉴 有神兽和不错变异宝宝

⌛ 09-08 👁️ 2709