基于TCP(面向连接)的socket编程步骤
服务器端程序:
1、创建套接字(socket);
2、将套接字绑定到一个本地地址和端口上(bind);
3、将套接字设为监听模式,准备接收客户请求(listen);
4、等待客户请求到来。当请求到来后,接收连接请求,返回一个新的对应于此次连接的套接字(accept);
5、用返回的套接字和客户端进来通信(send/recv);
6、返回。等待另一个客户请求;
7、关闭套接字(closesocket)。
客户端程序:
1、创建套接字(socket);
2、向服务器发出连接请求(connect);
3、和服务器端进行通信(send/recv);
4、关闭套接字(closesocket);
基于UDP(面向无连接)的socket编程步骤
服务器端程序:
1、创建套接字(socket);
2、将套接字绑定到一个本地地址和端口上(bind);
3、等待接收数据(recvfrom);
7、关闭套接字(closesocket)。
客户端程序:
1、创建套接字(socket);
2、向服务器发送数据(sendto);
3、关闭套接字(closesocket);
socket()函数
int socket(int domain, int type, int protocol);
•domain:即协议域,又称为协议族(family)。常用的协议族有,AF_INET(IPv4)、AF_INET6(IPv6)、AF_LOCAL(或称AF_UNIX,Unix域socket)、AF_ROUTE等等。协议族决定了socket的地址类型,在通信中必须采用对应的地址,如AF_INET决定了要用ipv4地址(32位的)与端口号(16位的)的组合、AF_UNIX决定了要用一个绝对路径名作为地址。
•type:指定socket类型。常用的socket类型有,SOCK_STREAM(流式套接字)、SOCK_DGRAM(数据报式套接字)、SOCK_RAW、SOCK_PACKET、SOCK_SEQPACKET等等
•protocol:就是指定协议。常用的协议有,IPPROTO_TCP、PPTOTO_UDP、IPPROTO_SCTP、IPPROTO_TIPC等,它们分别对应TCP传输协议、UDP传输协议、STCP传输协议、TIPC传输协议。
注意:并不是上面的type和protocol可以随意组合的,如SOCK_STREAM不可以跟IPPROTO_UDP组合。当protocol为0时,会自动选择type类型对应的默认协议。对于TCP/IP协议的套接字,domain只能是AF_INET。
bind()函数
bind()函数把一个地址族中的特定地址赋给socket。例如对应AF_INET、AF_INET6就是把一个ipv4或ipv6地址和端口号组合赋给socket。
int bind(SOCKET sockfd, const struct sockaddr *addr, int addrlen);
函数的三个参数分别为:
•sockfd:即socket描述字,它是通过socket()函数创建了,唯一标识一个socket。bind()函数就是将给这个描述字绑定一个名字。
•addr:一个const struct sockaddr *指针,指向要绑定给sockfd的协议地址。
•addrlen:对应的是地址的长度。
struct sockaddr{
sa_family_t sin_family; //地址族(Address Family),也就是地址类型
char sa_data[14]; //IP地址和端口号
};
可以用SOCKADDR_IN代替
struct sockaddr_in {
short sin_family; //地址族
u_short sin_port; //端口
struct in_addr sin_addr;
char sin_zero[8];
};
listen()、connect()函数
如果作为一个服务器,在调用socket()、bind()之后就会调用listen()来监听这个socket,如果客户端这时调用connect()发出连接请求,服务器端就会接收到这个请求。
int listen(SOCKET sockfd, int backlog);
int connect(SOCKET sockfd, const struct sockaddr *addr, socklen_t addrlen);
listen函数的第一个参数即为要监听的socket描述字,第二个参数为相应socket可以排队的最大连接个数。socket()函数创建的socket默认是一个主动类型的,listen函数将socket变为被动类型的,等待客户的连接请求。
connect函数的第一个参数即为客户端的socket描述字,第二参数为服务器的socket地址,第三个参数为socket地址的长度。客户端通过调用connect函数来建立与TCP服务器的连接。
accept()函数
TCP服务器端依次调用socket()、bind()、listen()之后,就会监听指定的socket地址了。TCP客户端依次调用socket()、connect()之后就向TCP服务器发送了一个连接请求。TCP服务器监听到这个请求之后,就会调用accept()函数取接收请求,这样连接就建立好了。之后就可以开始网络I/O操作了,即类同于普通文件的读写I/O操作。
int accept(SOCKET sockfd, struct sockaddr *addr, socklen_t *addrlen);
recv()、send()等函数
int send(
SOCKET s,
const char FAR* buf,
int len,
int flags
);
int recv(
SOCKET s,
char FAR* buf,
int len,
int flags
);
基于TCP的socket的编程代码:
服务端TcpSrv.cpp,需要包含头文件WinSock2.h和链接ws2_32.lib
#include <WinSock2.h>
#include <stdlib.h>
#include <stdio.h>
void main()
{
//加载套接字库
WORD wVersionRequested;
WSADATA wsaData;
int err;
wVersionRequested = MAKEWORD( 1, 1 );
err = WSAStartup( wVersionRequested, &wsaData );
if ( err != 0 ) {
return;
}
if ( LOBYTE( wsaData.wVersion ) != 1 ||
HIBYTE( wsaData.wVersion ) != 1 ) {
/* Tell the user that we could not find a usable */
/* WinSock DLL. */
WSACleanup( );
return;
}
SOCKET sockSrv = socket(AF_INET, SOCK_STREAM,0);//创建socket
SOCKADDR_IN addrSrv;
addrSrv.sin_addr.S_un.S_addr = htonl(INADDR_ANY);//除了sin_family都要使用网络字节序
//htonl将主机的无符号长整形数转换成网络字节顺序。
addrSrv.sin_family = AF_INET;
addrSrv.sin_port = htons(6000);//需要用1024以上,将一个无符号短整型数值转换为网络字节序,即大端模式(big-endian)
bind(sockSrv, (SOCKADDR*)&addrSrv, sizeof(SOCKADDR));//绑定
listen(sockSrv, 5);//监听
SOCKADDR_IN addrClient;//保存客户端的地址
int len = sizeof(SOCKADDR);
while(1)
{
SOCKET sockConn = accept(sockSrv, (SOCKADDR*)&addrClient, &len);//接受用户请求
char sendBuf[100];
sprintf(sendBuf, "Welcome %s to http", inet_ntoa(addrClient.sin_addr));
send(sockConn, sendBuf, strlen(sendBuf)+1, 0);//用返回的套接字和客户端进行通信
char recvBuf[100];
recv(sockConn, recvBuf, 100, 0);
printf("这是服务端:%s\n", recvBuf);
closesocket(sockConn);//关闭套接字
}
}
客户端TcpClient.cpp,需要包含头文件WinSock2.h和链接ws2_32.lib
#include <WinSock2.h>
#include <stdlib.h>
#include <stdio.h>
void main()
{
//加载套接字库
WORD wVersionRequested;
WSADATA wsaData;
int err;
wVersionRequested = MAKEWORD( 1, 1 );
err = WSAStartup( wVersionRequested, &wsaData );
if ( err != 0 ) {
return;
}
if ( LOBYTE( wsaData.wVersion ) != 1 ||
HIBYTE( wsaData.wVersion ) != 1 ) {
/* Tell the user that we could not find a usable */
/* WinSock DLL. */
WSACleanup( );
return;
}
SOCKET sockClient = socket(AF_INET, SOCK_STREAM, 0);//创建套接字
SOCKADDR_IN addrSrv;
addrSrv.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");//本地回路地址
addrSrv.sin_family = AF_INET;
addrSrv.sin_port = htons(6000);//与服务端保持一致
connect(sockClient, (SOCKADDR*)&addrSrv, sizeof(SOCKADDR));//向服务器发送请求connect
char recvBuf[100];
recv(sockClient, recvBuf, 100, 0);//接受服务端的数据
printf("这是客户端 %s\n", recvBuf);
send(sockClient, "This is ZhangSan", strlen("This is ZhangSan")+1, 0);//向服务端发送数据
closesocket(sockClient);
WSACleanup();//终止对套接字库的使用
}
基于UDP的socket的编程代码:
服务端UdpSrv.cpp,需要包含头文件WinSock2.h和链接ws2_32.lib
#include <WinSock2.h>
#include <stdlib.h>
#include <stdio.h>
void main()
{
//加载套接字库
WORD wVersionRequested;
WSADATA wsaData;
int err;
wVersionRequested = MAKEWORD( 1, 1 );
err = WSAStartup( wVersionRequested, &wsaData );
if ( err != 0 ) {
return;
}
if ( LOBYTE( wsaData.wVersion ) != 1 ||
HIBYTE( wsaData.wVersion ) != 1 ) {
/* Tell the user that we could not find a usable */
/* WinSock DLL. */
WSACleanup( );
return;
}
SOCKET sockSrv = socket(AF_INET, SOCK_DGRAM, 0);
SOCKADDR_IN addrSrv;
addrSrv.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
addrSrv.sin_family = AF_INET;
addrSrv.sin_port = htons(6000);
bind(sockSrv, (SOCKADDR*)&addrSrv, sizeof(SOCKADDR));
SOCKADDR_IN addrClient;
int len = sizeof(SOCKADDR);
char recvBuf[100];
recvfrom(sockSrv, recvBuf, 100, 0, (SOCKADDR*)&addrClient, &len);
printf("%s\n", recvBuf);
closesocket(sockSrv);
WSACleanup();
}
客户端UdpClient.cpp,需要包含头文件WinSock2.h和链接ws2_32.lib
#include <WinSock2.h>
#include <stdlib.h>
#include <stdio.h>
void main()
{
//加载套接字库
WORD wVersionRequested;
WSADATA wsaData;
int err;
wVersionRequested = MAKEWORD( 1, 1 );
err = WSAStartup( wVersionRequested, &wsaData );
if ( err != 0 ) {
return;
}
if ( LOBYTE( wsaData.wVersion ) != 1 ||
HIBYTE( wsaData.wVersion ) != 1 ) {
/* Tell the user that we could not find a usable */
/* WinSock DLL. */
WSACleanup( );
return;
}
SOCKET sockClient = socket(AF_INET, SOCK_DGRAM, 0);
SOCKADDR_IN addrSrv;
addrSrv.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
addrSrv.sin_family = AF_INET;
addrSrv.sin_port = htons(6000);
sendto(sockClient, "Hello", strlen("Hello") + 1, 0,
(SOCKADDR*)&addrSrv, sizeof(SOCKADDR));
closesocket(sockClient);
WSACleanup();
}