以下是大学刚毕业做通讯时总结的TCP和UDP编程经验,如有不对,请指出。
Socket开发所必须需要的文件
头文件:#include <Winsock2.h>
库文件:#pragma comment(lib,"Ws2_32")
动态库:main函数里添加
WSADATA ws;
WSAStartup(MAKEWORD(2,2),&ws);
有连接(流式)
服务器:socket →bind (→listen →accept) →recv/send →closesocket
客户端:socket →connect (bind)→recv/send →closesocket
(调用bind,此时属于对等通信)
Listen打开监听是否有连接请求,并将所有连接请求排列。accept新建一个专用于关于listen列表的socket。当不使用这两个函数时,就无法完成排序和保证数据完整性功能。
无连接(数据报式)
服务器:socket →bind →recvfrom /sendto →closesocket
客户端:socket (→bind) →recvfrom /sendto →closesocket
在服务器方,无论是否面向连接,均要调用 bind()。
对于客户方,大多数情况下,不调用bind,利用connect或者recvfrom /sendto来隐式完成公告socket的任务。
可以调用bind,此时属于对等通信(客户端和服务器没实际区别了,一般不这样做)。
sendto \ recvfrom 多两个参数(另一方的地址和地址结构大小),对于UDP这种无连接的,需要知道对方地址,才能进行回复。所以用于无连接数据报传输
send \ recv 常用于有连接。如果你使用UDP这种无连接,并且不想回复对方,也可以用。
返回类型
socket():返回套接字号。
bind()、connect()、listen()、closesocket()
如果没有错误发生,bind()返回0。否则返回SOCKET_ERROR。
recv()、send()
返回总共接收/发送的字节数。如果连接被关闭,返回0。否则它返回SOCKET_ERROR。
accept()
如果没有错误发生,返回一个SOCKET类型的值,表示接收到的套接字的描述符。否则返回值INVALID_SOCKET。
详细命令大全
创建套接字──socket()
SOCKET PASCAL FAR socket(int af, int type, int protocol);
1.参数af指定通信发生的区域,:AF_UNIX、AF_INET、AF_NS等,而DOS、 WINDOWS中仅支持AF_INET,它是网际网区域。
2.参数type 描述要建立的套接字的类型。这里分三种:
一是流式套接字(SOCK_STREAM)提供了一个面向连接、可靠的数据传输服务,数据无差错、无重复地发送,且按发送顺序接收。内设流量控制,避免数据流超限;数据被看作是字节流,无长度限制。// FTP、TCP
二是数据报式套接字(SOCK_DGRAM)提供了一个无连接服务。数据包以独立包形式被发送,不提供无错保证,数据可能丢失或重复,并且接收顺序混乱。//网络文件系统(NFS)、UDP 以及著名的ping命令
三是原始式套接字 (SOCK_RAW)该接口允许对较低层协议,如IP、ICMP直接访问。常用于检验新的协议实现或访问现有服务中配置的新设备。//IP层
3.参数protocol说明该套接字使用的特定协议,如果调用者不希望特别指定使用的协议,则置为0,使用默认的连接模式。
TCP:SOCKET(也可以用int) MySocket = socket(AF_INET ,SOCK_STREAM, 0);
UDP: SOCKET(也可以用int) MySocket = socket(AF_INET, SOCK_DGRAM, 0);
指定本地地址──bind()
int PASCAL FAR bind(SOCKET s, const struct sockaddr FAR * name, int namelen);
参数s套接字号。参数name本地地址指针。参数namelen表明name的长度.
Int port = 3010; //本地端口
SOCKADDR_IN addr; //本地地址。 指针类型
//初始化本地地址
addr.sin_family = AF_INET; //本地地址addr指向的区域
addr.sin_port = htons(port); //本地地址addr指向的端口
addr.sin_addr.s_addr = htonl(INADDR_ANY); //本地地址addr指向的
bind(MySocket,(LPSOCKADDR)&addr,sizeof(addr)); //完成绑定
建立套接字连接──connect()与accept()
connect()用于建立连接。
在面向连接的协议中,该调用导致本地系统和外部系统之间连接实际建立。
无连接的套接字进程也可以调用connect(),但这时在进程之间没有实际的报文交换。这样做的优点是程序员不必为每一数据指定目的地址,而且如果收到的一个数据报,其目的端口未与任何套接字建立“连接”,便能判断该端靠周知口操作。
accept()用于使服务器等待来自某客户进程的实际连接。
connect()的调用格式如下:
int PASCAL FAR connect(SOCKET s, const struct sockaddr FAR * name, int namelen);
参数name是指向对方套接字地址结构的指针。Namelen是对方套接字地址长度。
accept()的调用格式如下:
SOCKET PASCAL FAR accept(SOCKET s, struct sockaddr FAR* addr, int FAR* addrlen);
accept()调用前应该先调用过listen()。addr 指向客户方套接字地址结构的指针。addrlen 为客户方套接字地址的长度(字节数)。调用前,参数addr 指向一个初始值为空的地址结构,而addrlen 的初始值为0;调用accept()后,服务器等待从编号为s的套接字上接受客户连接请求,而连接请求是由客户方的connect()调用发出的。当有连接请求到达时,accept()调用将请求连接队列上的第一个客户方套接字地址及长度放入addr 和addrlen,并创建一个与s有相同特性的新套接字号。新的套接字可用于处理服务器并发请求。
****** 监听连接──listen()******
调用listen() 在bind()之后,在accept()之前。listen()建立长度为backlog的请求连接队列。所以用于面向连接服务器,客户端不需要。
int PASCAL FAR listen(SOCKET s, int backlog);
参数s本地套接字号。backlog请求连接队列的最大长度,目前允许的最大值为5。
数据传输──send()与recv()
send()发送数据
int PASCAL FAR send(SOCKET s, const char FAR *buf, int len, int flags);
参数s为已连接的本地套接字描述符。buf 指向存有发送数据的缓冲区的指针(也可以直接由字符串代替),其长度由len 指定。flags 指定传输控制方式,如是否发送带外数据等。
recv()接收数据
int PASCAL FAR recv(SOCKET s, char FAR *buf, int len, int flags);
参数s 为已连接的套接字描述符。buf指向接收输入数据缓冲区的指针,其长度由len 指定。flags 指定传输控制方式,如是否接收带外数据等。
输入/输出多路复用──select()
select() 调用用来检测一个或多个套接字的状态。对每一个套接字来说,这个调用可以请求读、写或错误状态方面的信息。请求给定状态的套接字集合由一个fd_set结构指示。在返回时,此结构被更新,以反映那些满足特定条件的套接字的子集,同时, select()调用返回满足条件的套接字的数目
int PASCAL FAR select(int nfds, fd_set FAR * readfds, fd_set FAR * writefds, fd_set FAR * exceptfds, const struct timeval FAR * timeout);
参数nfds指明被检查的套接字描述符的值域,此变量一般被忽略。
参数readfds指向要做读检测的套接字描述符集合的指针,调用者希望从中读取数据。参数writefds 指向要做写检测的套接字描述符集合的指针。 exceptfds指向要检测是否出错的套接字描述符集合的指针。timeout指向select()函数等待的最大时间,如果设为NULL则为阻塞操作。select()返回包含在fd_set结构中已准备好的套接字描述符的总数目,或者是发生错误则返回SOCKET_ERROR。
关闭套接字──closesocket()
closesocket()关闭套接字s,并释放分配给该套接字的资源;如果s涉及一个打开的TCP连接,则该连接被释放。
BOOL PASCAL FAR closesocket(SOCKET s);