【一个简单的socket客户端和服务端的例子】网络编程中最基础的就是socket的操作,这里记录一下socket的基础操作有哪些,分别是什么作用,最后以一个简单的客户端和服务端例子收尾。
socket是什么?
socket起源于Unix,秉承着一切皆文件的思想,socket也是打开 -读写- 关闭 这样的模式的一个实现。socket用于不同主机间进程的通信,而每个进程由 所使用的协议,Ip,端口号,三者决定,有兴趣的可以百度一下 多路分解和多路复用。
让我们看一下socket在TCP/IP中所处的位置
文章图片
可以看到,socket位于应用层与传输层之间,作为一层抽象层把tcp/IP的复杂操作抽象成了一些简单的接口供应用层来调用。
socket的基本操作
主要介绍如下几个函数
int socket(int domain , int type, int protocol)
该函数创建一个socket套接字,返回一个socket描述符,相当于对该套接字的引用,在接下来的函数中将会用到它。 我们来看看各个参数,这里只介绍一些楼主使用过的
domain : 协议族,常用的就是AF_INET,AF_INET6,AF_LOCAL等几种(还有不少),它最关键的作用是指定了地址格式,比如说AF_INET(事实上楼主至今也只用了它..),它决定了该socket套接字的地址必须使用 32位的ipv4地址和16位的port号
type :类型,当然指的就是创建的socket类型了,常见的有如下两种,SOCK_STREAM,SOCK_DGRAM(还有不少),分别对应TCP套接字和UDP套接字,至于tcp和udp协议,大家可以去看一下我之前的博文关于tcp和udp的一些事。
protocol: 协议,有IPPROTO_TCP,IPPROTO_UDP等几种(还有不少),显然就是对应于传输层TCP和UDP协议,这里我经常用0,当传入0的时候,会使用socket类型对应的默认传输协议
int bind(int sockfd, const struct sockaddr* addr, socklen_t addrlen)
该函数用于给socket绑定一个地址,之前说过了不同的协议族有对应不同的地址,我们以AF_INET,即ipv4+port来解释,先看看sockaddr结构体有哪些成员
struct sockaddr {
sa_family_tsa_family;
/* address family, AF_xxx*/
charsa_data[14];
/* 14 bytes of protocol address */
}共16个字节
但是我们传入的参数一般是 sockaddr_in结构体,我们来看看sockaddr_in结构体的成员有哪些
struct sockaddr_in
{
__SOCKADDR_COMMON (sin_);
in_port_t sin_port;
/* Port number. */
struct in_addr sin_addr;
/* Internet address. */
/* Pad to size of `struct sockaddr'. */
unsigned char sin_zero[sizeof (struct sockaddr) -
__SOCKADDR_COMMON_SIZE -
sizeof (in_port_t) -
sizeof (struct in_addr)];
/* 字符数组sin_zero[8]的存在是为了保证结构体struct sockaddr_in的大小和结构体struct sockaddr的大小相等 */
};
其实也就是方便我们使用,可以直接指定ip和port(当然这里有主机字节序和网络字节序的问题),而不需要去操作字符串了
int listen(int sockfd,int backlog)
该函数在服务端编程会用到,用于监听某个端口,至于是哪个就看你传入进去的sockfd设置的是哪一个了,backlog参数用于指定等待连接建立的socket队列的最大长度,成功返回0,失败为-1.
int accept(int sockfd, struct sockaddr* addr,socklen_t addrlen)
该函数返回与客户端建立连接的socket描述符,注意这跟监听socket不同,这是新建的一个用于与客户端通信的socket。该函数会一直阻塞到接收到一个连接为止。
第一个参数是监听socket的描述符,后面2个参数用于接受客户端的地址和地址长度。
上面2个函数都是在服务端编程中用于监听端口,被动创建与客户端的连接时,需要用到的,下面这个函数一般在客户端中需要用到的用于主动创建连接的
int connect(int sockfd,const struct sockaddr* addr,socklen_t addrlen )
第一个参数是客户端的socket描述符,第二个参数是服务器的地址,第三个当然还是地址长度,成功的时候返回0,失败则返回-1,该函数在服务器accept后,数据到达时会返回,具体连接过程,可以百度三次握手,也可以看看我写过的tcp的一些事。
OK,连接已经建立好了,现在要进行通讯了,
常用的函数有如下4个
int read(int sockfd,void* buf ,ssize_t count)
int write(int sockfd,void* buf,ssize_t count)
int recv(int sockfd,void* buf,ssize_t count,int flags)
int send(int sockfd,void* buf,ssize_t count,int flags)
这4个函数如何使用,请参照这篇文章http://blog.csdn.net/u011408355/article/details/45921541
接下来展示一个简单的例子,服务端监听端口,客户端向服务端发起连接,建立连接后客户端发送一句“Client”,服务端接收到后返回一句“Server”。
服务端:
#include
#include
#include
#include
#include
#include
#include
#include
#define PORT 6888
int main()
{
int listenfd,conndfd;
struct sockaddr_inserver;
char buf[2048+1];
if(-1==(listenfd=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP)))
printf("Create Socket Error\n");
memset(&server,0,sizeof(server));
server.sin_family=AF_INET;
server.sin_addr.s_addr=inet_addr("127.0.0.1");
server.sin_port=htons(PORT);
if(-1==bind(listenfd,(struct sockaddr*)&server,sizeof(server)))
printf("Bind Error\n");
printf("Start Listen\n");
if( -1==listen(listenfd,5)){
printf("Listen Error\n");
exit(0);
}
while(1)
{
if( -1==(conndfd=accept(listenfd,NULL,NULL) ))
{
printf("Accept Error\n");
continue;
}
memset(buf,0,sizeof(buf));
int n=recv(conndfd,buf,2048,0);
printf("Recv msg form cilent, %d byte\n",n);
printf("%s\n",buf);
memset(buf,0,sizeof(buf));
strcpy(buf,"Server");
send(conndfd,buf,strlen(buf),0);
close(conndfd);
}
close(listenfd);
return 0;
}
客户端:
#include
#include
#include
#include
#include
#include
#include
#include
#define PORT 6888
int main()
{
int conndfd;
struct sockaddr_in serverAddr;
char buf[2048+1];
if(-1==(conndfd=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP)))
printf("Create Socket Error\n");
memset(&serverAddr,0,sizeof(serverAddr));
serverAddr.sin_family=AF_INET;
serverAddr.sin_addr.s_addr=inet_addr("127.0.0.1");
serverAddr.sin_port=htons(PORT);
if(-1==connect(conndfd,(struct sockaddr*)&serverAddr,sizeof(serverAddr)))
{
printf("Connetc Error\n");
exit(0);
}
printf("Connect Success.Lets communicate!\n");
strcpy(buf,"Client");
int n=send(conndfd,buf,strlen(buf),0);
printf("Send %d byte to Server\n",n);
memset(buf,0,sizeof(buf));
n=recv(conndfd,buf,2048,0);
printf("Recv %d byte from Server\n",n);
printf("%s\n",buf);
close(conndfd);
return 0;
}
欢迎大家指正
推荐阅读
- linux学习|ubuntu中root和普通用户切换
- ubuntu18.04之安装vim
- CentOS 7 设置批量后台运行程序,并开机自启动
- Linux学习|CentOS 7 安装Golang
- Linux学习|每天一个小技巧———————如何修改超级用户密码
- Linux学习|PXE网络安装已经无人值守安装教程
- Linux学习|每天一个小技巧---------如何搭建自己的Linux的yum源环境
- 命令|工欲善其事必先利其器----浅谈RPM包管理工具
- Linux学习|每天 一个小知识-----如何让你的Linux系统永久登录
- Linux系统分区概念