Socket编程(1)
编写环境为Windows
不过在Linux略微修改就行了,这个真的不是在做课程实验
实现目标
实现服务端Server与客户端Client,有客户端向服务端发起通信,服务端能够进行响应。
实现思路
使用TCP协议进行实现,所以在整个通信流程中,很显然需要让客户端知道怎么与服务端发起通信(也就是怎么找到服务端)。
服务端需要确定IP地址和端口号,例如本机IP可以用127.0.0.1这类127开头的环回地址(也就是localhost)或者使用ipconfig查询IP地址,以及在服务端运行时的端口号。
客户端通过socket套接字向服务端的IP和端口建立连接,进行通信。
代码实现
服务端
创建服务端的套接字并返回createsockfd
默认端口号12138(当然只是我自己写乱填的,只要不是系统保留的端口也别大于65535就可以)。
WSADATA根据百度百科的描述是(一种用于存储被WSAStartup函数调用后返回的Windows Sockets数据的数据结构)。
套接字SOCKET变量名为fd。使用SOCKET socket(int af, int type, int protocol)函数建立,第一个参数为地址族,IPV4即AP_INET,第二个参数为协议类型,SOCK_STREAM为流式套接字,第三个参数填写为IPPROTO_TCP使用TCP协议(填0时系统会根据套接字的类型决定应使用的传输层协议,当前仅TCP可选),判断返回值是否合法。
结构体sockaddr_in和sockaddr定义如下,分别用于存储地址族类型、IP地址、端口号最后一个不使用(用于与结构体sockaddr的大小保持一致),其中端口号和地址需要使用网络字节序(大端法),要注意主机中的字节序是否一致,使用hton()函数进行转换。
struct sockaddr_in {
ADDRESS_FAMILY sin_family;
USHORT sin_port;
IN_ADDR sin_addr;
CHAR sin_zero[8];
}
struct sockaddr {
ADDRESS_FAMILY sa_family; // Address family.
CHAR sa_data[14]; // Up to 14 bytes of direct address.
}
sin_famaily设置为AF_INET,端口号使用htons函数将主机字节序转换为网络字节寻进行存放,地址监听任意网卡也就是全0的地址,可以查看INADDR_ANY的定义。
#define INADDR_ANY (ULONG)0x00000000
使用bind函数将地址与套接字fd进行绑定,其中第二个参数要使用类型转换否则编译报错。使用listen函数设置fd的监听队列,其中第二个参数大小为TCP三次握手后完成的任务队列大小即accept queue的大小,还有一个队列syn queue为三次握手过程中的任务队列大小,参数在系统中设置。若正确完成则返回fd即可。
SOCKET createsockfd(int port=12138)
{
WSADATA wd;
WSAStartup(MAKEWORD(2, 2), &wd);
SOCKET fd;
if ((fd = socket(AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET)
{
printerror((char*)"建立SOCKET时错误码:");
}//建立服务端的socket
struct sockaddr_in serveraddr;
memset(&serveraddr, 0, sizeof(sockaddr_in));
serveraddr.sin_family = AF_INET;
serveraddr.sin_port = htons((unsigned short)port);
serveraddr.sin_addr.s_addr = htonl(INADDR_ANY);
if (bind(fd, (struct sockaddr*)&serveraddr, sizeof(sockaddr_in))!=0