网络编程基础知识学习要点记录

来源:互联网 发布:java客户端 api接口 编辑:程序博客网 时间:2024/04/29 06:43
1、网络中通信的两个进程可以用以下五元组标识:
  (协议(TCP/UDP),本地IP地址,本地端口号,远程IP地址,远程端口号)
2、在Windows下的网络应用程序编程接口叫Windows Sockets,简称WinSock。
3、套接口(Socket):Windows Sockets API依靠套接口进行通信。
4、WinSock API基本函数:
(1)套接口与连接的建立:            
a)  打开WinSock——WSAStartup():应用程序或dll只能在一次成功的WSAStartup()调用之后才能进一步调用其他的Windows Sockets API函数。
b)  创建套接口——socket()或WSASocket():应用程序在使用套接口通信之前,必须要拥有一个套接口,所以要创建一个套接口。
c)  指定本地地址——bind():当用socket()创建一个套接口后,该套接口还是不能直接使用,因为它只是存在于一个名字空间(地址族)中,也就是说它只确定了通信所希望使用的服务类型,并没有与该主机上提供服务的某端口联系在一起,这样的套接口可以叫未命名的套接口。bind()函数通过给一个未命名的套接口分配一个本地名字来为套接口建立本地捆绑,即把一个套接口与一个主机地址和端口号联系起来。
d)  监听连接——listen():在一个服务器端用socketd调用成功创建了一个套接口,并用bind()函数和一个指定的地址关联(即绑定)在一起后,要指示该套接口进入监听连接请求的状态以及接受由客户发来的连接请求,就要用到listen()函数。
e)  请求连接——connect()或WSAConnect():当服务器端建立好套接口并与一个本地地址绑定后,就进入监听状态,等待发出连接请求,从而为客户提供服务。在客户端套接口建立好之后,就要调用connect()函数,提出与一个服务器连接的请求,如果服务器接受请求,就可以在服务器的远程套接口与客户端的本地套接口之间建立一条连接。
f)  接受连接——accept()或WSAAccept():在服务器端通过listen()函数调用表示服务器进入监听客户的连接请求状态,而在服务器端调用accept()函数表示可以接受来自客户端由connect()发来的连接请求。该函数用于面向连接的服务器端,在IP协议族中,只用于TCP服务器端
(2)数据传输:
a)  在已建立连接的套接口上发送数据——send()或WSASend():适用于已建立连接的数据报或流式套接口发送数据。
b)  在已建立连接的套接口上接受数据——recv()或WSARecv():用于在已建立连接的数据报或流式套接口上进行数据的接受。
c)  在无连接的套接口上接受数据——recvfrom()或WSARecvFrom():其使用类似于recv()和WSARecv()函数。该函数也可以用于有连接时数据的接受。
d)  在无连接的套接口上发送数据——sendto()或WSASendTo():其使用类似于send()或WSASend()函数。当用于无连接的套接口时,调用函数之前要设置,指出目标IP地址和目标端口号。如果用于有连接的套接口时,则不能指定目标地址和端口,应将to设置为空,地址长度设置为0。当然在有连接的情况下很少使用该函数。
(3)连接与套接口的关闭:

a)  关闭读写通道——shutdown():在一个套接口上的读写操作完成后,应该首先使用shutdown()函数来关闭套接口的读通道、写通道或读写通道。这样做的好处是当通信的双方不再有数据要发送或接受时,它可以通知对方,以防止数据丢失。注意,该函数并不关闭套接口,且套接口所占有的资源被一只保持到closesocket()调用之前。

b)  关闭套接口——closesocket():一个套接口不再使用时一定要关闭这个套接口,以释放与该套接口关联的所有资源,包括等候处理的数据。对于有连接的TCP来说,closesocket()调用还要关闭TCP连接。

c)  终止使用Winsock——WSACleanup():当在应用程序中不再使用Winsock API中的任何函数时,必须调用WSACleanup()将其从Windows Sockets的实现中注销,以释放为应用程序或dll分配的任何资源。
5、面向连接的客户/服务器程序工作流程(TCP协议):
(1)  服务器端程序的工作流程:工作时,在服务器端的应用程序应该首先启动,以等待客户的服务请求。
a)  使用WSAStartup()函数检查系统协议栈的安装情况,只有在协议栈正确安装的情况下,才可以指向以下的各步骤。
b)  使用socket()或WSASocket()函数创建服务器端的套接口。如果套接口创建不成功,也不能执行以下的各部操作,并且要调用WSACleanup()函数,结束Windows Sockets API的使用。这一步调用过程确定了相关五元组的协议。
c)  使用bind()函数将创建的套接口与服务器地址绑定。它确定了相关五元组中的本地IP地址和端口号。
d)  使用listen()函数使服务器套接口做好接受连接请求的准备。
e)  使用accept()或WSAAccept()函数接受来自客户端由connnect()发出的连接请求。在该函数的参数中,由clientAddr参数返回客户端协议、IP地址和端口信息,至此,一个通信所需的五元组已建立。
f)  根据连接请求建立连接后,使用send()或WSASend()函数发送数据,或使用recv()或WSARecv()函数接受数据。
g)  使用closesocket()函数关闭套接口。
h)  最后调用WSACleanup()函数,结束Windows Sockets API的使用。
(2)客户端程序工作流程:
a)  使用WSAStartup()函数检查系统协议栈的安装情况。
b)  使用socket()或WSASocket()函数创建客户端套接口。
c)  使用connect()或WSAConnect()函数发出与服务器建立连接的请求。其中serverAddr是指向远程服务器地址的指针,因此调用此函数之前一定要知道远程服务器的地址和端口号,并把其值赋给serverAddr结构量。
d)  建立连接后,使用send()或WSASend()函数发送数据,或者使用recv()或WSARecv()函数接受数据。
e)  使用closesocket()函数关闭套接口。
f)  最后调用WSACleanup()函数,结束Windows Sockets API的使用。
6、无连接的客户/服务器程序工作流程(UDP协议):与面向连接的通信过程不同,它最大特点是不需要在客户和服务器之间先建立连接,通信的任何一方可以先发送数据,这样首先发送数据的一方就成了客户端,而接受数据的一方就是服务器。无连接的数据报传输服务通信时,客户端和服务器端所使用的函数时类似的。
a)  使用WSAStartup()函数检查系统协议栈的安装情况。
b)  使用socket()或WSASocket()函数创建套接字,以确定相关五元组的协议。
c)  使用bind()函数将创建的套接口与本地地址绑定。它确定了相关五元组中的本地IP地址和端口号。
d)  使用sendto()或WSASendTo()函数发送数据,使用recvfrom()或WSARecvFrom()函数接受数据。
e)  使用closesocket()函数关闭套接口。
f)  最后调用WSACleanup()函数,结束Windows Sockets API的使用。
     在编写无连接的客户/服务器程序时,应该注意以下几个问题:
    (1)通信的一方可以不用bind()绑定IP地址和端口,而由系统自动分配。
    (2)不绑定IP地址和端口的一方必须首先向绑定地址的一方发送数据。
    (3)无连接应用程序也可以调用connect()函数,但是它并不向对方发出建立连接的请求,内核只是将connect()中指定的目标方的IP地址和端口号记录下来,在以后的通信中就可以使用面向连接的数据发送函数send()和数据接收函数
    (4)无连接的数据报传输过程中,作为服务器的一方也必须先启动,否则客户请求传不到服务进程。
    (5)由于无连接的客户端一般不调用connect(),因此在数据发送之前,客户与服务器之间尚未建立一个全相关(五元组),但各自通过socket()和bind()调用建立了半相关。发送数据时,发送发除指定本地套接口的地址外,还需要指定接受方案套接口的地址,从而在数据收发过程中动态地建立全相关。



源码:客户端和服务器端的程序(基于TCP协议)

客户端源码:VS2008


/*基于TCP的Socket编程,客户端程序:0、打开Winsock1、创建套接字(socket)2、向服务器发出连接请求(connect)3、和服务器进行通信(send/recv)4、关闭套接字5、终止Winsock*/#include <WinSock2.h>#include <stdio.h>#include <stdlib.h>#include <string.h>#pragma comment(lib,"ws2_32.lib")  //added for fixing error LNK2019: 无法解析的外部符号#define SPORT 5050 //服务端口int main() {WORD wVersionRequested;WSADATA wsaData;SOCKET sClient = INVALID_SOCKET;struct sockaddr_in serAddr;char *sendMsg = (char *)malloc(100*sizeof(char));char *recvMsg = (char *)malloc(100 * sizeof(char));memset(sendMsg, 0, sizeof(char)*100);memset(recvMsg, 0, sizeof(char)*100);//使用的Winsock版本wVersionRequested = MAKEWORD(2,2);//打开Winsockif(WSAStartup(wVersionRequested, &wsaData) != 0) {printf("WSAStartup failed!\n");return 0;}if(wsaData.wVersion != wVersionRequested) {printf("Invalid Winsock version!\n");WSACleanup();return 0;}//创建客户端的套接口,使用TCP协议sClient = socket(AF_INET, SOCK_STREAM, 0);if(sClient == INVALID_SOCKET) {printf("Create socket failed, errno = %d\n", WSAGetLastError());WSACleanup();return 0;}//创建服务器的IP地址和端口号serAddr.sin_family = AF_INET;serAddr.sin_port = htons(SPORT);serAddr.sin_addr.s_addr = inet_addr("10.18.36.32"); //请求服务器的连接if(connect(sClient, (sockaddr*)&serAddr, sizeof(serAddr)) == SOCKET_ERROR) {printf("Connect Service failed, errno = %d\n", WSAGetLastError());closesocket(sClient); //关闭套接字WSACleanup();return 0;}scanf_s("%s", sendMsg,100);if(send(sClient, sendMsg, 100, 0) == SOCKET_ERROR) {printf("Send Message failed, errno = %d\n", WSAGetLastError());} else {printf("Client message send to Service successfully!\n");}if(recv(sClient, recvMsg, 100, 0) == SOCKET_ERROR) {printf("Recv Message failed, errno = %d\n", WSAGetLastError());} else {printf("Client receive message from Service successfully: \n");printf("%s\n", recvMsg);}free(sendMsg);sendMsg = 0;free(recvMsg);recvMsg = 0;closesocket(sClient);WSACleanup();return 0;}

服务器端源码:VS2008


/*基于TCP的Socket编程,服务器端程序:0、打开Winsock1、创建套接字(socket)2、将套接字绑定到本地地址和端口上(bind)3、将套接字设为监听模式,准备接受客户端请求(listen)4、等待客户请求到来,当请求到来时,接受请求,返回一个新的对应于此次连接的套接字(accept)5、用返回的套接字和客户端进行通信(send/recv)6、返回,等待另一个客户的请求7、关闭套接字8、终止Winsock*/#include <WinSock2.h>#include <stdio.h>#include <stdlib.h>#include <string.h>#pragma comment(lib,"ws2_32.lib")  //added for fixing error LNK2019: 无法解析的外部符号#define SPORT 5050 //服务端口int main() {WORD wVersionRequested;WSADATA wsaData;SOCKET sListen = INVALID_SOCKET, sAccept = INVALID_SOCKET;struct sockaddr_in serAddr, cliAddr;int len, left, recvSize;char *ptr;//使用的Winsock版本wVersionRequested = MAKEWORD(2,2);//打开Winsockif(WSAStartup(wVersionRequested, &wsaData) != 0) {printf("WSAStartup() failed!\n");return 0;}if(wVersionRequested != wsaData.wVersion) {printf("Invalid Winsock version!\n");WSACleanup();return 0;}//创建服务器端的套接口,使用TCP协议sListen = socket(AF_INET, SOCK_STREAM, 0);if(sListen == INVALID_SOCKET) {printf("Create socket failed, errno = %d\n", WSAGetLastError());WSACleanup();return 0;}//构建本地IP地址和端口号serAddr.sin_family = AF_INET;serAddr.sin_port = htons(SPORT);serAddr.sin_addr.s_addr = htonl(INADDR_ANY);//由系统内核指定任意IP地址//将套接字sListen绑定到本地IP和端口号if(bind(sListen, (sockaddr *)&serAddr, sizeof(serAddr)) == SOCKET_ERROR) {printf("Bind socket failed, errno = %d\n", WSAGetLastError());closesocket(sListen);WSACleanup();return 0;}//监听客户端的连接请求if(listen(sListen, 5) == SOCKET_ERROR) {printf("listen() failed, errno = %d\n", WSAGetLastError());closesocket(sListen);WSACleanup();return 0;}printf("Waiting for the connection from Client!\n");printf("Ctrl + C to quit!\n");//等待客户请求while(1) {len = sizeof(cliAddr);//接受请求,返回一个新的对应于此次连接的套接字sAccept = accept(sListen, (sockaddr*)&cliAddr, &len);if(sAccept == INVALID_SOCKET) {printf("accept() failed, errno = %d\n", WSAGetLastError());closesocket(sListen);WSACleanup();return 0;}char sendMsg[] = "Hello, I am waiting for you!";//发送数据if(send(sAccept, sendMsg, strlen(sendMsg)+1, 0) == SOCKET_ERROR) {printf("send() failed, errno = %d\n", WSAGetLastError());}//接受数据char recvMsg[20] = {0};/*left = sizeof(recvMsg);ptr = recvMsg;while(left > 0) {recvSize = recv(sAccept, ptr, left, 0);if(recvSize == SOCKET_ERROR) {printf("recv() failed, errno = %d\n", WSAGetLastError());}left -= recvSize;ptr += recvSize;}*/recvSize = recv(sAccept, recvMsg, 20, 0);if(recvSize == SOCKET_ERROR) {printf("recv() failed, errno = %d\n", WSAGetLastError());}recvMsg[19]= '\0';printf("Receive Message: %s\n", recvMsg);}closesocket(sListen);closesocket(sAccept);WSACleanup();return 0;}



源码:客户端和服务器端的程序(基于UDP协议)

客户端源码:VS2008


/*基于UDP的Socket编程,客户端程序:0、打开Winsock1、创建套接字(socket)2、和服务器进行通信(send/recv)3、关闭套接字4、终止Winsock*/#include <Winsock2.h>  #include <stdio.h>  #pragma comment(lib, "WS2_32.lib");  #define SERVERPORT 5050  //服务器端口号#define BUFFER 1024  //缓冲区大小int Init() {  WORD wVersionRequested;   WSADATA wsaData;  int ret = 0;  wVersionRequested = MAKEWORD(2,2);  if(WSAStartup(wVersionRequested, &wsaData) != 0) {  ret = -1;  } else if(wVersionRequested != wsaData.wVersion){  ret = -1;  WSACleanup();  }  return ret;  }  int main() {  int flag = 0;  SOCKET clientSocket = INVALID_SOCKET;  struct sockaddr_in serAddr;  //服务器地址int iPort = SERVERPORT;  char sendMsg[BUFFER] = "Hello, I am a Client!\n";  char recvMsg[BUFFER] = {0};  do {  if(Init() != 0) {  printf("Init() failed!\n");  return -1;  }  //创建客户端的套接字clientSocket = socket(AF_INET, SOCK_DGRAM, 0);  if(clientSocket == INVALID_SOCKET) {  printf("socket() failed, errno = %d\n", WSAGetLastError());  WSACleanup();  return -1;  }  //创建客户端的地址serAddr.sin_family = AF_INET;  serAddr.sin_port = htons(iPort);  serAddr.sin_addr.s_addr = inet_addr("10.18.36.32"); //需要修改地址 int len = sizeof(serAddr);  //向服务器发送数据if(sendto(clientSocket, sendMsg, strlen(sendMsg)+1, 0, (sockaddr *)&serAddr, len) == SOCKET_ERROR) {  printf("sendto() failed, errno = %d\n", WSAGetLastError());  flag = -1;  break;  } else {printf("Send Message to Server successfully!\n");}//从服务器端接收数据if(recvfrom(clientSocket, recvMsg, BUFFER, 0, (sockaddr *)&serAddr, &len) == SOCKET_ERROR) {  printf("recvfrom() failed, errno = %d\n", WSAGetLastError());  flag = -1;  break;  } else {  recvMsg[BUFFER-1] = '\0';  printf("Receive Message from Server : %s\n", recvMsg);  }  } while(0);  if(flag == -1) {  closesocket(clientSocket);  WSACleanup();  }  closesocket(clientSocket);  WSACleanup();  return 0;  }

服务器端代码,VS2008


/*基于UDP的Socket编程,服务器端程序:0、打开Winsock1、创建套接字(socket)2、将套接字绑定到本地地址和端口上(bind)3、和客户端进行通信(send/recv)4、关闭套接字5、终止Winsock*/#include <Winsock2.h>  #include <stdio.h>  #pragma comment(lib, "WS2_32.lib");  #define SERVERPORT 5050  //服务器端口号#define BUFFER 1024  //缓冲区的长度int Init() {  WORD wVersionRequested;   WSADATA wsaData;  int ret = 0;  wVersionRequested = MAKEWORD(2,2);  if(WSAStartup(wVersionRequested, &wsaData) != 0) {  ret = -1;  } else if(wVersionRequested != wsaData.wVersion){  ret = -1;  WSACleanup();  }  return ret;  }  int main() {  SOCKET serverSocket = INVALID_SOCKET;  struct sockaddr_in serAddr, cliAddr;  //本地地址和客户端地址int iPort = SERVERPORT;  char sendMsg[BUFFER] = "Hello, I am a Server!\n";  char recvMsg[BUFFER] = {0};  if(Init() != 0) {  printf("Init() failed!\n");  return -1;  }  //创建服务器端的套接口serverSocket = socket(AF_INET, SOCK_DGRAM, 0);  if(serverSocket == INVALID_SOCKET) {  printf("socket() failed, errno = %d\n", WSAGetLastError());  WSACleanup();  return -1;  }  //创建服务器端地址serAddr.sin_family = AF_INET;  serAddr.sin_port = htons(iPort);  serAddr.sin_addr.s_addr = htonl(INADDR_ANY);  //将服务器端的套接口和本地IP地址和端口号绑定if(bind(serverSocket, (sockaddr *)&serAddr, sizeof(serAddr)) == SOCKET_ERROR) {  printf("bind() failed, errno = %d\n", WSAGetLastError());  closesocket(serverSocket);  WSACleanup();  return -1;  }  int len = sizeof(cliAddr); //进入无限循环,进行数据接收和发送while(1) {  if(recvfrom(serverSocket, recvMsg, BUFFER, 0, (sockaddr *)&cliAddr, &len) == SOCKET_ERROR) {  printf("recvfrom() failed, errno = %d\n", WSAGetLastError());  closesocket(serverSocket);  WSACleanup();  return -1;  } else {  recvMsg[BUFFER-1] = '\0';  printf("Receive Message from client : %s\n", recvMsg);  }  if(sendto(serverSocket, sendMsg, strlen(sendMsg)+1, 0, (sockaddr *)&cliAddr, sizeof(cliAddr)) == SOCKET_ERROR) {  printf("sendto() failed, errno = %d\n", WSAGetLastError());  closesocket(serverSocket);  WSACleanup();  return -1;  } else {printf("Send Message to client successfully!\n");}} closesocket(serverSocket);  WSACleanup();  return 0;  }  


                                             
0 0
原创粉丝点击