socket开发步骤及相关API介绍

socket服务器和客户端的开发步骤

TCP服务端:

  1. 创建套接字socket
  2. 为套接字添加信息(IP地址和端口号)bind
  3. 监听网络连接listen
  4. 监听到由客户端接入,接受一个连接accept
  5. 数据交互read、write
  6. 关闭套接字,断开连接close

TCP客户端:

  1. 创建套接字socket
  2. 知道IP地址端口号与服务端连接connect
  3. 数据交互read、write
  4. 关闭套接字,断开连接close

创建套接字socket

函数原型:

int socket(int domain,int type,int protocol)

参数:

1**.domain:**指明所使用的协议,通常为AF_INEF,表示互联网协议族(TCP/IP协议族)

  • AF_INET --- IPv4因特网域
  • AF_INET6 --- IPv6因特网域
  • AF_UNIX --- Unix域
  • AF_ROUTE --- 路由套接字
  • AF_KEY --- 密钥套接字
  • AF_UNSPEC --- 未指定

2**.type:**指定socket的类型

  • SOCK_STREAM:流式套接字,面向连接的通信流,它使用TCP协议,从而保证了数据传输的正确性和顺序性。
  • SOCK_DGRAM:数据报套接字定义了一种无连接的服务,数据通过相互独立的报文进行传输,是无序的,并且不保证是可靠、无差错的。它使用数据协议UDP。
  • SOCK_RAM:允许程序使用低层协议,原始套接字允许对底层协议如IP或ICMP进行直接访问,功能强大但使用较为不便,主要用于一些协议的开发。

3.**protocol:**通常赋值为0

  • 0选择type类型对应的默认协议
  • IPPROTO_TCP --- TCP传输协议
  • IPPROTO_UDP --- UDP传输协议
  • IPPROTO_SCTP --- SCTP传输协议
  • IPPROTO_TIPC --- TIPC传输协议

返回值:

成功返回socket套接字描述符,失败返回-1

套接字添加信息bind

用于绑定IP地址和端口号到socketfd

函数原型:

int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);

参数:

  1. int sockfd:socket描述符
  2. const struct sockaddr *addr:是一个执行包含有本机IP地址及端口号等信息的socket类型的指针,指向要绑定给sockfd的协议地址结构,这个地址结构根据地址创建socket时的地址协议族的不同而不同。
  3. socklen_t addrlen:第二个参数结构体的长度

man手册给的第二个参数是:

但是一般写成如下形式,再强转为struct sockaddr *形式

例如如下写法:

监听网络连接listen

listen()的功能:

  • 设置能处理的最大连接数,listen()并未开始接受连线,只是设置socket的listen模式,listen函数只用于服务器端,服务器进程不知道要与谁连接,因此它不会主动的要求与某个进程连接,只是一直监听是否有其他客户进程与之链接,然后响应该连接请求,并对它做出处理,一个服务进程可以同时处理多个客户进程的连接。主要就两个功能:将一个未连接的套接字转换未一个被动套接字(监听),规定内核为套接字排队的最大连接数。
  • 内核为任何一个给监听套接字维护两个队列
  1. 未完成连接队列,每个这样的SYN报文段对应其中一项:已由某个客户端发出并到达服务器,而服务器正在等待完成响应的TCP三次握手过程,这些套接字处于SYN_REVD状态。
  2. 已完成连接队列。每个已完成TCP三次握手过程的客户端对应其中一项,这些套接字处于ESTABLISHED状态。

函数原型:

int listen(int sockfd, int backlog);

参数:

  1. int sockfd:是socket系统调用返回的服务器端socket描述符
  2. int backlog:指定在请求队列中允许的最大请求数

返回值:

成功返回0,失败返回-1,并且errno中包含相应的错误码

客户端连接服务器connect

功能:

用于绑定之后的client端,与服务器建立连接

函数原型:

int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);

参数:

  1. int sockfd:socket系统调用返回的服务器端socket描述符
  2. struct sockaddr *addr:用来返回已连接的对端(客户端)的协议地址
  3. socklen_t *addrlen:客户端地址的长度

返回

成功返回0,失败返回-1,并且errno中包含相应的错误码

接受连接accept

功能:

accept函数由TCP服务器调用,用于从已经完成连接队列队头返回下一个已完成连接,如果已完成连接队列为空,那么进程之间进入睡眠。

函数原型:

int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

参数:

  1. int sockfd:socket系统调用返回的服务器端socket描述符
  2. struct sockaddr *addr:用来返回已连接的对端(客户端)的协议地址
  3. socklen_t *addrlen:客户端地址的长度

返回值:

**该函数的返回值是一个新的套接字描述符,返回值是表示已连接的套接字描述符,**而第一个参数是服务器监听套接字描述都,一个服务器通常仅仅创建一个监听套接字,它在服务器的生命周期内一直存在。内核为每个由服务器进程接受的客户端连接创建一个已连接套接字(表示TCP三次握手协议已完成),当服务器完成对某个给定客户的服务时,响应的已连接套接字就会被关闭。

数据交互read、write

与文件编程中使用的read、write是一模一样的,不过多赘述

此外还要另外两种数据收发API:

  1. ssize_t send(int sockfd, const void *buf, size_t len, int flags);
  2. ssize_t recv(int sockfd, void *buf, size_t len, int flags);

其实和read、write也是差不多的,只不过多了一个flag参数,flag表示控制选项,一般设置为0。

字节序转换API

#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);返回主机字节序的值uint32_t

h代表host,n代表net,s代表short(两个字节),l代表long(四个字节),通过上面4个函数可以表示主机字节序和网络字节序之间的转换。有时可以用INADDR_ANY , INADDR_ANY指定地址让操作系统自己获取。

地址转换API

一般用下面黑体标出的两个

#include <sys/socket.h>

#include <netinet/in.h>

#include <arpa/inet.h>

int inet_aton(const char *cp, struct in_addr *inp);把字符串形式的192.168.1.123转为网络能识别的格式

in_addr_t inet_addr(const char *cp);

in_addr_t inet_network(const char *cp);

char *inet_ntoa(struct in_addr in);把网络格式的ip地址转为字符串形式

struct in_addr inet_makeaddr(int net, int host);

in_addr_t inet_lnaof(struct in_addr in);

in_addr_t inet_netof(struct in_addr in);

示例一:

服务端:连接客户端并读取客户端IP、发送的信息并返回读取状态。

cpp 复制代码
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
//#include <linux/in.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <string.h>

int main()
{
        int s_fd;
        char readBuf[128];
        memset(readBuf,0,sizeof(readBuf));
        int nread=0;
        struct sockaddr_in s_addr;
        struct sockaddr_in c_addr;

        memset(&s_addr,0,sizeof(struct aockaddr_in *));
        memset(&s_addr,0,sizeof(struct aockaddr_in *));

        s_fd=socket(AF_INET,SOCK_STREAM,0);

        if(s_fd==-1)
        {
                printf("creat soclet failed\n");
                perror("socket");
                exit(-1);
        }

        s_addr.sin_family=AF_INET;
        s_addr.sin_port=htons(9090);
        inet_aton("169.254.6.127",&(s_addr.sin_addr));

        bind(s_fd,(struct sockaddr *)&s_addr,sizeof(struct sockaddr_in));

        listen(s_fd,10);

        int clen=sizeof(struct sockaddr_in);
        int c_fd=accept(s_fd,(struct sockaddr *)&c_addr,&clen);

        if(c_fd == -1)
        {
                perror("acccept");
        }

        printf("get connect :%s\n",inet_ntoa(c_addr.sin_addr));

        nread=read(c_fd,readBuf,128);
        if(nread ==-1)
        {
                perror("read");
        }
        else if(nread>0)
        {
                printf("get message:%d,%s\n",nread,readBuf);
        }
        else
        {
                printf("client quit\n");
        }

         write(c_fd,"hhhhhhhhhhhhhhh",128);


        return 0;
}

示例二:

客户端:

cpp 复制代码
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
//#include <linux/in.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <string.h>
int main()
{

        int c_fd;
        char readBuf[128];
        memset(readBuf,0,sizeof(readBuf));
        int nread=0;
        struct sockaddr_in c_addr;

        memset(&c_addr,0,sizeof(struct aockaddr_in *));

        c_fd=socket(AF_INET,SOCK_STREAM,0);

        if(c_fd==-1)
        {
                printf("creat soclet failed\n");
                perror("socket");
                exit(-1);
        }
        c_addr.sin_family=AF_INET;
        c_addr.sin_port=htons(9090);
        inet_aton("169.254.6.127",&(c_addr.sin_addr));

        if(connect(c_fd,(struct sockaddr *)&c_addr,sizeof(struct sockaddr)) ==-1)

        {
                perror("connect");
                exit(-1);
        }


        write(c_fd,"xxxxxxxxx",128);

        nread=read(c_fd,readBuf,128);
        if(nread ==-1)
        {
                perror("read");
        }
        else
        {
                printf("get message from sever:%d,%s\n",nread,readBuf);
        }


        return 0;
}
相关推荐
ice___Cpu21 分钟前
Linux 基本使用和 web 程序部署 ( 8000 字 Linux 入门 )
linux·运维·前端
z2023050823 分钟前
linux 之0号进程、1号进程、2号进程
linux·运维·服务器
Happy鱿鱼1 小时前
C语言-数据结构 有向图拓扑排序TopologicalSort(邻接表存储)
c语言·开发语言·数据结构
KBDYD10101 小时前
C语言--结构体变量和数组的定义、初始化、赋值
c语言·开发语言·数据结构·算法
LWDlwd05251 小时前
shell指令及笔试题
c语言
狐心kitsune1 小时前
erlang学习:Linux常用命令1
linux·学习·erlang
Crossoads1 小时前
【数据结构】排序算法---桶排序
c语言·开发语言·数据结构·算法·排序算法
DREAM依旧2 小时前
《深入了解 Linux 操作系统》
linux
QXH2000002 小时前
数据结构—单链表
c语言·开发语言·数据结构
David猪大卫2 小时前
数据结构修炼——顺序表和链表的区别与联系
c语言·数据结构·学习·算法·leetcode·链表·蓝桥杯