Socket编程实践(2) Socket API 与 简单例程
在本篇文章中,先介绍一下Socket编程的一些API,然后利用这些API实现一个客户端-服务器模型的一个简单通信例程。该例子中,服务器接收到客户端的信息后,将信息重新发送给客户端。
socket()函数
socket()函数用于创建一个套接字。这就好像购买了一个电话。不过该电话还没有分配号码。
#include <sys/types.h>
#include <sys/socket.h>
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()函数的函数原型如下:
#include <sys/types.h>
#include <sys/socket.h>
int bind(int sockfd, const struct sockaddr *addr,
socklen_t addrlen);
参数说明:
sockfd:socket()函数创建后成功返回的套接字
addr : 需要绑定的地址
addrlen:套接字的大小
这里需要使用到sockaddr_in结构来表示一个地址,该结构如下:
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()函数的第二个参数。下面是一段例程:
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()函数的函数原型如下:
#include <sys/types.h>
#include <sys/socket.h>
int listen(int sockfd, int backlog);
参数说明:
sockfd : 套接字。
backlog: 指定连接队列的长度。
对于给定的监听套接字,内核需要维护两个队列:
已完成连接队列:该队列中的连接处于ESTABLISHED状态,也即是已经完成了三次握手过程。
未完成连接队列:该队列中的连接处于SYN_RCVD状态,还未建立连接。
两个队列的长度之和不能够超过backlogi。如果一个连接请求到达时未完成队列已满,客户端可能接收到一个错误指示ECONNREFUSED。服务器使用accept()函数从已完成连接队列的队头返回一个连接。下面是TCP为监听套接口维护的两个队列:
accept()函数
accept()函数用于从已完成队列的队头返回一个连接。它的函数原型为:
#include <sys/types.h>
#include <sys/socket.h>
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
参数说明:
sockfd : 服务器套接字
addr :用于接收对等方(客户端)的套接字地址。该参数填充为NULL时,不接收任何信息。
addrlen:返回对等方的套接字地址长度。如果不关心可以设置为NULL,否则一定要初始化。
函数返回值:成功返回一个非负整数,代表一个套接字;失败返回-1;
connect()函数
该函数用于建立一个连接到指定的套接字。函数的原型为:
#include <sys/types.h>
#include <sys/socket.h>
int connect(int sockfd, const struct sockaddr *addr,
socklen_t addrlen);
参数说明:
sockfd : 未连接的套接字
addr:未连接的套接字地址
addrlen:addr的长度
一个简单的socket 通信例程
客户端代码:
#include<stdio.h>
#include<stdlib.h>
#include<errno.h>
#include<arpa/inet.h>
#include<netinet/in.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<string.h>
#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;
}
服务器端代码:
#include<stdio.h>
#include<stdlib.h>
#include<errno.h>
#include<arpa/inet.h>
#include<netinet/in.h>
#include <sys/types.h>
#include <sys/socket.h>
#include<string.h>
#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;
}
Socket编程实践(2) Socket API 与 简单例程的更多相关文章
- Socket编程实践(3) --Socket API
socket函数 #include <sys/types.h> #include <sys/socket.h> int socket(int domain, int type, ...
- Socket编程实践(2) --Socket编程导引
什么是Socket? Socket可以看成是用户进程与内核网络协议栈的接口(编程接口, 如下图所示), 其不仅可以用于本机进程间通信,可以用于网络上不同主机的进程间通信, 甚至还可以用于异构系统之间的 ...
- C# socket编程实践
C# socket编程实践——支持广播的简单socket服务器 在上篇博客简单理解socket写完之后我就希望写出一个websocket的服务器了,但是一路困难重重,还是从基础开始吧,先搞定C# ...
- Socket编程实践(6) --TCP服务端注意事项
僵尸进程处理 1)通过忽略SIGCHLD信号,避免僵尸进程 在server端代码中添加 signal(SIGCHLD, SIG_IGN); 2)通过wait/waitpid方法,解决僵尸进程 sign ...
- Socket编程实践(6) --TCPNotes服务器
僵尸进程过程 1)通过忽略SIGCHLD信号,避免僵尸进程 在server端代码中加入 signal(SIGCHLD, SIG_IGN); 2)通过wait/waitpid方法.解决僵尸进程 sign ...
- Socket编程实践(10) --select的限制与poll的使用
select的限制 用select实现的并发服务器,能达到的并发数一般受两方面限制: 1)一个进程能打开的最大文件描述符限制.这可以通过调整内核参数.可以通过ulimit -n(number)来调整或 ...
- C# socket编程实践——支持广播的简单socket服务器
在上篇博客简单理解socket写完之后我就希望写出一个websocket的服务器了,但是一路困难重重,还是从基础开始吧,先搞定C# socket编程基本知识,写一个支持广播的简单server/clie ...
- Socket编程实践(1) 基本概念
1. 什么是socket socket可以看成是用户进程与内核网络协议栈的编程接口.TCP/IP协议的底层部分已经被内核实现了,而应用层是用户需要实现的,这部分程序工作在用户空间.用户空间的程序需要通 ...
- C# Socket编程 笔记,Socket 详解,入门简单
目录 一,网络基础 二,Socket 对象 三,Bind() 绑定与 Connect() 连接 四,Listen() 监听请求连接 和 Accept() 接收连接请求 五,Receive() 与 Se ...
随机推荐
- SVG 文本
该部分为四个主要部分: 1. <text>和<tspan>标签详解 2. 文本水平垂直居中问题 3. <textpath>让文本在指定路径上排列 4 ...
- JS eval()函数的一些见解
一.eval是基本使用规则 1 eval() 函数可计算某个字符串,并执行其中的的 JavaScript 代码. 2 eval(string) 3 string必需.要计算的字符串,其中含有要计算的 ...
- mvc mvp mvvm模式的区别
mvc模式中,Model不依赖于View,但是View是依赖于Model的,m和v没有进行完全的分离,三者之间是单向的操作 mvp模式中,m和v之间的交互是双向的,m和v完全分离,m和v的交互是通过P ...
- win 10 安装软件 报错发布者不受信任
1:打开任务管理器, [运行新任务] 2:(勾上以系统管理员权限创建此任务) 输入 cmd 3:进入要安装的软件所在的目录:cd D:\111_安装包\submit 3 (本文以安装submit 为 ...
- VmWare平台Windows Server 2012 无响应宕机
我们生产服务器都部署在VMware ESXi 5.5平台上,最近大半年的时间,偶尔就会出现操作系统为Windows Servre 2012的服务器出现没有任何响应(unresponsive)的情况,出 ...
- Oracle组合索引与回表
回表 简单来说就是数据库根据索引找到了指定的记录所在行后,还需要根据rowid再次到数据块里取数据的操作. "回表"一般就是指执行计划里显示的"TABLE ACCESS ...
- Hive-0.x.x - Enviornment Setup
All Hadoop sub-projects such as Hive, Pig, and HBase support Linux operating system. Therefore, you ...
- postgresql 导出数据字典文档
项目上需要整理目前数据库的数据字典文档.项目不规范,这种文档只要后期来补.这么多张表,每个字段都写到word文档里真心头大.就算前面写了个查询表结构的sql,但是最后整理到word里还是感觉有点麻烦. ...
- centos本地yum源安装
1.为DVD或U盘创建一个用于挂载的目录 [root@localhost ~]# mkdir /media/CentOS/ 2.查看DVD或U盘所在的路径 [root@localhost ~]# fd ...
- Would Your Work Habits Change if You Were Paid by the Job?
原文地址:http://success-sys.com/2016/09/26/would-your-work-habits-change-if-you-were-paid-by-the-job/ A ...