基于TCP(面向连接)的socket编程,分为客户端和服务器端。
服务器端的流程如下:
(1)创建套接字(socket)
---- Create a socket.
(2)将套接字绑定到一个本地地址和端口上(bind)
---- Remove any existing file with the same pathname as that to which we want to bind the socket.
(3)将套接字设为监听模式,准备接收客户端请求(listen)即等待客户端调用connect().
---- Construct an address structure for the server's socket, bind the socket to that address, and mark the socket as a listening socket.
(4)等待客户请求到来;当请求到来后,接受连接请求,返回一个新的对应于此次连接的套接字(accept)
---- Execute an infinite loop to handle incoming client requests. Each loop iteration (迭代,反复,重复) performs the following steps:
-- Accept a connection, obtaining a new socket, for the connection.
-- Read all of the data from the connected socket and write it to standard output.
(5)用返回的套接字和客户端进行通信(send/recv)
(6)返回,等待另一个客户请求。
(7)关闭套接字。the server must be terminated manually.
客户端的流程如下:
(1)创建套接字(socket)
(2)向服务器发出连接请求(connect)
(3)和服务器端进行通信(send/recv)
(4)关闭套接字
设置服务器端口时,会用到htons()函数,该函数把一个u_short类型的值从主机字节顺序转换为TCP/IP网络字节顺序,因为不同的计算机存放多字节的顺序不同,所以网络中不同主机间通信时,要统一采用网络字节顺序。
设置服务器IP地址时,会使用到inet_addr()函数,它是将点分十进制的IP地址(“127.0.0.1”)的字符串转换成unsigned long型,inet_ntoa()函数做相反的转换。
根据连接启动的方式以及本地套接字要连接的目标,套接字之间的连接过程可以分为以下三个步骤:
>> 服务器监听:服务器套接字并不定位具体的客户端套接字,而是出于等待连接的状态,实时监控网络状态。listen()
>> 客户端请求:是指由客户端的套接字提出连接请求,要连接的目标是服务器端的套接字。为此,客户端的套接字必须首先描述它要连接的服务器的套接字,指出服务器端套接字的地址和端口号,然后就向服务器端套接字提出连接请求。connect()
>> 连接确认:是指当服务器端套接字监听到或者说接收到客户端套接字的连接请求accept(), 它就响应客户端套接字的请求,建立一个新的线程,把服务器端新的套接字的描述发给客户端,一旦客户端确认了此描述,连接就建立好了。而服务器端的listensocket继续处于监听状态,继续接收其他客户端套接字的连接请求。
服务器端的代码如下,启动服务器监听,连接putty:
#include
#include
#include
#include
using namespace std;
#pragma comment(lib,"ws2_32.lib")
int main()
{
WORD versionRequsted;
WSADATA wsaData; //winodws sockets asynchronous
int ret;
SOCKET m_Server,m_AcceptClient;
versionRequsted = MAKEWORD(2,2); //hope to use this winsock dll version
if(WSAStartup(versionRequsted,&wsaData)!=0){
printf("WSAStartup() failed!\n");
return 0;
}
if(LOBYTE(wsaData.wVersion)!=2 || HIBYTE(wsaData.wVersion)!=2)
{
WSACleanup(); //free the resources
printf("Invalid winsock version!\n");
}
m_Server = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
if(m_Server == -1){ //INVALID_SOCKET
WSACleanup();
printf("Create socket() failed!\n");
}
struct sockaddr_in aServer,aclient;
unsigned short tcp_port = 12988;
aServer.sin_family = AF_INET;
aServer.sin_port = htons(tcp_port); //convert to network byte sequence (short)
aServer.sin_addr.s_addr = htonl(INADDR_ANY);
ret = bind(m_Server,(struct sockaddr *)&aServer,sizeof(sockaddr));
if(ret == SOCKET_ERROR){
printf("bind() failed!\n");
closesocket(m_Server);
WSACleanup();
exit(1);
}
//set listen mode
ret = listen(m_Server,1);
if(ret < 0)
{
int errcode;
printf("listen() error\n");
errcode = WSAGetLastError();
printf("errcode = %d",errcode);
closesocket(m_Server);
WSACleanup();
exit(1);
}
int i_mode = 1;
ioctlsocket(m_Server,FIONBIO,(u_long *)&i_mode);
char cmd[200] = "start D:/2017/2017_new/platform/whale2/package/other/extapp/putty.exe -sessionlog test -raw 127.0.0.1 12988 ";
system(cmd);
int addrlen = sizeof(sockaddr);
m_AcceptClient = accept(m_Server,(sockaddr *)&aclient,&addrlen);
while(m_AcceptClient > 0){
char sendMsg[50];
memset(sendMsg,0,sizeof(sendMsg));
printf("please input send message:\n");
gets(sendMsg);
if(strcmp(sendMsg,"quit")!=0) {
ret = send(m_AcceptClient,(char *)sendMsg,sizeof(sendMsg),0);
if(ret == SOCKET_ERROR){
printf("send() failed!\n");
break;
}else{
printf("client info has been sent!\n");
}
}
else {
break;
}
}
char recvBuf[80];
memset(recvBuf,0,sizeof(recvBuf));
recv(m_AcceptClient,recvBuf,80,0);
printf("%s\n",recvBuf);
closesocket(m_AcceptClient);
closesocket(m_Server);
WSACleanup();
system("pause");
return 0;
}
运行后截图如下:
int errcode;
errcode = WSAGetLastError();
if(WSAEWOULDBLOCK == errcode || WSAEINVAL == errcode){
//have no client connection in block mode or not activate listen() before accept()
printf("not in nonblock mode and no connection or have no listen().\n");
} else if(WSAEISCONN == errcode){
printf("this socket have already connected.\n");
}
上面的代码没有加入循环,而且要等发完之后才能收,优化后的代码见下一节。
客户端的代码如下: