Socket是什么
在Linux中,Socket是应用进程与内核网络协议栈之间的接口。它表现为一个文件描述符,读写这个描述符就等于收发网络数据。Linux遵循“一切皆文件”,所以Socket可以配合read、write、close等系统调用使用,也可以用select、poll、epoll做多路复用。
创建与地址绑定
编程第一步是调用socket(),指定地址族(IPv4用AFINET)、套接字类型(流式SOCKSTREAM对应TCP,数据报SOCKDGRAM对应UDP)和协议。之后用sockaddrin结构填写IP与端口,通过bind()绑定到本地地址。
服务端通常绑定INADDR_ANY,表示监听本机所有网卡。端口建议使用1024以上,避免权限问题。
字节序问题
网络传输使用大端字节序,主机可能是小端。端口和IP地址必须用htons()、htonl()转换,读取时再用ntohs()、ntohl()还原。漏掉这一步会导致端口错乱。
TCP服务端流程
TCP是有连接的可靠传输,服务端流程固定:
socket()创建套接字bind()绑定地址listen()进入监听状态,设置连接队列长度accept()阻塞等待客户端连接,返回新的已连接套接字- 用read/write收发数据
close()关闭
注意accept()返回的是新描述符,原监听套接字继续用于接受后续连接。
TCP客户端流程
客户端更简单:socket()后直接connect()到服务端地址,成功后即可收发。connect()失败要检查errno,常见的是连接被拒绝(ECONNREFUSED)或超时。
UDP的差异
UDP无连接,不需要listen和accept。服务端bind()后用recvfrom()接收数据,该调用会带回对端地址;客户端用sendto()直接指定目标地址发送。UDP不保证顺序和到达,适合实时性优先、可容忍丢包的场景。
常见陷阱
- 短读短写:TCP是字节流,一次read不一定读到完整消息,需要在应用层定义长度前缀或分隔符。
- SIGPIPE:向已关闭的连接写数据会触发SIGPIPE导致进程退出,应忽略该信号或使用MSG_NOSIGNAL。
- 地址复用:服务端重启常遇到“Address already in use”,可设置SO_REUSEADDR。
- 阻塞问题:默认套接字是阻塞的,单线程处理多连接需借助epoll等机制。
小结
掌握socket、bind、listen、accept、connect五个调用,就能写出可运行的TCP程序;UDP则替换为sendto/recvfrom。真正的难点不在接口,而在处理字节流边界、错误码和并发模型。