Linux网络编程

What

Socket也是一种IPC,允许同一计算机不同应用程序通信或者不同计算的应用程序通过网络连接进行通信。

socket类型有两种:

  • 字节流式socket

    双向可靠面向连接的字节流传输,对应的就是TCP传输

  • 数据报socket

    数据报的消息形式交换,不可靠,无连接,消息到达是无序、重复或者甚至不能到达,对应UDP传输

相关系统调用

创建套接字

#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>

int socket(int domain, int type, int protocol);
domain: AF —— Address family
     AF_UNIX, AF_LOCAL   Local communication              unix(7)
     AF_INET             IPv4 Internet protocols          ip(7)
     AF_INET6            IPv6 Internet protocols          ipv6(7)
     AF_IPX              IPX - Novell protocols
     AF_NETLINK          Kernel user interface device     netlink(7)
     AF_X25              ITU-T X.25 / ISO-8208 protocol   x25(7)
     AF_AX25             Amateur radio AX.25 protocol
     AF_ATMPVC           Access to raw ATM PVCs
     AF_APPLETALK        AppleTalk                        ddp(7)
     AF_PACKET           Low level packet interface       packet(7)
     AF_ALG              Interface to kernel crypto API
type:  常用SOCK_STREAM和SOCK_DGRAM
	 SOCK_STREAM       字节流    
     SOCK_DGRAM        数据报
     SOCK_SEQPACKET 
     SOCK_RAW       
     SOCK_RDM                
     SOCK_PACKET     
protocol:
	一般为0即可
	
RETURN VALUE
`	成功 : 套接字文件描述符
	失败 : -1

将Socket绑定到地址

#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>

int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
sockefd:
	socket()执行成功得到的socket文件描述符
addr:
	IP地址、端口相关的结构体
addrlen:
	addr的长度
        
RETURN VALUE
     成功 : 0.  
     失败 : -1

struct 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)];
 };
typedef uint32_t in_addr_t;
struct in_addr
{
    in_addr_t s_addr;
};

/* Address to accept any incoming messages.  */
#define	INADDR_ANY		((in_addr_t) 0x00000000)
/* Address to send to all hosts.  */
#define	INADDR_BROADCAST	((in_addr_t) 0xffffffff)
/* Address indicating an error return.  */
#define	INADDR_NONE		((in_addr_t) 0xffffffff)

连接到socket

#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>

//一般客户端调用
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
//参数的含义和bind()一致
RETURN VALUE
     成功 : 0.  
     失败 : -1

根据socket的连接方式分为主动连接被动连接

在这里插入图片描述

被动连接一般用于服务端,等待客户端的主动连接。

监听接入连接

#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>

//用于服务端
int listen(int sockfd, int backlog);

sockfd:
	socket文件描述符
backlog:
	客户端可能调用connect()先于服务端调用accept(),
    可能服务端忙于处理客户端,将会产生一个未决的连接
    backlog用于限制未决连接的数量
RETURN VALUE 
    成功:0
    失败:-1

在这里插入图片描述

接受连接

#include <sys/types.h>      
#include <sys/socket.h>

//用于服务端  
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen)
//参数含义和bind()一致
  
RETURN VALUE
    成功:主动连接的socket文件描述符

终止连接

close()

发送数据

 #include <sys/types.h>
 #include <sys/socket.h>

//send()调用只能在套接字处于连接状态时使用
ssize_t send(int sockfd, const void *buf, size_t len, int flags);

//如果在连接模式(SOCK_STREAM, SOCK_SEQPACKET)套接字上使用sendto(),
//则忽略参数dest_addr和addrlen
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
               const struct sockaddr *dest_addr, socklen_t addrlen);

sockfd:
	socket文件描述符
buf :
	发送的数据
len :
	发送数据的长度
flag :
	一般为0即可
        
RETURN VALUE 
   成功:发送的字节数
   失败:-1

send(sockfd, buf, len, flags)sendto(sockfd, buf, len, flags, NULL, 0)等价

接收数据

#include <sys/types.h>
#include <sys/socket.h>

//只能用于连接状态下的socket,字节流socket,TCP
ssize_t recv(int sockfd, void *buf, size_t len, int flags);

//一般用于数据报socket,UDP
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
                        struct sockaddr *src_addr, socklen_t *addrlen);
sockfd:
	socket文件描述符
buf :
	接收数据缓冲区
len :
	要接收多长的数据
flag :
	一般为0即可
src_addr:
    发送数据者的sockaddr,调用recvfrom()的用户不想知道,可以设置为NULL
addr_len:
	struct sockaddr的长度,调用recvfrom()用户不想知道,可以设置为NULL

RETURN VALUE 
    成功:接收的字节数
    失败:-1

recv(sockfd, buf, len, flags)等价于 recvfrom(fd, buf, len, flags, NULL, 0))

字节流socket(TCP)

服务端:

#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdio.h>
#include <signal.h>

#define SERVER_PORT  9999
#define BACKLOG      10

int main(int argc, char **argv)
{
    int ret;
	int addr_len;
	int recv_len;
	int sock_server;
	int sock_client;
	struct sockaddr_in sock_server_addr;
	struct sockaddr_in sock_client_addr;
	
	unsigned char recv_buf[1024];
    int client_num = -1;

	sock_server = socket(AF_INET, SOCK_STREAM, 0);
	if (-1 == sock_server)
	{
		perror("socket error!\n");
		return -1;
	}
    
	sock_server_addr.sin_family      = AF_INET;
	sock_server_addr.sin_port        = htons(SERVER_PORT);  /* host to net, short */
 	sock_server_addr.sin_addr.s_addr = INADDR_ANY;  //任意地址都可以链接
	memset(sock_server_addr.sin_zero, 0, 8);
	ret = bind(sock_server, (const struct sockaddr *)&sock_server_addr, sizeof(struct sockaddr)); //绑定IP、端口号等
	if (-1 == ret)
	{
		perror("bind error!\n");
		return -1;
	}

	ret = listen(sock_server, BACKLOG);    //开始监听
	if (-1 == ret)
	{
		perror("listen error!\n");
		return -1;
	}

	while (1)
	{
		addr_len = sizeof(struct sockaddr);

		sock_client = accept(sock_server, (struct sockaddr *)&sock_client_addr, &addr_len); //等待接受连接
		if (-1 != sock_client)
		{
			client_num++;
			printf("Get Connection from client %d : %s\n", 
                   client_num, inet_ntoa(sock_client_addr.sin_addr));
			if (!fork())  //创建子进程 来支持高并发连接
			{
				while (1)
				{
					/* 接收数据 */
					recv_len = recv(sock_client, recv_buf, 1023, 0);
					if (recv_len <= 0)
					{
						close(sock_client);
						return -1;  //此处返回结束的只是子进程
					}
					else
					{
						recv_buf[recv_len] = '\0';
						printf("Get Message From Client %d: %s\n",
                                client_num, recv_buf);
					}
				}				
			}
		}
	}

	close(sock_server);

	return 0;
}

客户端:

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

#define SERVER_PORT      9999

int main(int argc, char **argv)
{
	int sock_client;
	struct sockaddr_in sock_server_addr;

	int ret;
	unsigned char send_buf[1024];
	int send_len;

	if (argc != 2)
	{
		printf("Usage:\n");
		printf("%s <server_ip>\n", argv[0]);
		return -1;
	}

	sock_client = socket(AF_INET, SOCK_STREAM, 0); //SOCK_STREAM-创建字节流socket
	sock_server_addr.sin_family      = AF_INET;    //IPv4
	sock_server_addr.sin_port        = htons(SERVER_PORT); //端口号

 	if (inet_aton(argv[1], &sock_server_addr.sin_addr) == 0)//从命令行参数中获得服务器IP地址
 	{
		perror("invalid server_ip\n");
		return -1;
	}
	memset(sock_server_addr.sin_zero, 0, 8);
    
	ret = connect(sock_client, (const struct sockaddr *)&sock_server_addr, sizeof(struct sockaddr)); //连接服务端	

	if (ret == -1)
	{
		perror("connect error!\n");
		return -1;
	}

	while (1)
	{
		if (fgets(send_buf, 1023, stdin)) 
		{
			send_len = send(sock_client, send_buf, strlen(send_buf), 0); //发送数据
			if (send_len <= 0)
			{
				close(sock_client); //关闭socket
				return -1;
			}
		}
	}

	return 0;
}

数据报socket(UDP)

服务端:

#include <sys/types.h>        
#include <sys/socket.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdio.h>
#include <signal.h>
#define SERVER_PORT   9999

int main(int argc, char **argv)
{
    int ret;
	int addr_len;
	int recv_len;
    unsigned char recv_buf[1024];
    
	int sock_server;
	int sock_client;
	struct sockaddr_in sock_server_addr;
	struct sockaddr_in sock_client_addr;
	
	sock_server = socket(AF_INET, SOCK_DGRAM, 0);//SOCK_DGRAM-创建数据报socket
	if (sock_server == -1)
	{
		perror("socket error!\n");
		return -1;
	}

	sock_server_addr.sin_family      = AF_INET;
	sock_server_addr.sin_port        = htons(SERVER_PORT);  /* host to net, short */
 	sock_server_addr.sin_addr.s_addr = INADDR_ANY;
	memset(sock_server_addr.sin_zero, 0, 8);

	ret = bind(sock_server, (const struct sockaddr *)&sock_server_addr, sizeof(struct sockaddr));//绑定IP、端口等
	if (ret == -1)
	{
		perror("bind error!\n");
		return -1;
	}

	while (1)
	{
		addr_len = sizeof(struct sockaddr);
		recv_len = recvfrom(sock_server, recv_buf, 1023, 0, (struct sockaddr *)&sock_client_addr, &addr_len); //直接接收数据,UDP不需要连接
		if (recv_len > 0)
		{
			recv_buf[recv_len] = '\0';
			printf("Get Message From %s : %s\n",
                    inet_ntoa(sock_client_addr.sin_addr), recv_buf);
		}
	}

	close(sock_server);
	return 0;

}

客户端:

#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdio.h>

#define SERVER_PORT   9999


int main(int argc, char **argv)
{
    int ret;
	unsigned char send_buf[1024];
	int send_len;
    
	int  sock_client;
	struct sockaddr_in sock_server_addr;
	
	if (argc != 2)
	{
		printf("Usage:\n");
		printf("%s <server_ip>\n", argv[0]);
		return -1;
	}
    
	sock_client = socket(AF_INET, SOCK_DGRAM, 0); //SOCK_DGRAM-创建数据报socket
	sock_server_addr.sin_family  = AF_INET;
	sock_server_addr.sin_port   = htons(SERVER_PORT);  /* host to net, short */
 	if (0 == inet_aton(argv[1], &sock_server_addr.sin_addr))
 	{
		perror("invalid server_ip\n");
		return -1;
	}
    
	memset(sock_server_addr.sin_zero, 0, 8);
	ret = connect(sock_client, (const struct sockaddr *)&sock_server_addr,  sizeof(struct sockaddr));	//连接服务端
	if (-1 == ret)
	{
		perror("connect error!\n");
		return -1;
	}

	while (1)
	{
		if (fgets(send_buf, 1024, stdin))
		{
			send_len = send(sock_client, send_buf, strlen(send_buf), 0); //发送数据
			if (send_len <= 0)
			{
				close(sock_client);
				return -1;
			}
		}
	}
	return 0;
}


IP — 通过IP地址指定主机

Port — 通过端口号指定应用程序(进程)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

欲盖弥彰1314

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

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

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

打赏作者

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

抵扣说明:

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

余额充值