linux网络编程篇-TCP UDP编程实战

OSI七层网络模型:
1.物理层:通过硬件设备将模拟信号转换为数字信号,于是有了0/1的数据流,叫做bit流
2.数据链路层:可以发比特流但是没有格式就会乱七八糟,于是就有了”帧”。采用了一种”帧”的数据块进行传输,为了确保数据通信的准确,实现数 据有效的差错控制,加入了检错等功能
3.网络层:前两层都是在于可以发数据,以及发的数据是否正确,然而如果连着两台电脑还行,多台电脑而又只想让其中一台可以通信,则需要路由 。选择性的发,那每台电脑就得有自己的身份,于是出现了IP协议等。
4.传输层:比特流传输的过程不可能会一直顺畅,偶尔出现中断很正常,如果人为制定出单位,分成一个个的信息段,从中又衍生了报文,结合上面几层,我们就可以有目标的发送正确数据给某台计算机了,传输层有两个重要的协议:TCP和UDP。TCP效率低但是发送包会校验是否完整,UDP效率高但是不管别人能否完整收到。
5.会话层:计算机收到了发送的数据,但是有那么多进程,具体哪个进程需要用到这个数据,则把他输送到那个进程。例如:如果80端口要用,所以系统内数据通信,将接收端口数据送至需求端口。
6.表示层:现在正确接收到了需要的数据,但是因为数据在传输过程中可能基于安全性,或者是算法上的压缩,还有就是网络类型不同。那就得有一个沟通的桥梁来整理整理,还原出原本应该有的表示,类似于一个拆快递的过程
7.应用层:是其他层对用户的已经封装好的接口,提供多种服务,用户只需操作应用层就可以得到服务内容,这样封装可以让更多的人能使用它。包含的主要协议:FTP(文件传送协议)、Telnet(远程登录协议)、DNS(域名解析协议)、SMTP(邮件传送协议),POP3协议(邮局协议),HTTP协议(Hyper Text Transfer Protocol)


socket编程:
TCP/UDP协议是工作在传输层的。
1.struct sockaddr,这个结构体是网络编程接口中用来表示一个IP地址的,注意这个IP地址是不区分IPv4和IPv6的(或者说是兼容IPv4和IPv6的)
2.typedef uint32_t in_addr_t; 网络内部用来表示IP地址的类型
3.两个很重要的结构体:struct sockaddr 和 struct sockaddr_in
4.struct sockaddr,这个结构体是网络编程接口中用来表示一个IP地址的,注意这个IP地址是不区分IPv4和IPv6的(或者说是兼容IPv4和IPv6的)
这个结构体是linux的网络编程接口中用来表示IP地址的标准结构体,bind、connect等函数中都需要这个结构体,这个结构体是兼容IPV4和IPV6的。在实际编程中这个结构体会被一个struct sockaddr_in或者一个struct sockaddr_in6所填充

struct in_addr
{
in_addr_t s_addr;
};
struct sockaddr
{
/地址族,就是一些协议类型的集合/
unsigned short sa_family;
/14 字节的协议地址,包含该 socket 的 IP 地址和端口号/
char sa_data[14];
};
struct sockaddr_in
{
short int sin_family; /地址族/
unsigned short int sin_port; /端口号/
struct in_addr sin_addr; /IP 地址/
unsigned char sin_zero[8];
};
5.sa_family 常见值如下:
AF_INET: IPv4 协议
AF_INET6: IPv6 协议
AF_LOCAL: UNIX 域协议
4.sudo tcpdump -iany -v tcp port 6003 tcp相关的调试工具


总结一下:
1.server端:
(1).创建socket
(2).bind
(3).listen
(4).accept: 这里会产生一个新的发送socket的fd
2.client端:
(1).socket
(2).connect

#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
int socket(int domain, int type, int protocol);

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

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

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


TCP编程实战
1.socket: 创建一个套接字
头文件:
#include <sys/types.h>
#include <sys/socket.h>
原型:
int socket(int domain, int type, int protocol);
第一个参数: domain 协议族(地址族)
AF_INET: IPv4网络通信
AF_INET6: IPv6网络通信
AF_PACKET: 链路层通信
AF_UNIX / AF_LOCAl: 本地通信
第二个参数:type soket的类型
SOCK_STREAM:字节流套接字, 流式套接字提供可靠的、面向连接的通信流;它使用 TCP 协议,从而保证了数据传输的正确性和顺序性
SOCK_DGRAM:数据包套接字,数据报套接字定义了一种无连接的服务,数据通过相互独立的报文进行传输,是无序的,并且不保证是可靠、无差错的。它使用的数据报协议是 UDP
SOCK_RAW:原始套接字,原始套接字允许对底层协议如 IP 或 ICMP 进行直接访问,它功能强大但使用较为不便,主要用于一些协议的开发
SOCK_SEQPACKET:有序分组套接字
第三个参数:protocol 传输协议,
IPPROTO_TCP: TCP传输协议
IPPTOTO_UDP: UDP传输协议
IPPROTO_SCTP: SCTP传输协议
IPPROTO_TIPCTCP: TIPC传输协议
使用实例:socket(AF_INET, SOCK_STREAM, 0); 最后的参数protocol默认给0的话,内核会把它修改为:IPPROTO_TCP

2.bind: 将套接字绑定一个IP地址和端口号
int bind(int sockfd, const struct sockaddr *addr,
socklen_t addrlen);
第一个参数:socket_fd
第二个参数:struct sockaddr 结构体指针,我们一般填充的是struct sockaddr_in,然后再强转为struct sockaddr类型
第三个参数:第二个参数的大小,填充 sizeof(struct sockaddr)

3.listen: netstat -na,可以查看网络相关的状态
假设由socket函数创建的套接字为主动套接字,那么listen函数就是将主动套接字转为被动套接字,当调用connect的时候将使用client端的主动套接字向对端(服务器端)发起
连接请求,当服务器端有被动套接字时并满足必要的条件,就将客户端的主动套接字和服务器端的被动套接字进行连接。
第一个参数:socket_fd
(下面的完成连接,未完成连接都是指的是套接字)
第二个参数:未完成连接队列和已完成连接队列的上限
未完成连接队列:服务器还未完成3次握手全过程的一个队列
已完成连接队列:服务器已经完成3次握手的队列,accept函数就是从这个队列中去返回下一个套接字(就是从这个队列中取最近完成连接的套接字)

4.accept: 这个函数会阻塞进程,直到服务器的被动套接字被客户端的主动套接字连,这个函数就会返回
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
返回值:连接套接字,之后就是用这个套接字来收发数据的
第一个参数:bind所用的套接字
第二个参数:客户端地址结构体指针,这个是一个输入参数
第三个参数:客户端地址结构体大小

5.struct sockaddr_in 此结构体在#include <netinet/in.h> 头文件中
netstat -at: 查看所有的tcp协议连接信息


UDP编程实战:
1.服务器端:socket bind
然后使用recvfrom函数接收客户端发来的信息
2.客户端:socket
然后使用sendto函数向服务器端发送信息

总结一下:
1.send recv一般用在TCP协议中
2.sendto recvfrom一般用在UDP协议中
3.sendto 可以指定发送的目的地址和端号,send需要socket已经连接才可以使用,sendto可以用在
无连接的socket中。


TCP代码:

#include <sys/types.h>         
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#include <stdio.h>


#define SERVER_PORT		(8090)
#define SERVER_IPADDR	("192.168.2.240")
#define NUM				(5)
int main(void)
{
	socklen_t len = 0;
	int ret = -1;
	int bindSocketFd = -1;
	int acceptSocketFd = -1;
	struct sockaddr_in serverAddr = {0};
	struct sockaddr_in clientAddr = {0};

	bindSocketFd = socket(AF_INET, SOCK_STREAM, 0);
	if (bindSocketFd < 0)
	{
		perror("");
		return -1;
	}
	fprintf(stderr, "bindSocketFd = %d\n", bindSocketFd);

	serverAddr.sin_family = AF_INET;
	serverAddr.sin_port = htons(SERVER_PORT);
	serverAddr.sin_addr.s_addr = inet_addr(SERVER_IPADDR);

	ret = bind(bindSocketFd, (const struct sockaddr *)&serverAddr, sizeof(struct sockaddr));
	if (ret)
	{
		fprintf(stderr, "bind fail\n");
		return -1;
	}
	fprintf(stderr, "bind ok!\n");

	ret = listen(bindSocketFd, NUM);
	if (ret)
	{
		fprintf(stderr, "listen fail\n");
		return -1;
	}
	fprintf(stderr, "listen ok!\n");

	len = sizeof(struct sockaddr);
	acceptSocketFd = accept(bindSocketFd, (struct sockaddr *)&clientAddr, &len);
	if (acceptSocketFd < 0)
	{
		fprintf(stderr, "accept fail\n");
		return -1;
	}
	fprintf(stderr, "accept ok\n");
	send(acceptSocketFd, "123", 3, 0);
	return 0;
}

#include <sys/types.h>        
#include <sys/socket.h>
#include <netinet/in.h>
#include <stdio.h>
#define SERVER_PORT		(8090)
#define SERVER_IPADDR	("192.168.2.240")


int main(void)
{
	int ret = -1;
	char buff[512] = {0};
	struct sockaddr_in serverAddr = {0};
	int socket_fd = socket(AF_INET, SOCK_STREAM, 0);
	if (socket_fd < 0)
	{
		perror("");
		return -1;
	}

	serverAddr.sin_family = AF_INET;
	serverAddr.sin_port = htons(SERVER_PORT);
	serverAddr.sin_addr.s_addr = inet_addr(SERVER_IPADDR);

	ret = connect(socket_fd, (const struct sockaddr *)&serverAddr, (socklen_t)sizeof(struct sockaddr));
	if (ret < 0)
	{
		perror("");
		return -1;
	}
	recv(socket_fd, buff, 3, 0);
	fprintf(stderr, "recv: %s\n", buff);
	return 0;
}

UDP代码:

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>

#define SERVER_IP "192.168.1.188"
#define SERVER_PORT 8888
#define BUFF_LEN 1024


/*
    server:
            socket-->bind-->recvfrom-->sendto-->close
*/

char buf[BUFF_LEN]; 
int main(int argc, char* argv[])
{
    int server_fd, ret;
    socklen_t len;
    struct sockaddr_in ser_addr; 
    struct sockaddr_in clent_addr;

    server_fd = socket(AF_INET, SOCK_DGRAM, 0); //AF_INET:IPV4;SOCK_DGRAM:UDP
    if(server_fd < 0)
    {
        printf("create socket fail!\n");
        return -1;
    }

    memset(&ser_addr, 0, sizeof(ser_addr));
    ser_addr.sin_family = AF_INET;
   // ser_addr.sin_addr.s_addr = htonl(INADDR_ANY); 
    ser_addr.sin_addr.s_addr = inet_addr(SERVER_IP);; 
   //IP地址,需要进行网络序转换,INADDR_ANY:本地地址
    ser_addr.sin_port = htons(SERVER_PORT);  //端口号,需要网络序转换

    ret = bind(server_fd, (struct sockaddr*)&ser_addr, sizeof(ser_addr));
    if(ret < 0)
    {
        printf("socket bind fail!\n");
        return -1;
    }

    //handle_udp_msg(server_fd);   //处理接收到的数据
    while (1)
    {
    	memset(buf, 0, BUFF_LEN);
        len = sizeof(clent_addr);
        ret = recvfrom(server_fd, buf, BUFF_LEN, 0, (struct sockaddr*)&clent_addr, &len);  //recvfrom是拥塞函数,没有数据就一直拥塞
        if(ret == -1)
        {
            printf("recieve data fail!\n");
            return;
        }
        printf("recv:%s\n",buf);  //打印client发过来的信息
    }
    close(server_fd);
    return 0;
}

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>

#define SERVER_PORT 8888
#define BUFF_LEN 512
#define SERVER_IP "192.168.1.188"


void client_udp_sned(int fd, struct sockaddr* dst)
{

    socklen_t len;
    struct sockaddr_in src;
    while(1)
    {
        char buf[BUFF_LEN] = "TEST UDP MSG!\n";
        len = sizeof(*dst);
        printf("client:%s\n",buf);  //打印自己发送的信息
        sendto(fd, buf, BUFF_LEN, 0, dst, len);

        sleep(1);  //一秒发送一次消息
    }
}

/*
    client:
            socket-->sendto-->revcfrom-->close
*/

int main(int argc, char* argv[])
{
    int client_fd;
    struct sockaddr_in ser_addr;

    client_fd = socket(AF_INET, SOCK_DGRAM, 0);
    if(client_fd < 0)
    {
        printf("create socket fail!\n");
        return -1;
    }

    memset(&ser_addr, 0, sizeof(ser_addr));
    ser_addr.sin_family = AF_INET;
    ser_addr.sin_addr.s_addr = inet_addr(SERVER_IP);
    //ser_addr.sin_addr.s_addr = htonl(INADDR_ANY);  //注意网络序转换
    ser_addr.sin_port = htons(SERVER_PORT);  //注意网络序转换

    client_udp_sned(client_fd, (struct sockaddr*)&ser_addr);

    close(client_fd);

    return 0;
}
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Linux网络编程中的TCPUDP是两种常见的传输协议。 TCP(Transmission Control Protocol)是一种基于连接的可靠传输协议。它提供了面向连接、可靠的数据传输服务。在TCP通信中,数据被分割成小的数据块,通过TCP连接按序传输,并且保证数据的可靠性,即使在网络拥塞或数据丢失的情况下也能重新传输丢失的数据。TCP适用于对可靠性要求较高的应用程序,如文件传输、电子邮件和网页浏览。 UDP(User Datagram Protocol)是一种无连接的不可靠传输协议。它提供了一种无序、不可靠的数据传输服务。在UDP通信中,数据以数据包(也称为数据报)的形式发送,不进行连接建立和断开,也不保证数据的可靠性和按序传输。UDP适用于对实时性要求较高、对数据可靠性要求较低的应用程序,如音视频流媒体、在线游戏等。 在Linux中进行TCPUDP网络编程可以使用Socket API。该API提供了一组函数和数据结构,用于创建套接字(socket)、绑定(bind)套接字到特定的IP地址和端口、监听(listen)连接请求、接受(accept)连接、建立连接(connect)、发送(send)和接收(receive)数据等操作。 你可以使用C语言或其他支持Socket API的编程语言来进行Linux网络编程,通过调用Socket API提供的函数来实现TCPUDP通信。在编程过程中,你需要了解TCPUDP的特点、使用套接字创建相应的连接类型、发送和接收数据的方式等。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值