Linux网络编程基础API

socket编程

TCP通信:
Server: socket->bind->listen->accept->recv->send->close
Client: socket->connect->send->recv->close

TCP服务器

  1. 专用socket地址

TCP/IP协议族有sockaddr_in和sockaddr_in6两个专用socket地址结构体。
sockaddr_in:

struct sockaddr_in
{	
	sa_family_t sin_family;		// 地址族:AF_INEt
	u_init16_t sin_port;		// 端口号,用网络字节序表示
	struct in_addr sin_addr;	// IPv4地址结构体
	unsigned char sin_zero[8];	// 为了让sockaddr与sockaddr_in两个数据结构保持大小相同而保留的空字节
};
struct in_addr
{
	u_int32_t s_addr;	// IPv4地址,用网络字节序表示
}

例:

#define PORT 8111

struct sockaddr_in local_addr;

local_addr.sin_family = AF_INET;
local_addr.sin_port = htons(PORT);
local_addr.sin_addr.s_addr = INADDR_ANY;
bzero(&(local_addr.sin_zero), 8);
  1. 创建socket——socket
#include<sys/types>
#include<sys/socket.h>

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

参数:
domain:告诉系统使用哪个底层协议族。
对于TCP/IP协议族,设置为PF_INET或PF_INET6;对于UNIX本地域协议族,设置为PF_UNIX。
type:指定服务类型。
对于TCP/IP协议族,取SOCK_STREAM表示传输层使用TCP协议,SOCK_DGRAM表示传输层使用UDP协议。

自Linux2.6.17起,type可以接受SOCK_STREAM或SOCK_DGRAM与下面两个标志相与的值:SOCK_NONBLOCK和SOCK_CLOEXEC。
SOCK_NONBLOCK:将新创建的socket设为非阻塞的;
SOCK_CLOEXEC:用fork调用创建子进程时在子进程中关闭该socket。
在2.6.17之前的Linux中,需要用额外的系统调用,比如fnctl来设置。

protocol:在前两个参数构成的协议集合下,再选择一个具体的协议(通常设置为0)。

返回值:
成功返回一个socket文件描述符,失败返回-1并设置errno。

例:

int socket_fd = -1;
socket_fd = socket(AF_INET, SOCK_STREAM, 0);
if(socket_fd==-1){
	perror("create socket error");
	exit(1);
}
  1. 绑定socket地址与端口——bind
    将一个socket与socket地址绑定称为给socket命名。只有命名sockethh后,客户端才知道如何连接它(客户端通常采取匿名的方式)。
#include<sys/types>
#include<sys/socket.h>

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

bind将my_addr所指的socket地址分配给未命名的sockfd文件描述符,addrlen指出该socket地址的长度。

返回值:
成功返回0,失败返回-1并设置errno。
errno:
EACCES:被绑定的地址是受保护的,仅超级用户可访问。
EADDRINUSE:被绑定的地址正在使用中。

例:

ret = bind(socket_fd, (struct sockaddr *)&local_addr, sizeof(struct sockaddr_in));
if(ret==-1){
	perror("bind error");
	exit(1);
}
  1. 监听socket——listen
    使用系统调用来创建一个监听队列以存放待处理的客户连接:
#include<sys/socket.h>
int listen(int sockfd, int backlog);

参数:
sockfd:指定被监听的socket。
backlog:内核监听队列的最大长度。
监听队列的长度如果超过backlog,服务器将不受理新的客户连接,客户端将收到ECONNREFUSED。

例:

ret = listen(socket_fd, backlog);
if(ret==-1){
	perror("listen error");
	exit(1);
}
  1. 接受连接——accept
#include<sys/types>
#include<sys/socket.h>

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

参数:
sockfd:执行过listen监听的socket。
addr:用来获取被接受连接的远端socket地址。
addrlen:远端socket地址的长度。

返回值:
accept成功时返回一个新的连接socket,该socket唯一地标识了被接受的这个连接,服务器通过读写该socket来与对应的客户端通信。
accept失败时返回-1并设置errno。

socklen_t addr_len = sizeof(struct sockaddr_in);
accept_fd = accept(socket_fd, (struct sockaddr *)&remote_addr, &addr_len);
  1. 数据读写——recv与send
#include<sys/types.h>
#include<sys/socket.h>

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

recv参数:
sockfd: 要读取的socket文件描述符
buf: 读缓冲区
len: 读缓冲区大小
flags: 见下文

send参数:
sockfd: 要写入的socket文件描述符
buf: 写缓冲区
len: 写缓冲区大小

失败返回-1

例:

char in_buf[MESSAGE_SIZE]={0};
while(1)
{
	memset(in_buf, 0, MESSAGE_SIZE);
	ret = recv(accept_fd, (void*)in_buf, MESSAGE_SIZE, 0);
	if(ret==0){
		break;
	}
	printf("receive message:%s\n", in_buf);
	send(accept_fd, (void*)in_buf, MESSAGE_SIZE, 0);
}

flags参数:
在这里插入图片描述

  1. 关闭连接——close
#include<unistd.h>

int close(int fd);

参数:
fd: 待关闭的socket

close并非总是立即关闭一个连接 而是将fd的引用计数减1 当fd的引用计数为0时 才真正关闭连接
在多进程编程中 一次fork默认使父进程中打开的socket引用计数加1 因此必须在父进程和子进程中都对socket执行close调用才能将连接关闭

TCP服务器实例

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

#define PORT 8111
#define MESSAGE_SIZE 1024

int main()
{
	int ret=-1;
	
	int socket_fd = -1;
	int accept_fd = -1;
	
	int backlog = 10;
	
	char in_buf[MESSAGE_SIZE]={0};
	struct sockaddr_in local_addr, remote_addr;
	
	// crreate a tcp socket
	socket_fd = socket(AF_INET, SOCK_STREAM, 0);
	if(socket_fd==-1){
		perror("create socket error");
		exit(1);
	}

	// set option of socket
	ret = setsocket(socket_fd, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof(flag));
	if(ret==-1){
		perror("setsockopt error");
	}
	
	// set local address
	local_addr.sin_family = AF_INET;
	local_addr.sin_port = htons(PORT);
	local_addr.sin_addr.s_addr = INADDR_ANY;
	bzero(&(local_addr.sin_zero), 8);
	
	// bind socket
	ret = bind(socket_fd, (struct sockaddr*)&local_addr, sizeof(struct sockadddr_in));
	if(ret==-1){
		perror("bind error");
		exit(1);
	}
	
	ret = listen(socket_fd, backlog);
	if(ret==-1){
		perror("listen error");
		exit(1);
	}
	
	// loop
	for(;;){
		socklen_t addr_len = sizeof(struct sockaddr_in);
		// accept a new connection
		accept_Fd = accept(socket_fd, (struct sockaddr *)&remote_addr, &addr_len);
		for(;;){
			memset(in_buf, 0, MESSAGE_SIZE);
			// receive newwork data and print it
			ret = recv(accept_fd, (void*)in_buf, MESSAGE_SIZE, 0);
			if(ret==0){
				break;
			}
			printf("receive message:%s\n", in_buf);
			send(accept_fd, (void*)in_buf, MESSAGE_SIZE, 0);
		}
		printf("close client connection...\n");
		close(accept_fd);
	}
	printf("quit server..\n");
	close(socket_fd);
	return 0;
}

TCP客户端

  1. 主动与服务器建立连接——connect
#include<sys/types.h>
#include<sys/socket.h>

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

参数:
sockfd: 由socket系统调用返回一个socket
serv_addr: 服务器监听的socket地址
addrlen: 地址的长度

返回值:
成功返回0, 失败返回-1并设置errno
ECONNREFUSED: 连接被拒绝
ETIMEOUT: 连接超时

TCP客户端实例

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include<iostream>
#include <fcntl.h>
#include <errno.h>
#include <netdb.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>


#define SERVER_PORT 8111
#define MESSAGE_LENGTH 1024


int main()
{
    int ret=-1;
    int socket_fd;

    // server addr
    struct sockaddr_in serverAddr;

    char sendbuf[MESSAGE_LENGTH];
    char recvbuf[MESSAGE_LENGTH];

    int data_len;

    if((socket_fd = socket(AF_INET, SOCK_STREAM,0))<0)
    {
        perror("socket");
        return 1;
    }

    serverAddr.sin_family = AF_INET;
    serverAddr.sin_port = htons(SERVER_PORT);

    // inet_addr()函数, 将点分十进制IP转换成网络字节序IP
    serverAddr.sin_addr.s_addr = inet_addr("127.0.0.1");


    if(connect(socket_fd, (struct sockaddr *)&serverAddr, sizeof(serverAddr)) < 0)
    {
        perror("connect");
        return 1;
    }

    printf("success to connect server...\n");

    while(1)
    {
        memset(sendbuf, 0, MESSAGE_LENGTH);
        printf("<<<<send message:");
        std::cin>>sendbuf;

        ret = send(socket_fd, sendbuf, strlen(sendbuf), 0);
        if(ret<=0){
            printf("the connection is disconnection!\n");
            break;
        }

        if(strcmp(sendbuf, "quit") == 0){
            break;
        }

        printf(">>> echo message:");

        recvbuf[0] = '\0';

        data_len = recv(socket_fd, recvbuf, MESSAGE_LENGTH, 0);

        recvbuf[data_len]='\0';
        printf("%s\n", recvbuf);
    }
    close(socket_fd);

    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值