Linux套接字编程

linux

套接字编程

socket

#include <sys/socket.h>
int socket(int family, int type, int protocol);
若成功返回非负描述符,出错返回-1
    
family: 协议族 AF_INET
type: 套接字类型 SOCK_STREAM
protocol:协议类型常值 0

connect 客户用connect来建立与TCP服务器的连接

#include <sys/socket.h>
int connect(int sockfd, const struct sockaddr *servaddr, socklen_t addrlen);
成功返回0,出错返回-1

sockfd: socket返回的套接字描述符
*servaddr: 指向一个套接字地址结构的指针 (SA *)&cliaddr
addrlen: 套接字大小 sizeof(cliaddr)

bind 把一个本地协议地址绑定一个套接字 //服务端

#include <sys/socket.h>
int bind(int sockfd, const struct sockaddr *myaddr, socklen_t addrlen);
成功返回0,出错返回-1
sockfd:套接字描述符
myaddr:本地套接字 servaddr
addrlen:siezof(servaddr)

listen 仅由服务器调用,1将未连接套接字转换成被动套接字,2规定了该套接字的最大连接个数

#include <sys/socket.h>
int listen(int sockfd, int backlog);

通常在调用socket和bind函数之后,并在调用accept之前调用
backlog = 未完成连接队列 + 已完成连接队列之和

accept 从已完成连接队列队头返回下一个已完成连接

#include <sys/socket.h>
int accept(int sockfd, struct sockaddr *cliaddr, socklen_t *addrlen);

cliaddr: 客户的套接字 (SA *) &cliaddr
addrlen:客户套接字地址引用 &cli_len
如果我们对客户的协议地址不感兴趣,可以置为空指针

close 关闭套接字,终止TCP连接

#include <unistd.h>
int close(int sockfd);

Sample:

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

int main(int argc, char *argv[])
{
	int listenfd, connfd; 
	struct sockaddr_in servaddr, cliaddr; //两个套接字结构,一个服务器,一个客户
	socklen_t serv_len, cli_len;
	listenfd = socket(AF_INET, SOCK_STREAM, 0); //套接字listenfd

	servaddr.sin_family = AF_INET;
	servaddr.sin_addr.s_addr = htonl(INADDR_ANY); //自动源主机ip
	servaddr.sin_port = htons(9999);

	//serv_len = sizeof(servaddr);

	bind(listenfd, (SA *) &servaddr, sizeof(servaddr)); //绑定
	listen(listenfd, 100); //转换成被动套接字,最大连接数量100

	while(1)
	{
        char ch[5000];
        std::string rec;
		cli_len = sizeof(cliaddr);
		connfd = accept(listenfd, (SA *) &cliaddr, &cli_len); //accept
		int n;
		while((n = read(connfd, &ch, 5000)) > 0)
		{
			ch[n] = 0;
			rec += ch;
		}

		write(connfd, &send_str, sizeof(send_str)/sizeof(send_str[0]));

		close(connfd);
	}

}

getsockname & getpeername

这两个函数或者返回与某个套接字关联的本地协议地址,或者返回与某个套接字关联的外地协议地址

#include <sys/socket.h>
int getsockname(int sockfd, struct sockaddr *localaddr, socklen_t *addrlen);
int getpeername(int sockfd, struct sockaddr *peeraddr, socklen_t *addrlen);
  • 在一个没有调用bind的TCP客户上,connect返回成功后,getsockname用于返回由内核赋予的本地ip地址和本地端口号
  • 在以端口号0调用bind(告诉内核去选择本地端口号)后,getsockname用于返回由内核赋予的本地端口号
并发
  • fork
#include <unistd.h>
pid_t fork(void);
返回:在子进程种为0, 在父进程种为子进程id,出错为-1

子进程可以通过调用getppid得到父进程的ID,父进程记录每次调用fork的返回值来记录子进程ID
pid_t pid;
int listenfd, connfd;
listenfd = socket(.....);

bind(listenfd,...);
listen(listenfd, 100);

while(1) {
    connfd = accept(listenfd, ....);
    if( (pid = fork()) == 0) { //fork创建子进程
        close(listenfd); //子进程关闭监听套接字
        doit(connfd); //process the request
        close(connfd); //处理完关闭已连接套接字
        exit(0);
    }
    /*
    line 10-15 是子进程
    */
    close(connfd); //父进程
}

每个文件或描述符都有一个引用计数,引用计数在文件表项中维护,它是当前打开着的引用该文件或套接字的描述符的个数

上面代码中 socket返回后,与listenfd关联的文件表项的引用计数为1,accept返回后,与connfd关联的文件表项的引用计数也是1. 然而fork返回后 这两个描述符在父进程和子进程之间共享(也就是被复制),所以这两个套接字相关联的文件表项的引用计数为2,这样父进程关闭connfd,在listenfd继续等待下一个连接,而子进程关闭listenfd,去connfd处理请求去,处理完成之后关闭connfd,该套接字才真正的清理和资源释放。

也就是说只有计数为0时,才会发送FIN报文,然后正常的TCP连接终止序列。如果我们确实想在某个TCP连接上发送一个FIN,可以改用shutdown函数代替close。

后续持续更新~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值