此文为作者学习网络编程的学习笔记系列第一篇,由于才疏学浅,如有疏漏请多指正!
正文:
网络编程通常采用"服务器端-客户端"模型编写,先来看服务器端编写(本文介绍windows下编写,但linux所使用的函数格式相差不大)
一套接字
网络编程又被称为套接字编程,足见套接字之重要性.如果将网络比作打电话,那么创建套接字相当于构建了一个电话机.windows中套接字由socket函数创建(SOCKET socket(int af,int type,int protocol))
af 为协议族,type为套接字类型,protocol决定最终采用的协议.下面是具体解释:网络通信有几种协议,如PF_INET为Ipv4协议族,PF_INET为Ipv6协议族,因此第一个参数决定采用哪一种协议族.但是即使是同一协议族,仍然有几种数据传输方式,像大家熟知的tcp udp就是两种不同的数据传输方式.因此第二个参数决定数据传输方式.不过即使是同一协议族同种数据传输方式,仍然有可能不同,因此第三个参数便派上用场(但大多数情况前两个参数即可创建套接字,这种情况下可填0).
二bind函数
有了电话机还需要什么呢?--分配电话.此处就牵扯到网络通信中怎么定位到具体的电脑了,我简单的讲下就是,ip地址用以找到具体电脑,但因为一台电脑有多个应用可以联网(一边上网一边听歌),所以端口号便用来区分这些应用.ip地址,端口号以及上文提到的协议族,便是分配给这个电话机的电话号.bind函数原型为:int bind(int sockfd, const struct sockaddr *my_addr, socklen_t addrlen),其中第二个参数就是提到的传递给bind函数的信息,正如大家所见,这些信息被保存在结构体SOCKADDR_IN中(具体在后文代码中具体阐释),参数一就是创建的服务器端套接字.参数三为参数二结构体的长度.
三listen函数
分配好电话后就要赋予接听电话能力,listen函数原型为int listen(int sock,int backlog)其中sock是套接字文件描述符(由socket函数返回),backlog是请求等待队列的长度.很好理解,带电话来的不止一个,总不能都接吧,所以得有人等着,那么允许等待人数即为backlog,listen函数就是保安喽.
四accept函数
这里便真正开始接听电话了(说起来这里像不像一个极其美丽的女孩被一群人追,段位低的一个个相处,段位高的(多线程)同时相处,咳扯远了)accept函数原型为int accept(int sock,struct sockaddr* sockaddr,socklen_t *addrlen)参数一为服务器端套接字描述符,参数二为客户端地址信息,参数三为客户端地址信息长度,其中参数二三在链接成功后填满.
下面为服务器端代码:
#include<stdio.h>
#include<stdlib.h>
#include<WinSock2.h>
#pragma comment(lib,"ws2_32.lib")
//#define BUFF_SIZE 1024
char message[] = "Hello World!";
void errorHandling(const char* message);
int main()
{
WSADATA wsaData;
SOCKET servSock,clntSock;
SOCKADDR_IN servAdr, clntAdr;
int size_Clnt,str_len;
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)//开启windows网络相关库
{
errorHandling("WSAStartup() error!");
}
servSock = socket(PF_INET, SOCK_STREAM, 0);//创建服务器端套接字(SOCK-STREAM为TCP套接字)
if (servSock == INVALID_SOCKET)
{
errorHandling("socket() error!");
}
memset(&servAdr, 0, sizeof(servAdr));
servAdr.sin_family = AF_INET;//协议族
servAdr.sin_addr.s_addr = htonl(INADDR_ANY);//ip要转化为大端序
servAdr.sin_port = htons(atoi("9190"));//端口号
if (bind(servSock, (SOCKADDR*)&servAdr, sizeof(servAdr)) == SOCKET_ERROR)
{
errorHandling("bind() error!");
}
if (listen(servSock, 5) == SOCKET_ERROR)
{
errorHandling("listen() error!");
}
size_Clnt = sizeof(clntAdr);
clntSock = accept(servSock, (SOCKADDR*)&clntAdr, &size_Clnt);
if (clntSock == INVALID_SOCKET)
{
errorHandling("accept() error!");
}
send(clntSock, message,sizeof(message), 0);//向练接到的客户端套接字发送"Hello World"
closesocket(clntSock);
closesocket(servSock);
WSACleanup();
return 0;
}
void errorHandling(const char*m)
{
fputs(m, stderr);
fputc('\n', stderr);
exit(1);
}
接下来就是客户端代码编写了,由于客户端相对简单,故此处只介绍客户端的connect函数,函数原型为int connect(int sock,struct sockaddr* servaddr,socklen_t addrlen),sock为客户端套接字,再填入存有服务器端信息的结构体和长度即可连接服务器端~
代码如下:
#include<stdio.h>
#include<stdlib.h>
#include<WinSock2.h>
#pragma comment(lib,"ws2_32.lib")
#define BUFF_SIZE 1024
void errorHanding(const char* message);
char message[BUFF_SIZE];
int main()
{
WSADATA wsaData;
SOCKET hSocket;
SOCKADDR_IN servAddr;
char message[BUFF_SIZE];
int strlenth;
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
{
errorHanding("WSAStartup() error!");
}
hSocket = socket(PF_INET, SOCK_STREAM, 0);
if (hSocket == INVALID_SOCKET)
{
errorHanding("socket() error!");
}
memset(&servAddr, 0, sizeof(servAddr));
servAddr.sin_family = AF_INET;
servAddr.sin_addr.s_addr = inet_addr("127.0.0.1");//本机ip地址
servAddr.sin_port = htons(atoi("9190"));
if (connect(hSocket, (SOCKADDR*)&servAddr, sizeof(servAddr)) == SOCKET_ERROR)
{
errorHanding("connect() error!");
}
else {
printf("Connected....");
}
strlenth = recv(hSocket,message,BUFF_SIZE,0);
if (strlenth == -1)
{
errorHanding("recv() error!");
}
printf("Message from server : %s", message);
closesocket(hSocket);
WSACleanup();
return 0;
}
void errorHanding(const char*m)
{
fputs(m, stderr);
fputc('\n', stderr);
exit(1);
}
运行截图:
感谢观看