Linux网络编程 tcp套接字代码

时间:2022-05-06 10:18:54

本次介绍一下TCP协议下的套接字代码,总体来看,tcp协议比udp协议更加安全可靠,无论是从用户使用的角度还是从编写代码的角度,你会发现与udp不同的是tcp在每次通信前,服务器端和客户端都会进行一次连接,连接成功后,才可以进行相互间的通信。

套接字API

TCP套接字的API主要有以下几个:

//建立套接字
int socket(int domain, int type, int protocol);

//绑定端口号和IP地址,需要定义struct sockaddr_in结构体
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

//开始监听
int listen(int sockfd, int backlog);

//等待客户端连接,保存地址到addr
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

//客户端请求连接服务端
int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);

//从套接字读取数据,recv和read用法一样
ssize_t recv(int sockfd, void *buf, size_t len, int flags);
ssize_t read(int fd, void *buf, size_t count);

//写入数据到套接字,send和write用法一样
ssize_t send(int sockfd, const void *buf, size_t len, int flags);
ssize_t write(int fd, const void *buf, size_t count);

TCP套接字服务器代码

创建套接字

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

socket函数的参数分别是AF_INET表示IPv4网络,SOCK_STREAM表示使用TCP协议

进行端口号绑定

//2.绑定
typedef struct sockaddr sockaddr;
typedef struct sockaddr_in sockaddr_in;

sockaddr_in local;
local.sin_family = AF_INET;
local.sin_addr.s_addr = inet_addr(argv[1]);
local.sin_port = htons(atoi(argv[2]));

if( bind(sock_listen, (sockaddr*)&local,  sizeof(local) ) < 0  ) {
    perror("bind");
    return 3;
}

先定义一个sturct sockaddr类型的变量,在对结构体的各个变量赋值,使用bind()函数进行绑定。

到目前为止,tcp和udp的步骤是一样的,都是先创建创建套接字,在将套接字和端口号,IP地址进行绑定。

进行监听

if(listen(sock_listen, 5) < 0){
    perror("listen error\n");
    return 3;
}

使用listen()设置套接字处于监听状态,成功返回0,失败返回-1,第一个参数就是刚才创建的套接字文件描述符,第二个参数表示最多允许有多少backbacklog个客户端处于连接状态,如果接收到更多的连接请求就忽略,这里设置的不会太大(一般为5)。

接受连接

//4.链接客户端
sockaddr_in client;
socklen_t len = sizeof(client);

int sock_work = accept(sock_listen, (sockaddr*)&client, &len );
if(sock_work < 0){
    perror("accept");
    continue;
}
printf("[%d] client accept success!!\n",sock_work);

服务器在三次握手完成后,调用accept()接受连接,如果服务器调用accept()时还没有客户端的连接请求,就阻塞等待直到 有客户端连接上来。addr是⼀个传出参数,accept()返回时传出客户端的地址和端⼝号;如果给addr 参数传NULL,表⽰不关⼼客户端的地址。addrlen参数是⼀个传⼊传出参数(value-result argument), 传⼊的是调⽤者提供的, 缓冲区addr的⻓度以避免缓冲区溢出问题, 传出的是客户端地址结构体的实际⻓度(有可能没有占满调⽤者提供的缓冲区);

读取发发送数据

如同读取普通文件一样,可以使用read和write直接对套接字进行读写,要注意的是这里的套接字,并不是最开始创建的那个套接字,而是在调用accept()后返回的那个文件描述符,这也是和udp不同的地方。tcp有两个套接字,第一个由我们创建,用于监听,当有客户端连入时,变返回另外一个套接字,这个是套接字是主要用来读取,写入数据的。


TCP客户端套接字代码

客户端与服务器端不同的仅仅是客户端不需要进行端口号绑定,和不需要接受连接,因为客户端总是发起连接(请求连接)的一方。

相对的客户端就多了一个请求连接的步骤

int ret = connect(sock_client, (sockaddr*)&server, sizeof(server));
if(ret < 0){
    perror("connect");
    return 1;
}

客户端需要调⽤connect()连接服务器。connectbind的参数形式⼀致, 区别在于bind的参数是⾃⼰的地址, ⽽connect的参数是对⽅的地址。connect()成功返回0,出错返回-1


测试

当我们运行起服务器端时,可以使用 netstatshell指令来显示网络连接,路由表,接口状态,伪装连接,网络链路信息和组播成员组。

netstat -lntp

来显示正在侦听的套接字。

Linux网络编程 tcp套接字代码