Linux网络编程

1、网络协议

1.TCP/UDP
①TCP面向连接需要连接,而UDP不需要连接
②tcp可靠,传输数据无差错,不丢失,不重复,且按序到达,udp最大努力交付,即不保证可靠。
③tcp面向字节流,udp报文,udp没有拥塞控制,因此网络出现拥塞不会使源主机的发送速率降低(对事实应用很有用,如IP电话,视频会议)
④tcp连接点到点,udp支持一对一,一对多,多对一,多对多的交互通信
⑤tcp首部开销20字节,udp8字节
⑥tcp逻辑通信信道是全双工的可靠信道,udp是不可靠的
2.端口号作用
①一台拥有IP地址的主机可以提供许多服务②IP地址与网络服务关系是一对多
③IP+端口号区分不同的服务
④服务器一般都是通过知名端口号来识别的。例:对于每个tcp/ip实现来说,FTP服务器tcp端口号是21。Linux应用层一般都是5000-10000之间
3.字节序
Little endian 小端字节序(低字节存储在起始地址)
Big endian 大端字节序
网络字节序=大端字节序
4.socket编程步骤
服务器
①创建套接字(通道)
socket()
②添加信息(IP,端口号)
bind()
③监听网络连接
listen()
④监听到客户端接入,接受
accept()
⑤数据交互
read()
write()
⑥关闭套接字,断开连接
close()
客户端
①发起连接
connect()
②数据交互
③断开连接

2、API

2.1创建套接字socket

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

返回值:网络描述符
参数1:所使用的协议族,通常使用AF_TNET,表示IPv4因特网域
参数2:指定socket类型
SOCK_STREAM:TCP
SOCK_DGRAM:UDP
SOCK_RAW:直接访问IP或ICMP,通常用于协议开发
参数3:通常0,选择tpye类型对应的默认协议

2.2绑定IP和端口号bind

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

用于绑定IP地址和端口号到socketfd
参数1:网络描述符
参数2:包含IP和端口号的结构体指针
参数3:参数2占用字节个数sizeof
补充:
参数2结构体通常用struct sockaddr_in:

struct sockaddr_in {
  __kernel_sa_family_t  sin_family;     /* Address family               */
  __be16                sin_port;       /* Port number                  */
  struct in_addr        sin_addr;       /* Internet address             */

  /* Pad to size of `struct sockaddr'. */
  unsigned char         __pad[__SOCK_SIZE__ - sizeof(short int) -
                        sizeof(unsigned short int) - sizeof(struct in_addr)];
};

struct in_addr {
        __be32  s_addr;
};

成员1:协议族
成员2:端口号(要转换网络字节序)
成员3:IP地址结构体
成员4:没有实际意义,只为跟sockaddr内存对齐,这样才能转换,不需要配置

2.3地址转换API

int inet_aton(const char* straddr,struct in_addr* addrp)

把字符串形式192.168.1.111转换为网络格式
char* inet_ntoa(struct in_addr inaddr)
把网络形式的IP转换为字符串形式
注:头文件
在这里插入图片描述

2.4监听listen

int listen(int sockfd,int backlog)

设置处理最大连接数,只用于服务器端。
参数1:网络描述符
参数2:最大连接数

2.5连接accept

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

返回值:新的套接字描述符,用于读写操作
参数1:网络描述符
参数2:客户端的地址结构体
参数3:参数2结构体的长度(变量int)

2.6数据收发co

read()
write()

第二套API

ssize_t send(int s,const void* msg,ssize_t len,int flags)

最后一个参数设置阻塞,一般0

ssize_t recv(int s,void* buf,ssize_t len,int flags)

2.7客户机连接主机connect

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

返回值:失败返回-1,errno看错误代码
参数1:服务器端的网络描述符(socket
参数2:服务端的IP和端口号结构体指针
参数3:参数2结构体的内存大小sizeof

2.8字节转换API

返回网络字节序(大端字节序)

uint16_t htons(uint16_t host16bitvalue)
uint32_t htonl(uint32_t host32bitvalue)

返回主机字节序

uint16_t ntohs(uint16_t net16bitvalue)
uint32_t ntohl(uint32_t net32bitvalue)

h代表host,n代表net,s代表short(2字节),l代表long(4字节),INADDR_ANY指定地址让操作系统自己获取

3、例子

双方收发消息
1.服务器等待连接
2.客户端连接服务器
3.双方互发消息
注:可以多个客户端连接,但会出错,因为进程资源竞争
客户端代码

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <stdlib.h>
int main(int argc,char **argv)
{
        if(argc != 3)
        {
                printf("parameter error\n");
                exit(-1);
        }
        int s_fd;
        int r_read;
        char msg[128];
        char read_buf[128];
        struct sockaddr_in s_addr;
        memset(&s_addr,0,sizeof(struct sockaddr_in));

        s_fd = socket(AF_INET,SOCK_STREAM,0);
        if(s_fd == -1)
        {
                perror("socket");
                exit(-1);
        }
        s_addr.sin_family = AF_INET;
        s_addr.sin_port = htons(atoi(argv[2]));
        inet_aton(argv[1],&s_addr.sin_addr);
        /*bind(s_fd,(struct sockaddr *)&s_addr,sizeof(struct sockaddr_in));*/
        //客户端不需要bind
        if(connect(s_fd,(struct sockaddr *)&s_addr,sizeof(struct sockaddr_in)) == -1)
        {
                perror("connect");
                exit(-1);
        }
        while(1)
        {
                if(fork() == 0)
                {
                        while(1)
                        {
                                memset(&read_buf,0,sizeof(read_buf));
                                r_read = read(s_fd,read_buf,128);
                                printf("read:%d,buf:%s\n",r_read,read_buf);
                        }
                }
                while(1)
                {
                        memset(&msg,0,sizeof(msg));
                        printf("input: ");
                        gets(msg);
                        write(s_fd,msg,strlen(msg));
                }
        }
        close(s_fd);
        return 0;
}

服务器代码

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <stdlib.h>
int main(int argc,char **argv)
{
        if(argc != 3)
        {
                printf("parameter error\n");
                exit(-1);
        }
        int s_fd;
        int x_fd;
        int r_read;
        char msg[128];
        char read_buf[128];
        struct sockaddr_in s_addr;
        struct sockaddr_in x_addr;
        memset(&s_addr,0,sizeof(struct sockaddr_in));
        memset(&x_addr,0,sizeof(struct sockaddr_in));
        s_fd = socket(AF_INET,SOCK_STREAM,0);
        if(s_fd == -1)
        {
                perror("socket");
                exit(-1);
        }
        s_addr.sin_family = AF_INET;
        s_addr.sin_port = htons(atoi(argv[2]));
        inet_aton(argv[1],&s_addr.sin_addr);
        bind(s_fd,(struct sockaddr *)&s_addr,sizeof(struct sockaddr_in));
        listen(s_fd,10);
        int changdu = sizeof(struct sockaddr_in);
        while(1)
        {
                x_fd = accept(s_fd,(struct sockaddr *)&x_addr,&changdu);//这里必须要变量
                printf("client IP:%s\n",inet_ntoa(x_addr.sin_addr));
                if(fork() == 0)
                {
                        if(fork() == 0)
                        {
                                while(1)
                                {
                                        memset(&msg,0,sizeof(msg));
                                        printf("input: ");
                                        gets(msg);
                                        write(x_fd,msg,strlen(msg));
                                }
                        }
                        while(1)
                        {
                                memset(&read_buf,0,sizeof(read_buf));
                                r_read = read(x_fd,read_buf,128);
                                printf("read:%d,buf:%s\n",r_read,read_buf);
                        }
                }
        }
        close(x_fd);
        return 0;
}

运行结果
在这里插入图片描述

4、多方收发(思路)

1.服务器作为中转来实现
2.例如:a发消息给b,服务器收到a消息转发给b
代码(服务器)
在这里插入图片描述

运行结果
在这里插入图片描述
师承上官可编程 —— 陈立臣

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

dz小伟

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值