socket简单编程(C++语言实现)
客户端实现
并发面向连接服务器基本流程
主线程1: 创建(主)套接字,并绑定熟知端口号;
主线程2: 设置(主)套接字为被动监听模式,准备用于服务器;
主线程3: 反复调用accept()函数接收下一个连接请求(通过主套接字),并创建一个新的子线程处理该客户响应;
子线程1:接收一个客户的服务请求(通过新创建的套接字);
子线程2: 遵循应用层协议与特定客户进行交互;
子线程3:关闭/释放连接并退出(线程终止).
并发无连接服务器基本流程
主线程1: 创建套接字,并绑定熟知端口号;
主线程2: 反复调用recvfrom()函数,接收下一个客户请求,并创建新线程处理该客户响应;
子线程1: 接收一个特定请求;
子线程2: 依据应用层协议构造响应报文,并调用sendto()发送;
子线程3: 退出(一个子线程处理一个请求后即终止)。
consock.cpp
/* consock.cpp - connectsock */
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <winsock.h>
#ifndef INADDR_NONE
//是32位均为1的值(即255.255.255.255,它是Internet的有限广播地址)
#define INADDR_NONE 0xffffffff
#endif /* INADDR_NONE */
void errexit(const char *, ...);
/*-------------------------------------------------------
* connectsock - allocate & connect a socket using TCP or UDP
*------------------------------------------------------
*/
SOCKET connectsock(const char *host, const char *service, const char *transport )
{
//gethostbyname将域名或者IP地址转换为32为IP地址返回指向hostent的指针
struct hostent *phe; /* pointer to host information entry */
//getservbyname将服务名转化为服务器端口比如HTTP转化为80
struct servent *pse; /* pointer to service information entry */
//getprotobyname将协议名转换为协议号
struct protoent *ppe; /* pointer to protocol information entry */
//bind将端口号与本地端口地址绑定
struct sockaddr_in sin;/* an Internet endpoint address */
int s, type; /* socket descriptor and socket type */
memset(&sin, 0, sizeof(sin));//将sin全部置零
sin.sin_family = AF_INET;
//服务-->服务器端口号 允许服务为直接指定的端口号
//1.service是字符时
if ( pse = getservbyname(service, transport) )//getservbyname("ftp", "tcp");
sin.sin_port = pse->s_port;
//2.service是数字串时
//atoi将字符串转化为整数 htons是将整型变量从主机字节顺序转变成网络字节顺序
else if ( (sin.sin_port = htons((u_short)atoi(service))) == 0 )
errexit("can't get \"%s\" service entry\n", service);
// host name ---> IP address 允许点分十进制
//1.当host为主机名时
if ( phe = gethostbyname(host) )
memcpy(&sin.sin_addr, phe->h_addr, phe->h_length);
//2.当host为点分十进制,功能是将一个点分十进制的IP转换成一个长整数型数(u_long类型)
else if ( (sin.sin_addr.s_addr = inet_addr(host))==INADDR_NONE)
errexit("can't get \"%s\" host entry\n", host);
//协议--->协议号
if ( (ppe = getprotobyname(transport)) == 0)
errexit("can't get \"%s\" protocol entry\n", transport);
/* Use protocol to choose a socket type */
//确定type
if (strcmp(transport, "udp") == 0)
type = SOCK_DGRAM;
else
type = SOCK_STREAM;
/*创建套接字*/
s = socket(PF_INET, type, ppe->p_proto);
if (s == INVALID_SOCKET)
errexit("can't create socket: %d\n", GetLastError());
/* Connect the socket */
if (connect(s, (struct sockaddr *)&sin, sizeof(sin))==SOCKET_ERROR)
errexit("can't connect to %s.%s: %d\n", host, service,GetLastError());
return s;
}
conTCP.cpp
/* conTCP.cpp - connectTCP */
#include <winsock.h>
SOCKET connectsock(const char *, const char *, const char *);
/*----------------------------------------------------
* connectTCP - connect to a specified TCP service
* on a specified host
*---------------------------------------------------
*/
SOCKET connectTCP(const char *host, const char *service )
{
return connectsock( host, service, "tcp");
}
errexit.cpp
/* errexit.cpp - errexit */
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <winsock.h>
/*----------------------------------------------------------
* errexit - print an error message and exit
*----------------------------------------------------------
*/
/*VARARGS1*/
void errexit(const char *format, ...)
{ va_list args;
va_start(args, format);
vfprintf(stderr, format, args);
va_end(args);
WSACleanup();
exit(1);}
TCPdtc.cpp
/* TCPdtc.cpp - main, TCPdaytime */
#include <stdlib.h>
#include <stdio.h>
#include <winsock.h>
void TCPdaytime(const char *, const char *);
void errexit(const char *, ...);
SOCKET connectTCP(const char *, const char *);
#define LINELEN 128
#define WSVERS MAKEWORD(2, 0)
/*--------------------------------------------------------
* main - TCP client for DAYTIME service
*--------------------------------------------------------
*/
int main(int argc, char *argv[])
{
char *host = "localhost"; /* host to use if none supplied */
char *service = "daytime"; /* default service port */
WSADATA wsadata;
switch (argc) {
case 1:
host = "localhost";
break;
case 3:
service = argv[2];
/* FALL THROUGH */
case 2:
host = argv[1];
break;
default:
fprintf(stderr, "usage: TCPdaytime [host [port]]\n");
exit(1);
}
if (WSAStartup(WSVERS, &wsadata) != 0)
errexit("WSAStartup failed\n");
TCPdaytime(host, service);
WSACleanup();
return 0; /* exit */
}
/*-----------------------------------------------------
* TCPdaytime - invoke Daytime on specified host and print results
*-----------------------------------------------------
*/
void TCPdaytime(const char *host, const char *service)
{
char buf[LINELEN+1]; /* buffer for one line of text */
SOCKET s; /* socket descriptor */
int cc; /* recv character count */
s = connectTCP(host, service);
cc = recv(s, buf, LINELEN, 0);
while( cc != SOCKET_ERROR && cc > 0)
{
buf[cc] = '\0'; /* ensure null-termination */
(void) fputs(buf, stdout);
cc = recv(s, buf, LINELEN, 0);
}
getchar();
closesocket(s);
}
服务器实现
errexit
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <winsock.h>
/*----------------------------------------------------------
* errexit - print an error message and exit
*----------------------------------------------------------
*/
/*VARARGS1*/
void errexit(const char *format, ...)
{ va_list args;
va_start(args, format);
vfprintf(stderr, format, args);
//stream
//指向标识输出流的FILE对象的指针。
//format
//包含格式字符串的C字符串,其格式与printf中的格式相同
//arg
//标识使用va_start初始化的变量参数列表的值。
//va_list是在<cstdarg>中定义的特殊类型。
va_end(args);
WSACleanup();
exit(1);}
passsock.cpp
/* passsock.cpp - passivesock */
#include <stdlib.h>
#include <string.h>
#include <winsock.h>
void errexit(const char *, ...);
/*-----------------------------------------------------------------------
* passivesock - allocate & bind a server socket using TCP or UDP
*------------------------------------------------------------------------
*/
SOCKET passivesock(const char *service, const char *transport, int qlen)
{
struct servent *pse; /* pointer to service information entry */
struct protoent *ppe; /* pointer to protocol information entry */
struct sockaddr_in sin;/* an Internet endpoint address */
SOCKET s; /* socket descriptor */
int type; /* socket type (SOCK_STREAM, SOCK_DGRAM)*/
memset(&sin, 0, sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = INADDR_ANY;
/* Map service name to port number */
if ( pse = getservbyname(service, transport) )
sin.sin_port = (u_short)pse->s_port;
else if ( (sin.sin_port = htons((u_short)atoi(service))) == 0 )
errexit("can't get \"%s\" service entry\n", service);
/* Map protocol name to protocol number */
if ( (ppe = getprotobyname(transport)) == 0)
errexit("can't get \"%s\" protocol entry\n", transport);
/* Use protocol to choose a socket type */
if (strcmp(transport, "udp") == 0)
type = SOCK_DGRAM;
else
type = SOCK_STREAM;
/* Allocate a socket */
s = socket(PF_INET, type, ppe->p_proto);
if (s == INVALID_SOCKET)
errexit("can't create socket: %d\n", GetLastError());
/* Bind the socket */
if (bind(s, (struct sockaddr *)&sin, sizeof(sin)) == SOCKET_ERROR)
errexit("can't bind to %s port: %d\n", service,GetLastError());
//int listen(sd,queuesize);0:成功 SOCKET_ERROR:失败
if (type == SOCK_STREAM && listen(s, qlen) == SOCKET_ERROR)
errexit("can't listen on %s port: %d\n", service,GetLastError());
return s;}
passTCP.cpp
/* passTCP.cpp - passiveTCP */
#include <winsock.h>
SOCKET passivesock(const char *, const char *, int);
/*------------------------------------------------------------------------------------
* passiveTCP - create a passive socket for use in a TCP server
*------------------------------------------------------------------------------------
*/
SOCKET passiveTCP(const char *service, int qlen)
{
return passivesock(service, "tcp", qlen);
}
TCPdtd.cpp
/* TCPdtd.cpp - main, TCPdaytimed */
#include <stdlib.h>
#include <winsock.h>
#include <process.h>
#include <time.h>
#include <stdio.h>
void errexit(const char *, ...);
void TCPdaytimed(SOCKET);
SOCKET passiveTCP(const char *, int);
#define QLEN 5
#define WSVERS MAKEWORD(2, 0)
/*------------------------------------------------------------------------
* main - Concurrent TCP server for DAYTIME service
*------------------------------------------------------------------------
*/
int main(int argc, char *argv[]){
struct sockaddr_in fsin; /* the from address of a client */
char *service = "daytime"; /* service name or port number*/
SOCKET msock, ssock; /* master & slave sockets */
int alen; /* from-address length */
WSADATA wsadata;
switch (argc) {
case 1:
break;
case 2:
service = argv[1];
break;
default:
errexit("usage: TCPdaytimed [port]\n");
}
if (WSAStartup(WSVERS, &wsadata) != 0)
errexit("WSAStartup failed\n");
msock = passiveTCP(service, QLEN);
while(1){
alen = sizeof(struct sockaddr);
//accept: 接受/提取一个连接请求,创建新套接字
//通过新套接 (仅用于服务器端的TCP套接字)
ssock = accept(msock, (struct sockaddr *)&fsin, &alen);
if (ssock == INVALID_SOCKET)
errexit("accept failed: error number %d\n",GetLastError());
//创建新线程
if (_beginthread((void (*)(void *)) TCPdaytimed, 0,(void *)ssock) < 0) {
errexit("_beginthread: %s\n", strerror(errno));
}
else{
printf("一次");
}
}
return 1; /* not reached */
}
/*----------------------------------------------------------------------
* TCPdaytimed - do TCP DAYTIME protocol
*-----------------------------------------------------------------------
*/
void TCPdaytimed(SOCKET fd)
{
char * pts; /* pointer to time string */
time_t now; /* current time */
(void) time(&now);
pts = ctime(&now);//返回一个表示当地时间的字符串
(void) send(fd, pts, strlen(pts), 0);
//输出fd套接字号
char str[255];
sprintf(str, "%d", fd);
(void) send(fd, str, strlen(str), 0);
(void) closesocket(fd);
}