
本文共 3390 字,大约阅读时间需要 11 分钟。
网络编程基础
一. 库函数
1.connect()
向服务器发起连接请求。函数声明:
int connect(int sockfd, struct sockaddr * serv_addr, int addrlen);
函数说明:connect函数用于将参数sockfd 的socket 连至参数serv_addr 指定的服务端,参数addrlen为sockaddr的结构长度。
返回值:成功则返回0,失败返回-1,错误原因存于errno 中。
connect函数只用于客户端。
如果服务端的地址错了,或端口错了,或服务端没有启动,connect一定会失败。
2.bind()函数
服务端把用于通信的地址和端口绑定到socket上。函数声明:
int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
参数sockfd,需要绑定的socket。
参数addr,存放了服务端用于通信的地址和端口。
参数addrlen表示addr结构体的大小。
返回值:成功则返回0,失败返回-1,错误原因存于errno 中。
如果绑定的地址错误,或端口已被占用,bind函数一定会报错,否则一般不会返回错误。
问题一:
同时启动两个服务端,报出异常tdx96@ubuntu:~/TDXFolder/socket$ ./server 5005bind: Address already in usetdx96@ubuntu:~/TDXFolder/socket$
解决办法:
在socket调用和bind调用之间加入int opt = 1; setsockopt(socket_fd,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));
问题二:
socket的端口范围 1024以上到65535可用,1024以下为系统保留端口tdx96@ubuntu:~/TDXFolder/socket$ ./server 1024
tdx96@ubuntu:~/TDXFolder/socket$ ./server 1023bind: Permission denied
若要使用1024以下的端口,则输入指令
tdx96@ubuntu:~/TDXFolder/socket$ su密码:root@ubuntu:/home/tdx96/TDXFolder/socket# ./server 1023
3.listen()
listen函数把主动连接socket变为被动连接的socket,使得这个socket可以接受其它socket的连接请求,从而成为一个服务端的socket。函数声明:
int listen(int sockfd, int backlog);
返回:0-成功, -1-失败
参数sockfd是已经被bind过的socket。socket函数返回的socket是一个主动连接的socket,在服务端的编程中,程序员希望这个socket可以接受外来的连接请求,也就是被动等待客户端来连接。由于系统默认时认为一个socket是主动连接的,所以需要通过某种方式来告诉系统,程序员通过调用listen函数来完成这件事。
参数backlog,这个参数涉及到一些网络的细节,比较麻烦,填5、10都行,一般不超过30。
当调用listen之后,服务端的socket就可以调用accept来接受客户端的连接请求。
返回值:成功则返回0,失败返回-1,错误原因存于errno 中。
listen函数一般不会返回错误。
4.accept()
服务端接受客户端的连接。函数声明:
int accept(int sockfd,struct sockaddr *addr,socklen_t *addrlen);
参数sockfd是已经被listen过的socket。
参数addr用于存放客户端的地址信息,用sockaddr结构体表达,如果不需要客户端的地址,可以填0。
参数addrlen用于存放addr参数的长度,如果addr为0,addrlen也填0。
accept函数等待客户端的连接,如果没有客户端连上来,它就一直等待,这种方式称之为阻塞。
accept等待到客户端的连接后,创建一个新的socket,函数返回值就是这个新的socket,即客户端的socket,服务端使用这个新的socket和客户端进行报文的收发。
返回值:成功则返回0,失败返回-1,错误原因存于errno 中。
accept在等待的过程中,如果被中断或其它的原因,函数返回-1,表示失败,如果失败,可以重新accept。
连接请求会被放在队列中,accept读取队列中的链接请求。如果队列为空,则accept阻塞。
5.send()
send函数用于把数据通过socket发送给对端。不论是客户端还是服务端,应用程序都用send函数来向TCP连接的另一端发送数据。函数声明:
ssize_t send(int sockfd, const void *buf, size_t len, int flags);
sockfd为已建立好连接的socket。
buf为需要发送的数据的内存地址,可以是C语言基本数据类型变量的地址,也可以数组、结构体、字符串,内存中有什么就发送什么。
len需要发送的数据的长度,为buf中有效数据的长度。
flags填0, 其他数值意义不大。
函数返回已发送的字符数。出错时返回-1,错误信息errno被标记。
注意,就算是网络断开,或socket已被对端关闭,send函数不会立即报错,要过几秒才会报错。
如果send函数返回的错误(<=0),表示通信链路已不可用。
当发送内容超出缓冲区大小时,send会阻塞,等待缓冲器的空间。6.recv()
recv函数用于接收对端socket发送过来的数据。recv函数用于接收对端通过socket发送过来的数据。不论是客户端还是服务端,应用程序都用recv函数接收来自TCP连接的另一端发送过来数据。
函数声明:
ssize_t recv(int sockfd, void *buf, size_t len, int flags);
sockfd为已建立好连接的socket。
buf为用于接收数据的内存地址,可以是C语言基本数据类型变量的地址,也可以数组、结构体、字符串,只要是一块内存就行了。
len需要接收数据的长度,不能超过buf的大小,否则内存溢出。
flags填0, 其他数值意义不大。
函数返回已接收的字符数。出错时返回-1,失败时不会设置errno的值。
如果socket的对端没有发送数据,recv函数就会等待,如果对端发送了数据,函数返回接收到的字符数。出错时返回-1。如果socket被对端关闭,返回值为0。
如果recv函数返回的错误(<=0),表示通信通道已不可用。
二.TCP的三次握手和连接队列
tdx96@ubuntu:~$ netstat -na|more
查看服务端5005状态
tdx96@ubuntu:~$ netstat -na|grep 5005tcp 0 0 0.0.0.0:5005 0.0.0.0:* LISTEN
开启客服端 连接5005端口
查看客户端5005状态tdx96@ubuntu:~$ netstat -na|grep 5005tcp 0 0 0.0.0.0:5005 0.0.0.0:* LISTENtcp 0 0 127.0.0.1:60354 127.0.0.1:5005 ESTABLISHEDtcp 0 0 127.0.0.1:5005 127.0.0.1:60354 ESTABLISHED
三次握手已经完成
三. TCP的分包和粘包
发表评论
最新留言
关于作者
